'object', 'additionalProperties' => true, ); } /** * {@inheritDoc} * * @param Extra $extra Extra. * @param Selection|null $selection Selection. * @param Context $context Context. */ public function validate( Extra $extra, ?Selection $selection, Context $context ): true|\WP_Error { if ( $extra->required && ( null === $selection || $this->is_empty_value( $selection->value ) ) ) { return new \WP_Error( 'extrova_required', $this->required_message( $extra ), array( 'extra_id' => $extra->id ) ); } return true; } /** * {@inheritDoc} * * @param Extra $extra Extra. * @param Selection $selection Selection. */ public function is_chargeable( Extra $extra, Selection $selection ): bool { return ! $this->is_empty_value( $selection->value ); } /** * {@inheritDoc} * * @param Extra $extra Extra. * @param Selection $selection Selection. */ public function pricing_for( Extra $extra, Selection $selection ): array { return $extra->pricing; } /** * {@inheritDoc} * * @param Extra $extra Extra. * @param Context $context Context. */ public function to_client( Extra $extra, Context $context ): array { return array(); } /** * Hypothetical selections used to show prices before the customer chooses. * Returns `preview key => value`; key '' is the extra's base price. * * @param Extra $extra Extra. * @return array */ public function preview_selections( Extra $extra ): array { return array(); } /** * Fee name shown in totals, order and emails. * * @param Extra $extra Extra. * @param Selection $selection Selection. */ public function fee_name( Extra $extra, Selection $selection ): string { return $extra->label; } /** * Required-field message (same wording as WooCommerce). * * @param Extra $extra Extra. */ protected function required_message( Extra $extra ): string { return sprintf( /* translators: %s: extra label. */ __( '%s is a required field.', 'extrova' ), $extra->label ); } /** * Whether a value counts as empty. * * @param mixed $value Value. */ protected function is_empty_value( mixed $value ): bool { return null === $value || false === $value || '' === $value || array() === $value; } /** * `name` attribute of the classic field. * * @param Extra $extra Extra. * @param string $sub Sub key. */ protected function field_name( Extra $extra, string $sub = '' ): string { return 'extrova[' . $extra->id . ']' . ( '' !== $sub ? '[' . $sub . ']' : '' ); } /** * `id` attribute of the classic field. * * @param Extra $extra Extra. * @param string $suffix Suffix. */ protected function field_id( Extra $extra, string $suffix = '' ): string { return 'extrova-' . $extra->id . ( '' !== $suffix ? '-' . sanitize_html_class( $suffix ) : '' ); } /** * Classic price hint "(+3,00 €)" from previews computed by the adapter. * * @param Extra $extra Extra. * @param Context $context Context. * @param string $key Preview key. */ protected function price_hint_html( Extra $extra, Context $context, string $key = '' ): string { $previews = $context->get( 'price_previews', array() ); $result = is_array( $previews ) ? ( $previews[ $extra->id ][ $key ] ?? null ) : null; if ( ! $result instanceof PriceResult || (float) $result->display_amount <= 0 ) { return ''; } return ' ' . sprintf( /* translators: %s: formatted price. */ esc_html__( '(+%s)', 'extrova' ), wc_price( (float) $result->display_amount ) ) . ''; } /** * Required marker (visual asterisk + screen reader text). * * @param Extra $extra Extra. */ protected function required_marker_html( Extra $extra ): string { if ( ! $extra->required ) { return ''; } return ' ' . esc_html__( '(required)', 'extrova' ) . ''; } /** * Description paragraph. * * @param Extra $extra Extra. */ protected function description_html( Extra $extra ): string { if ( '' === $extra->description ) { return ''; } return '

' . esc_html( $extra->description ) . '

'; } /** * `aria-describedby` attribute (or ''). * * @param Extra $extra Extra. * @param string[] $more Extra ids. */ protected function describedby_attr( Extra $extra, array $more = array() ): string { $ids = $more; if ( '' !== $extra->description ) { array_unshift( $ids, $this->field_id( $extra, 'desc' ) ); } return array() === $ids ? '' : ' aria-describedby="' . esc_attr( implode( ' ', $ids ) ) . '"'; } /** * Wrapper CSS classes. * * @param Extra $extra Extra. */ protected function wrapper_class( Extra $extra ): string { return 'extrova-extra extrova-extra--' . sanitize_html_class( $extra->type ) . ( $extra->required ? ' extrova-extra--required' : '' ); } }