D$i3vkn{1(>G_3KK8m^RP1lb zA2!fBG{=_W#SIPLaN`MOJ>U1^WJ%;-lttPdpi;;58^N17kbqYx|@rTrr^}cqEdBO3KhTHegp46E2(-jdYDvNM?y FRS6^#UZs3m}+I50sGVmpwIyGmZ#_KlHDW z$3=tSq9lw3fJ>9t!H7Z*#F@X5^K(eHH0)_J7CzP22&Wr7M>iK@ics2n!@Utnyf;YH zdhv_rO?bhi;TWZ(+!iM2z9@F@$#wn=(Z_=?+Cqoi6Ygr!usduQ;4va%io+*G%)>M9 hrBSO%7sU-x@%NDDuF6~cp#b$ +%2q diff --git a/wp-includes/images/wpspin.gif b/wp-includes/images/wpspin.gif index fbf9be46c1094ba61cb73b88fe8fbfbca37dd30a..b9b7ae4875168c5582a2716ecdb71ed783fd3437 100644 GIT binary patch literal 2052 zcmbW1ZERE58OP7P_uPBWeeu25j=8=UZ0Fj9E=?`w1SUb-4~|WEiAivHSxu&OB#gpT z7>1xI5CXQbgAERl6i|Y!k{6tWfIC7+*(TD3L|&*iMd{Q9ZQ7!CbWtm{%esL=Kd~ED z)a>idk 1=luT9^FRL$o7S&cTO$DpJXr=Z>GaIZ%-q~utg$hh&0f5CaejV&c6K(C z$&8GQOixcwPEHOF4?lbMEEbF9a=EmABbA!WO+K5SAI@fHXJ*prw0>!1c(}2#aWa?7 zWYXD8rm?Or6^qsBXDT(E9nOu6%*;&B&CSlw&0S2VM}|i-*@d0WW~OJRGns5IHwpf) z<#@(bmTlkWD-D#DtaKBYUc~VYWN5GqxE2a7$n;QmM_WQYSol?& Lot zak(Y8swV(f%BRP-7AsKbEpFZAMi7pfD72*Yfiv_u2QkUWE)y7zBdrxykY~W`Ko&(_ zg$ZaoB9;4~N}`Z#%!d_TNOUxDX5qRIkl5WO`Xo+)dVqc`64r!Oz}`}TnyB9Hv#16G zr3%U=<5{1TAQ{rWb^ifPeOPxSUtHE8n13EREUTvm%Iq84Hv38!w2(9~eWwLbzwjgj z-QQ~2Zz@S_&^jGFswjhx12=n5pX!#X5EhTu8U)S-K3x9qplkJo$hgyp1fi^oV+d=# zRbWr>K;T7jL6l5Um|bOBRZE3MZO7F#!(8p^p)!J_U%TkD0)ycSka>O!tcUshT?W+M z8xeXNUA2U0T0a62k{`-QIAp38(8tpcNp(-9jr`NJmLII}>Kg3+isu^ic>v(T00sno z11jVR2igOL_H o @r!g;K#3=)H4ew1DZJePegGu`d1-r)Xe*y77gRA$1ZxN+#6YSdB zUMiu~Zv4?sXL@Rz_i@9z*c*M{dnZ}&>+`pB_~))w$(;9>Z?62i(;qotDKc}Ctmzb_ zSl1+Bm}tihON4r^1*$6y2D)5k=@tT@JgRR6j?*{>k?DgKZKNc7Qeacwcx(UDOY9 z_pX=TYAusC7QZJ))4VU+@y8F=r3-rg`d-TRed78@C8-G_u+dCSBuqp5T<5w^e^#?^ zb>~FxZ^nI_LGTd1+ij)AIR5E_JFi^kADb?mox&sZDJ)*J9E--@*kHDtN-cMM`~UJ5 zy-U3_`eE#!m@eMmwP2f$=$ 5(I*cC739`e{h!DrXNc zzc(&;Mq?NUl_V_DJd3TF!Ox_k7F84(EGb8#R mPD)R>DPWfN}t%?U}K`}$*>L#iAqa@ zs)&qCV3maA7rUH(8tY|Qj_WdR=K8L4Oqj5W{X?6d&q?Q4Y#DNoroFm(s<8HoH~i5( z=0 q^-jvMSEVy+wSa<2G<5xZ3U4q3J(4GtFfXVl_;OK`iy_2^S$`0I9`=FIf=!}7y`@4f7Q(EvTLzYDuoPcr7#To5P6 zOLfDLIqYS(B3#yUW^F^?jvE-JK%9=}J_XjL248dN%{6Pzk`s<% $IV4$)fzIM%uKZ`7`#>jfqwJwR8SH z?|Yu-eV+5~&&|% |QH+1%Xpx?Jn)>!DC+U|=8^3 Qf$__gA6=zv@r(=lH zB=H%pqChN2+iJKa(?;HzK6kVF(^D_J`MY`RBrsi~Y#)EpqW8>erYqAjU^mv6n+*A} zJ>uO8x^oVRS%dL?IW@zNHIPxqu(3SCaim-*8#E|mG(bU+0O2D{E$Tc60Z6J5#v~Ah zb^^Q7&T08^hk<17w9{vBm7&!@{Y=1QpUg9_>I}elq|BiQD5?Uy?v0r94jCg6(pXyG zU YDUAb)A|iQ8K0<3RU9byW&W7iK?v zS~)pJ6LVq}{6QC4!|@u?u!udW%Sh{YV@RT?<+DmS1e053o;@uYcu5epL_w8`F#@Cy z=nM5=5C;JS85$l@1^L7q<^hLnK+&rPC`kv1Rw6;VgBYCeNBA*UYBeT^*%5-G;3}l} zCh-VQeX_Djg4!Yz`RR%|p}f$pXfWm%expJ0L(zZ$1n|lQWTVYjtgB2nT^qJ8*XgA5 zU0*zH%NQz-rWdzE%HCPM$A+W24(sGK?b1;AlC84h?_ccQ*Yx4T#H1FPAdJllX_oX- zraV5rM=}w~6qQG#Dx4SjK?*`rLQ&0?S4Kz4ECx24L*%jTL27rBxN4(=z*b@Eokog9 ztL4H2?4!E$v>{mYRE3-Fr=FckuLsg`*{aj&&l5HBdlhz29GlVc98I;FWxHKg(= _tkWpZ*FEL&Kf5?n>pB>7RN$Q`{^^Dymp1J*+cfwgxEv~h86>Z(~KPx zaTTf>#m9W2i9<(%jJh7vF{5n#|0L0B^DQUstw4$4QkbcwFV}#%!=}LULED#YFM414 zE(Lt}uP<6r B)RZU!QoJZRd6D1gjmfZ)XVXuupMBMq^5~-Fx0yYznMqg}_H2WEt_1 zKfiN1L6FCP4=yj%)Ic4%;8TArW?XoSde|qzYYW*Aro@8pmp6gebBvTgKDZBVKMz6K ztxM;ZaW{RI=}b*xJl>b~YxQUSNs0gdf0`ZdlCMwS8(n^u!&BawgGy`wR(&qbNXE?1 z_XR8yoy^haE5+TA0@Eqp=Q=uUPab#ZIq`mP`EP}-%$N}*QPqR#JsQ2K3&GF%J$fJr z2-5@I9+|wNla$!-L>qxwIFaJ%F%85pQf!L@28h!$48uk_(r`e{&d#wy%whZF#GF96 z{xu(EPhdNFI;Y)YrA`*RE;jzWY?r?4LCnoRCF35OwkdPa%g-K~`K+IQGvCtJl>Xyx z`^ F3GuLJIuybLR-|tOIst(gQ-rDWg<(rR&5W@u#|_66 zaM#B OH*SiW)U+gqkt*)oec$#J@c#=EjkgCcXc>SJKi$aI^|MlwXzlsl7u zYZgILc4_^=kQPO{bqYm!RZt{*z~ z*PHQaaWpymmp|;nJ@~c8*D`Ir M#(d5HkQqB1D-C get_tag() ) { + return null; + } + + $positions = $this->get_after_opener_tag_and_before_closer_tag_positions(); + if ( ! $positions ) { + return null; + } + list( $after_opener_tag, $before_closer_tag ) = $positions; + + return substr( $this->html, $after_opener_tag, $before_closer_tag - $after_opener_tag ); + } + + /** + * Sets the content between two balanced tags. + * + * @since 6.5.0 + * + * @access private + * + * @param string $new_content The string to replace the content between the matching tags. + * @return bool Whether the content was successfully replaced. + */ + public function set_content_between_balanced_tags( string $new_content ): bool { + $positions = $this->get_after_opener_tag_and_before_closer_tag_positions( true ); + if ( ! $positions ) { + return false; + } + list( $after_opener_tag, $before_closer_tag ) = $positions; + + $this->lexical_updates[] = new WP_HTML_Text_Replacement( + $after_opener_tag, + $before_closer_tag - $after_opener_tag, + esc_html( $new_content ) + ); + + return true; + } + + /** + * Appends content after the closing tag of a template tag. + * + * It positions the cursor in the closer tag of the balanced template tag, + * if it exists. + * + * @access private + * + * @param string $new_content The string to append after the closing template tag. + * @return bool Whether the content was successfully appended. + */ + public function append_content_after_template_tag_closer( string $new_content ): bool { + if ( empty( $new_content ) || 'TEMPLATE' !== $this->get_tag() || ! $this->is_tag_closer() ) { + return false; + } + + // Flushes any changes. + $this->get_updated_html(); + + $bookmark = 'append_content_after_template_tag_closer'; + $this->set_bookmark( $bookmark ); + $after_closing_tag = $this->bookmarks[ $bookmark ]->start + $this->bookmarks[ $bookmark ]->length + 1; + $this->release_bookmark( $bookmark ); + + // Appends the new content. + $this->lexical_updates[] = new WP_HTML_Text_Replacement( $after_closing_tag, 0, $new_content ); + + return true; + } + + /** + * Gets the positions right after the opener tag and right before the closer + * tag in a balanced tag. + * + * By default, it positions the cursor in the closer tag of the balanced tag. + * If $rewind is true, it seeks back to the opener tag. + * + * @since 6.5.0 + * + * @access private + * + * @param bool $rewind Optional. Whether to seek back to the opener tag after finding the positions. Defaults to false. + * @return array|null Start and end byte position, or null when no balanced tag bookmarks. + */ + private function get_after_opener_tag_and_before_closer_tag_positions( bool $rewind = false ) { + // Flushes any changes. + $this->get_updated_html(); + + $bookmarks = $this->get_balanced_tag_bookmarks(); + if ( ! $bookmarks ) { + return null; + } + list( $opener_tag, $closer_tag ) = $bookmarks; + + $after_opener_tag = $this->bookmarks[ $opener_tag ]->start + $this->bookmarks[ $opener_tag ]->length + 1; + $before_closer_tag = $this->bookmarks[ $closer_tag ]->start; + + if ( $rewind ) { + $this->seek( $opener_tag ); + } + + $this->release_bookmark( $opener_tag ); + $this->release_bookmark( $closer_tag ); + + return array( $after_opener_tag, $before_closer_tag ); + } + + /** + * Returns a pair of bookmarks for the current opener tag and the matching + * closer tag. + * + * It positions the cursor in the closer tag of the balanced tag, if it + * exists. + * + * @since 6.5.0 + * + * @return array|null A pair of bookmarks, or null if there's no matching closing tag. + */ + private function get_balanced_tag_bookmarks() { + static $i = 0; + $opener_tag = 'opener_tag_of_balanced_tag_' . ++$i; + + $this->set_bookmark( $opener_tag ); + if ( ! $this->next_balanced_tag_closer_tag() ) { + $this->release_bookmark( $opener_tag ); + return null; + } + + $closer_tag = 'closer_tag_of_balanced_tag_' . ++$i; + $this->set_bookmark( $closer_tag ); + + return array( $opener_tag, $closer_tag ); + } + + /** + * Skips processing the content between tags. + * + * It positions the cursor in the closer tag of the foreign element, if it + * exists. + * + * This function is intended to skip processing SVG and MathML inner content + * instead of bailing out the whole processing. + * + * @since 6.5.0 + * + * @access private + * + * @return bool Whether the foreign content was successfully skipped. + */ + public function skip_to_tag_closer(): bool { + $depth = 1; + $tag_name = $this->get_tag(); + while ( $depth > 0 && $this->next_tag( + array( + 'tag_name' => $tag_name, + 'tag_closers' => 'visit', + ) + ) ) { + if ( $this->has_self_closing_flag() ) { + continue; + } + $depth += $this->is_tag_closer() ? -1 : 1; + } + + return 0 === $depth; + } + + /** + * Finds the matching closing tag for an opening tag. + * + * When called while the processor is on an open tag, it traverses the HTML + * until it finds the matching closer tag, respecting any in-between content, + * including nested tags of the same name. Returns false when called on a + * closer tag, a tag that doesn't have a closer tag (void), a tag that + * doesn't visit the closer tag, or if no matching closing tag was found. + * + * @since 6.5.0 + * + * @access private + * + * @return bool Whether a matching closing tag was found. + */ + public function next_balanced_tag_closer_tag(): bool { + $depth = 0; + $tag_name = $this->get_tag(); + + if ( ! $this->has_and_visits_its_closer_tag() ) { + return false; + } + + while ( $this->next_tag( + array( + 'tag_name' => $tag_name, + 'tag_closers' => 'visit', + ) + ) ) { + if ( ! $this->is_tag_closer() ) { + ++$depth; + continue; + } + + if ( 0 === $depth ) { + return true; + } + + --$depth; + } + + return false; + } + + /** + * Checks whether the current tag has and will visit its matching closer tag. + * + * @since 6.5.0 + * + * @access private + * + * @return bool Whether the current tag has a closer tag. + */ + public function has_and_visits_its_closer_tag(): bool { + $tag_name = $this->get_tag(); + + return null !== $tag_name && ( + ! WP_HTML_Processor::is_void( $tag_name ) && + ! in_array( $tag_name, self::TAGS_THAT_DONT_VISIT_CLOSER_TAG, true ) + ); + } +} diff --git a/wp-includes/interactivity-api/class-wp-interactivity-api.php b/wp-includes/interactivity-api/class-wp-interactivity-api.php new file mode 100644 index 000000000..3db539aae --- /dev/null +++ b/wp-includes/interactivity-api/class-wp-interactivity-api.php @@ -0,0 +1,1000 @@ + 'data_wp_interactive_processor', + 'data-wp-router-region' => 'data_wp_router_region_processor', + 'data-wp-context' => 'data_wp_context_processor', + 'data-wp-bind' => 'data_wp_bind_processor', + 'data-wp-class' => 'data_wp_class_processor', + 'data-wp-style' => 'data_wp_style_processor', + 'data-wp-text' => 'data_wp_text_processor', + /* + * `data-wp-each` needs to be processed in the last place because it moves + * the cursor to the end of the processed items to prevent them to be + * processed twice. + */ + 'data-wp-each' => 'data_wp_each_processor', + ); + + /** + * Holds the initial state of the different Interactivity API stores. + * + * This state is used during the server directive processing. Then, it is + * serialized and sent to the client as part of the interactivity data to be + * recovered during the hydration of the client interactivity stores. + * + * @since 6.5.0 + * @var array + */ + private $state_data = array(); + + /** + * Holds the configuration required by the different Interactivity API stores. + * + * This configuration is serialized and sent to the client as part of the + * interactivity data and can be accessed by the client interactivity stores. + * + * @since 6.5.0 + * @var array + */ + private $config_data = array(); + + /** + * Flag that indicates whether the `data-wp-router-region` directive has + * been found in the HTML and processed. + * + * The value is saved in a private property of the WP_Interactivity_API + * instance instead of using a static variable inside the processor + * function, which would hold the same value for all instances + * independently of whether they have processed any + * `data-wp-router-region` directive or not. + * + * @since 6.5.0 + * @var bool + */ + private $has_processed_router_region = false; + + /** + * Gets and/or sets the initial state of an Interactivity API store for a + * given namespace. + * + * If state for that store namespace already exists, it merges the new + * provided state with the existing one. + * + * @since 6.5.0 + * + * @param string $store_namespace The unique store namespace identifier. + * @param array $state Optional. The array that will be merged with the existing state for the specified + * store namespace. + * @return array The current state for the specified store namespace. This will be the updated state if a $state + * argument was provided. + */ + public function state( string $store_namespace, array $state = array() ): array { + if ( ! isset( $this->state_data[ $store_namespace ] ) ) { + $this->state_data[ $store_namespace ] = array(); + } + if ( is_array( $state ) ) { + $this->state_data[ $store_namespace ] = array_replace_recursive( + $this->state_data[ $store_namespace ], + $state + ); + } + return $this->state_data[ $store_namespace ]; + } + + /** + * Gets and/or sets the configuration of the Interactivity API for a given + * store namespace. + * + * If configuration for that store namespace exists, it merges the new + * provided configuration with the existing one. + * + * @since 6.5.0 + * + * @param string $store_namespace The unique store namespace identifier. + * @param array $config Optional. The array that will be merged with the existing configuration for the + * specified store namespace. + * @return array The configuration for the specified store namespace. This will be the updated configuration if a + * $config argument was provided. + */ + public function config( string $store_namespace, array $config = array() ): array { + if ( ! isset( $this->config_data[ $store_namespace ] ) ) { + $this->config_data[ $store_namespace ] = array(); + } + if ( is_array( $config ) ) { + $this->config_data[ $store_namespace ] = array_replace_recursive( + $this->config_data[ $store_namespace ], + $config + ); + } + return $this->config_data[ $store_namespace ]; + } + + /** + * Prints the serialized client-side interactivity data. + * + * Encodes the config and initial state into JSON and prints them inside a + * script tag of type "application/json". Once in the browser, the state will + * be parsed and used to hydrate the client-side interactivity stores and the + * configuration will be available using a `getConfig` utility. + * + * @since 6.5.0 + */ + public function print_client_interactivity_data() { + if ( empty( $this->state_data ) && empty( $this->config_data ) ) { + return; + } + + $interactivity_data = array(); + + $config = array(); + foreach ( $this->config_data as $key => $value ) { + if ( ! empty( $value ) ) { + $config[ $key ] = $value; + } + } + if ( ! empty( $config ) ) { + $interactivity_data['config'] = $config; + } + + $state = array(); + foreach ( $this->state_data as $key => $value ) { + if ( ! empty( $value ) ) { + $state[ $key ] = $value; + } + } + if ( ! empty( $state ) ) { + $interactivity_data['state'] = $state; + } + + if ( ! empty( $interactivity_data ) ) { + wp_print_inline_script_tag( + wp_json_encode( + $interactivity_data, + JSON_HEX_TAG | JSON_HEX_AMP + ), + array( + 'type' => 'application/json', + 'id' => 'wp-interactivity-data', + ) + ); + } + } + + /** + * Registers the `@wordpress/interactivity` script modules. + * + * @since 6.5.0 + */ + public function register_script_modules() { + $suffix = wp_scripts_get_suffix(); + + wp_register_script_module( + '@wordpress/interactivity', + includes_url( "js/dist/interactivity$suffix.js" ) + ); + + wp_register_script_module( + '@wordpress/interactivity-router', + includes_url( "js/dist/interactivity-router$suffix.js" ), + array( '@wordpress/interactivity' ) + ); + } + + /** + * Adds the necessary hooks for the Interactivity API. + * + * @since 6.5.0 + */ + public function add_hooks() { + add_action( 'wp_enqueue_scripts', array( $this, 'register_script_modules' ) ); + add_action( 'wp_footer', array( $this, 'print_client_interactivity_data' ) ); + } + + /** + * Processes the interactivity directives contained within the HTML content + * and updates the markup accordingly. + * + * @since 6.5.0 + * + * @param string $html The HTML content to process. + * @return string The processed HTML content. It returns the original content when the HTML contains unbalanced tags. + */ + public function process_directives( string $html ): string { + if ( ! str_contains( $html, 'data-wp-' ) ) { + return $html; + } + + $context_stack = array(); + $namespace_stack = array(); + $result = $this->process_directives_args( $html, $context_stack, $namespace_stack ); + return null === $result ? $html : $result; + } + + /** + * Processes the interactivity directives contained within the HTML content + * and updates the markup accordingly. + * + * It needs the context and namespace stacks to be passed by reference, and + * it returns null if the HTML contains unbalanced tags. + * + * @since 6.5.0 + * + * @param string $html The HTML content to process. + * @param array $context_stack The reference to the array used to keep track of contexts during processing. + * @param array $namespace_stack The reference to the array used to manage namespaces during processing. + * @return string|null The processed HTML content. It returns null when the HTML contains unbalanced tags. + */ + private function process_directives_args( string $html, array &$context_stack, array &$namespace_stack ) { + $p = new WP_Interactivity_API_Directives_Processor( $html ); + $tag_stack = array(); + $unbalanced = false; + + $directive_processor_prefixes = array_keys( self::$directive_processors ); + $directive_processor_prefixes_reversed = array_reverse( $directive_processor_prefixes ); + + while ( $p->next_tag( array( 'tag_closers' => 'visit' ) ) ) { + $tag_name = $p->get_tag(); + + /* + * Directives inside SVG and MATH tags are not processed, + * as they are not compatible with the Tag Processor yet. + * We still process the rest of the HTML. + */ + if ( 'SVG' === $tag_name || 'MATH' === $tag_name ) { + $p->skip_to_tag_closer(); + continue; + } + + if ( $p->is_tag_closer() ) { + list( $opening_tag_name, $directives_prefixes ) = end( $tag_stack ); + + if ( 0 === count( $tag_stack ) || $opening_tag_name !== $tag_name ) { + + /* + * If the tag stack is empty or the matching opening tag is not the + * same than the closing tag, it means the HTML is unbalanced and it + * stops processing it. + */ + $unbalanced = true; + break; + } else { + // Remove the last tag from the stack. + array_pop( $tag_stack ); + } + } else { + if ( 0 !== count( $p->get_attribute_names_with_prefix( 'data-wp-each-child' ) ) ) { + /* + * If the tag has a `data-wp-each-child` directive, jump to its closer + * tag because those tags have already been processed. + */ + $p->next_balanced_tag_closer_tag(); + continue; + } else { + $directives_prefixes = array(); + + // Checks if there is a server directive processor registered for each directive. + foreach ( $p->get_attribute_names_with_prefix( 'data-wp-' ) as $attribute_name ) { + list( $directive_prefix ) = $this->extract_prefix_and_suffix( $attribute_name ); + if ( array_key_exists( $directive_prefix, self::$directive_processors ) ) { + $directives_prefixes[] = $directive_prefix; + } + } + + /* + * If this tag will visit its closer tag, it adds it to the tag stack + * so it can process its closing tag and check for unbalanced tags. + */ + if ( $p->has_and_visits_its_closer_tag() ) { + $tag_stack[] = array( $tag_name, $directives_prefixes ); + } + } + } + /* + * If the matching opener tag didn't have any directives, it can skip the + * processing. + */ + if ( 0 === count( $directives_prefixes ) ) { + continue; + } + + // Directive processing might be different depending on if it is entering the tag or exiting it. + $modes = array( + 'enter' => ! $p->is_tag_closer(), + 'exit' => $p->is_tag_closer() || ! $p->has_and_visits_its_closer_tag(), + ); + + foreach ( $modes as $mode => $should_run ) { + if ( ! $should_run ) { + continue; + } + + /* + * Sorts the attributes by the order of the `directives_processor` array + * and checks what directives are present in this element. + */ + $existing_directives_prefixes = array_intersect( + 'enter' === $mode ? $directive_processor_prefixes : $directive_processor_prefixes_reversed, + $directives_prefixes + ); + foreach ( $existing_directives_prefixes as $directive_prefix ) { + $func = is_array( self::$directive_processors[ $directive_prefix ] ) + ? self::$directive_processors[ $directive_prefix ] + : array( $this, self::$directive_processors[ $directive_prefix ] ); + + call_user_func_array( + $func, + array( $p, $mode, &$context_stack, &$namespace_stack, &$tag_stack ) + ); + } + } + } + + /* + * It returns null if the HTML is unbalanced because unbalanced HTML is + * not safe to process. In that case, the Interactivity API runtime will + * update the HTML on the client side during the hydration. + */ + return $unbalanced || 0 < count( $tag_stack ) ? null : $p->get_updated_html(); + } + + /** + * Evaluates the reference path passed to a directive based on the current + * store namespace, state and context. + * + * @since 6.5.0 + * + * @param string|true $directive_value The directive attribute value string or `true` when it's a boolean attribute. + * @param string $default_namespace The default namespace to use if none is explicitly defined in the directive + * value. + * @param array|false $context The current context for evaluating the directive or false if there is no + * context. + * @return mixed|null The result of the evaluation. Null if the reference path doesn't exist. + */ + private function evaluate( $directive_value, string $default_namespace, $context = false ) { + list( $ns, $path ) = $this->extract_directive_value( $directive_value, $default_namespace ); + if ( empty( $path ) ) { + return null; + } + + $store = array( + 'state' => $this->state_data[ $ns ] ?? array(), + 'context' => $context[ $ns ] ?? array(), + ); + + // Checks if the reference path is preceded by a negation operator (!). + $should_negate_value = '!' === $path[0]; + $path = $should_negate_value ? substr( $path, 1 ) : $path; + + // Extracts the value from the store using the reference path. + $path_segments = explode( '.', $path ); + $current = $store; + foreach ( $path_segments as $path_segment ) { + if ( isset( $current[ $path_segment ] ) ) { + $current = $current[ $path_segment ]; + } else { + return null; + } + } + + // Returns the opposite if it contains a negation operator (!). + return $should_negate_value ? ! $current : $current; + } + + /** + * Extracts the directive attribute name to separate and return the directive + * prefix and an optional suffix. + * + * The suffix is the string after the first double hyphen and the prefix is + * everything that comes before the suffix. + * + * Example: + * + * extract_prefix_and_suffix( 'data-wp-interactive' ) => array( 'data-wp-interactive', null ) + * extract_prefix_and_suffix( 'data-wp-bind--src' ) => array( 'data-wp-bind', 'src' ) + * extract_prefix_and_suffix( 'data-wp-foo--and--bar' ) => array( 'data-wp-foo', 'and--bar' ) + * + * @since 6.5.0 + * + * @param string $directive_name The directive attribute name. + * @return array An array containing the directive prefix and optional suffix. + */ + private function extract_prefix_and_suffix( string $directive_name ): array { + return explode( '--', $directive_name, 2 ); + } + + /** + * Parses and extracts the namespace and reference path from the given + * directive attribute value. + * + * If the value doesn't contain an explicit namespace, it returns the + * default one. If the value contains a JSON object instead of a reference + * path, the function tries to parse it and return the resulting array. If + * the value contains strings that represent booleans ("true" and "false"), + * numbers ("1" and "1.2") or "null", the function also transform them to + * regular booleans, numbers and `null`. + * + * Example: + * + * extract_directive_value( 'actions.foo', 'myPlugin' ) => array( 'myPlugin', 'actions.foo' ) + * extract_directive_value( 'otherPlugin::actions.foo', 'myPlugin' ) => array( 'otherPlugin', 'actions.foo' ) + * extract_directive_value( '{ "isOpen": false }', 'myPlugin' ) => array( 'myPlugin', array( 'isOpen' => false ) ) + * extract_directive_value( 'otherPlugin::{ "isOpen": false }', 'myPlugin' ) => array( 'otherPlugin', array( 'isOpen' => false ) ) + * + * @since 6.5.0 + * + * @param string|true $directive_value The directive attribute value. It can be `true` when it's a boolean + * attribute. + * @param string|null $default_namespace Optional. The default namespace if none is explicitly defined. + * @return array An array containing the namespace in the first item and the JSON, the reference path, or null on the + * second item. + */ + private function extract_directive_value( $directive_value, $default_namespace = null ): array { + if ( empty( $directive_value ) || is_bool( $directive_value ) ) { + return array( $default_namespace, null ); + } + + // Replaces the value and namespace if there is a namespace in the value. + if ( 1 === preg_match( '/^([\w\-_\/]+)::./', $directive_value ) ) { + list($default_namespace, $directive_value) = explode( '::', $directive_value, 2 ); + } + + /* + * Tries to decode the value as a JSON object. If it fails and the value + * isn't `null`, it returns the value as it is. Otherwise, it returns the + * decoded JSON or null for the string `null`. + */ + $decoded_json = json_decode( $directive_value, true ); + if ( null !== $decoded_json || 'null' === $directive_value ) { + $directive_value = $decoded_json; + } + + return array( $default_namespace, $directive_value ); + } + + /** + * Transforms a kebab-case string to camelCase. + * + * @param string $str The kebab-case string to transform to camelCase. + * @return string The transformed camelCase string. + */ + private function kebab_to_camel_case( string $str ): string { + return lcfirst( + preg_replace_callback( + '/(-)([a-z])/', + function ( $matches ) { + return strtoupper( $matches[2] ); + }, + strtolower( rtrim( $str, '-' ) ) + ) + ); + } + + /** + * Processes the `data-wp-interactive` directive. + * + * It adds the default store namespace defined in the directive value to the + * stack so that it's available for the nested interactivity elements. + * + * @since 6.5.0 + * + * @param WP_Interactivity_API_Directives_Processor $p The directives processor instance. + * @param string $mode Whether the processing is entering or exiting the tag. + * @param array $context_stack The reference to the context stack. + * @param array $namespace_stack The reference to the store namespace stack. + */ + private function data_wp_interactive_processor( WP_Interactivity_API_Directives_Processor $p, string $mode, array &$context_stack, array &$namespace_stack ) { + // When exiting tags, it removes the last namespace from the stack. + if ( 'exit' === $mode ) { + array_pop( $namespace_stack ); + return; + } + + // Tries to decode the `data-wp-interactive` attribute value. + $attribute_value = $p->get_attribute( 'data-wp-interactive' ); + + /* + * Pushes the newly defined namespace or the current one if the + * `data-wp-interactive` definition was invalid or does not contain a + * namespace. It does so because the function pops out the current namespace + * from the stack whenever it finds a `data-wp-interactive`'s closing tag, + * independently of whether the previous `data-wp-interactive` definition + * contained a valid namespace. + */ + $new_namespace = null; + if ( is_string( $attribute_value ) && ! empty( $attribute_value ) ) { + $decoded_json = json_decode( $attribute_value, true ); + if ( is_array( $decoded_json ) ) { + $new_namespace = $decoded_json['namespace'] ?? null; + } else { + $new_namespace = $attribute_value; + } + } + $namespace_stack[] = ( $new_namespace && 1 === preg_match( '/^([\w\-_\/]+)/', $new_namespace ) ) + ? $new_namespace + : end( $namespace_stack ); + } + + /** + * Processes the `data-wp-context` directive. + * + * It adds the context defined in the directive value to the stack so that + * it's available for the nested interactivity elements. + * + * @since 6.5.0 + * + * @param WP_Interactivity_API_Directives_Processor $p The directives processor instance. + * @param string $mode Whether the processing is entering or exiting the tag. + * @param array $context_stack The reference to the context stack. + * @param array $namespace_stack The reference to the store namespace stack. + */ + private function data_wp_context_processor( WP_Interactivity_API_Directives_Processor $p, string $mode, array &$context_stack, array &$namespace_stack ) { + // When exiting tags, it removes the last context from the stack. + if ( 'exit' === $mode ) { + array_pop( $context_stack ); + return; + } + + $attribute_value = $p->get_attribute( 'data-wp-context' ); + $namespace_value = end( $namespace_stack ); + + // Separates the namespace from the context JSON object. + list( $namespace_value, $decoded_json ) = is_string( $attribute_value ) && ! empty( $attribute_value ) + ? $this->extract_directive_value( $attribute_value, $namespace_value ) + : array( $namespace_value, null ); + + /* + * If there is a namespace, it adds a new context to the stack merging the + * previous context with the new one. + */ + if ( is_string( $namespace_value ) ) { + $context_stack[] = array_replace_recursive( + end( $context_stack ) !== false ? end( $context_stack ) : array(), + array( $namespace_value => is_array( $decoded_json ) ? $decoded_json : array() ) + ); + } else { + /* + * If there is no namespace, it pushes the current context to the stack. + * It needs to do so because the function pops out the current context + * from the stack whenever it finds a `data-wp-context`'s closing tag. + */ + $context_stack[] = end( $context_stack ); + } + } + + /** + * Processes the `data-wp-bind` directive. + * + * It updates or removes the bound attributes based on the evaluation of its + * associated reference. + * + * @since 6.5.0 + * + * @param WP_Interactivity_API_Directives_Processor $p The directives processor instance. + * @param string $mode Whether the processing is entering or exiting the tag. + * @param array $context_stack The reference to the context stack. + * @param array $namespace_stack The reference to the store namespace stack. + */ + private function data_wp_bind_processor( WP_Interactivity_API_Directives_Processor $p, string $mode, array &$context_stack, array &$namespace_stack ) { + if ( 'enter' === $mode ) { + $all_bind_directives = $p->get_attribute_names_with_prefix( 'data-wp-bind--' ); + + foreach ( $all_bind_directives as $attribute_name ) { + list( , $bound_attribute ) = $this->extract_prefix_and_suffix( $attribute_name ); + if ( empty( $bound_attribute ) ) { + return; + } + + $attribute_value = $p->get_attribute( $attribute_name ); + $result = $this->evaluate( $attribute_value, end( $namespace_stack ), end( $context_stack ) ); + + if ( + null !== $result && + ( + false !== $result || + ( strlen( $bound_attribute ) > 5 && '-' === $bound_attribute[4] ) + ) + ) { + /* + * If the result of the evaluation is a boolean and the attribute is + * `aria-` or `data-, convert it to a string "true" or "false". It + * follows the exact same logic as Preact because it needs to + * replicate what Preact will later do in the client: + * https://github.com/preactjs/preact/blob/ea49f7a0f9d1ff2c98c0bdd66aa0cbc583055246/src/diff/props.js#L131C24-L136 + */ + if ( + is_bool( $result ) && + ( strlen( $bound_attribute ) > 5 && '-' === $bound_attribute[4] ) + ) { + $result = $result ? 'true' : 'false'; + } + $p->set_attribute( $bound_attribute, $result ); + } else { + $p->remove_attribute( $bound_attribute ); + } + } + } + } + + /** + * Processes the `data-wp-class` directive. + * + * It adds or removes CSS classes in the current HTML element based on the + * evaluation of its associated references. + * + * @since 6.5.0 + * + * @param WP_Interactivity_API_Directives_Processor $p The directives processor instance. + * @param string $mode Whether the processing is entering or exiting the tag. + * @param array $context_stack The reference to the context stack. + * @param array $namespace_stack The reference to the store namespace stack. + */ + private function data_wp_class_processor( WP_Interactivity_API_Directives_Processor $p, string $mode, array &$context_stack, array &$namespace_stack ) { + if ( 'enter' === $mode ) { + $all_class_directives = $p->get_attribute_names_with_prefix( 'data-wp-class--' ); + + foreach ( $all_class_directives as $attribute_name ) { + list( , $class_name ) = $this->extract_prefix_and_suffix( $attribute_name ); + if ( empty( $class_name ) ) { + return; + } + + $attribute_value = $p->get_attribute( $attribute_name ); + $result = $this->evaluate( $attribute_value, end( $namespace_stack ), end( $context_stack ) ); + + if ( $result ) { + $p->add_class( $class_name ); + } else { + $p->remove_class( $class_name ); + } + } + } + } + + /** + * Processes the `data-wp-style` directive. + * + * It updates the style attribute value of the current HTML element based on + * the evaluation of its associated references. + * + * @since 6.5.0 + * + * @param WP_Interactivity_API_Directives_Processor $p The directives processor instance. + * @param string $mode Whether the processing is entering or exiting the tag. + * @param array $context_stack The reference to the context stack. + * @param array $namespace_stack The reference to the store namespace stack. + */ + private function data_wp_style_processor( WP_Interactivity_API_Directives_Processor $p, string $mode, array &$context_stack, array &$namespace_stack ) { + if ( 'enter' === $mode ) { + $all_style_attributes = $p->get_attribute_names_with_prefix( 'data-wp-style--' ); + + foreach ( $all_style_attributes as $attribute_name ) { + list( , $style_property ) = $this->extract_prefix_and_suffix( $attribute_name ); + if ( empty( $style_property ) ) { + continue; + } + + $directive_attribute_value = $p->get_attribute( $attribute_name ); + $style_property_value = $this->evaluate( $directive_attribute_value, end( $namespace_stack ), end( $context_stack ) ); + $style_attribute_value = $p->get_attribute( 'style' ); + $style_attribute_value = ( $style_attribute_value && ! is_bool( $style_attribute_value ) ) ? $style_attribute_value : ''; + + /* + * Checks first if the style property is not falsy and the style + * attribute value is not empty because if it is, it doesn't need to + * update the attribute value. + */ + if ( $style_property_value || $style_attribute_value ) { + $style_attribute_value = $this->merge_style_property( $style_attribute_value, $style_property, $style_property_value ); + /* + * If the style attribute value is not empty, it sets it. Otherwise, + * it removes it. + */ + if ( ! empty( $style_attribute_value ) ) { + $p->set_attribute( 'style', $style_attribute_value ); + } else { + $p->remove_attribute( 'style' ); + } + } + } + } + } + + /** + * Merges an individual style property in the `style` attribute of an HTML + * element, updating or removing the property when necessary. + * + * If a property is modified, the old one is removed and the new one is added + * at the end of the list. + * + * @since 6.5.0 + * + * Example: + * + * merge_style_property( 'color:green;', 'color', 'red' ) => 'color:red;' + * merge_style_property( 'background:green;', 'color', 'red' ) => 'background:green;color:red;' + * merge_style_property( 'color:green;', 'color', null ) => '' + * + * @param string $style_attribute_value The current style attribute value. + * @param string $style_property_name The style property name to set. + * @param string|false|null $style_property_value The value to set for the style property. With false, null or an + * empty string, it removes the style property. + * @return string The new style attribute value after the specified property has been added, updated or removed. + */ + private function merge_style_property( string $style_attribute_value, string $style_property_name, $style_property_value ): string { + $style_assignments = explode( ';', $style_attribute_value ); + $result = array(); + $style_property_value = ! empty( $style_property_value ) ? rtrim( trim( $style_property_value ), ';' ) : null; + $new_style_property = $style_property_value ? $style_property_name . ':' . $style_property_value . ';' : ''; + + // Generates an array with all the properties but the modified one. + foreach ( $style_assignments as $style_assignment ) { + if ( empty( trim( $style_assignment ) ) ) { + continue; + } + list( $name, $value ) = explode( ':', $style_assignment ); + if ( trim( $name ) !== $style_property_name ) { + $result[] = trim( $name ) . ':' . trim( $value ) . ';'; + } + } + + // Adds the new/modified property at the end of the list. + $result[] = $new_style_property; + + return implode( '', $result ); + } + + /** + * Processes the `data-wp-text` directive. + * + * It updates the inner content of the current HTML element based on the + * evaluation of its associated reference. + * + * @since 6.5.0 + * + * @param WP_Interactivity_API_Directives_Processor $p The directives processor instance. + * @param string $mode Whether the processing is entering or exiting the tag. + * @param array $context_stack The reference to the context stack. + * @param array $namespace_stack The reference to the store namespace stack. + */ + private function data_wp_text_processor( WP_Interactivity_API_Directives_Processor $p, string $mode, array &$context_stack, array &$namespace_stack ) { + if ( 'enter' === $mode ) { + $attribute_value = $p->get_attribute( 'data-wp-text' ); + $result = $this->evaluate( $attribute_value, end( $namespace_stack ), end( $context_stack ) ); + + /* + * Follows the same logic as Preact in the client and only changes the + * content if the value is a string or a number. Otherwise, it removes the + * content. + */ + if ( is_string( $result ) || is_numeric( $result ) ) { + $p->set_content_between_balanced_tags( esc_html( $result ) ); + } else { + $p->set_content_between_balanced_tags( '' ); + } + } + } + + /** + * Returns the CSS styles for animating the top loading bar in the router. + * + * @since 6.5.0 + * + * @return string The CSS styles for the router's top loading bar animation. + */ + private function get_router_animation_styles(): string { + return <<
\s*
/g,"\n\n")).replace(new RegExp("(<"+t+"[\\s/>])","g"),"\n\n$1")).replace(new RegExp("("+t+">)","g"),"$1\n\n")).replace(/\r\n|\r/g,"\n"),{"\n":" \x3c!-- wpnl --\x3e "})).indexOf("")),-1!==e.indexOf("")&&(e=(e=(e=e.replace(/(")).replace(/\s*(<\/?(?:param|embed)[^>]*>)\s*/g,"$1")),-1===e.indexOf("
"+n.replace(/^\n*|\n*$/g,"")+"
\n"})),e=(e=(e=(e=(e=(e=(e=(e=e.replace(/\s*<\/p>/g,"")).replace(/
([^<]+)<\/(div|address|form)>/g,"
$1
$2>")).replace(new RegExp("\\s*(?"+t+"[^>]*>)\\s*
","g"),"$1")).replace(/(
]*)>/gi,"")).replace(new RegExp("")).replace(/<\/blockquote><\/p>/g,"
\\s*(?"+t+"[^>]*>)","g"),"$1")).replace(new RegExp("(?"+t+"[^>]*>)\\s*
","g"),"$1"),n&&(e=(e=(e=(e=e.replace(/<(script|style).*?<\/\\1>/g,(e=>e[0].replace(/\n/g,"")))).replace(/
|
/g,"
")).replace(/(
)?\s*\n/g,((e,n)=>n?e:"
\n"))).replace(//g,"\n")),e=(e=(e=e.replace(new RegExp("(?"+t+"[^>]*>)\\s*
","g"),"$1")).replace(/
(\s*<\/?(?:p|li|div|dl|dd|dt|th|pre|td|ul|ol)[^>]*>)/g,"$1")).replace(/\n<\/p>$/g,""),p.forEach((n=>{const[p,r]=n;e=e.replace(p,r)})),-1!==e.indexOf("\x3c!-- wpnl --\x3e")&&(e=e.replace(/\s?\s?/g,"\n")),e}function c(e){const n="blockquote|ul|ol|li|dl|dt|dd|table|thead|tbody|tfoot|tr|th|td|h[1-6]|fieldset|figure",p=n+"|div|p",r=n+"|pre",t=[];let c=!1,l=!1;return e?(-1===e.indexOf(" - - ${styles} - ${scripts} - - - - -`; - const [src, cleanup] = (0,external_wp_element_namespaceObject.useMemo)(() => { - const _src = URL.createObjectURL(new window.Blob([html], { - type: 'text/html' - })); - return [_src, () => URL.revokeObjectURL(_src)]; - }, [html]); - (0,external_wp_element_namespaceObject.useEffect)(() => cleanup, [cleanup]); - - // We need to counter the margin created by scaling the iframe. If the scale - // is e.g. 0.45, then the top + bottom margin is 0.55 (1 - scale). Just the - // top or bottom margin is 0.55 / 2 ((1 - scale) / 2). - const marginFromScaling = contentHeight * (1 - scale) / 2; - return (0,external_wp_element_namespaceObject.createElement)(external_wp_element_namespaceObject.Fragment, null, tabIndex >= 0 && before, (0,external_wp_element_namespaceObject.createElement)("iframe", { - ...props, - style: { - border: 0, - ...props.style, - height: expand ? contentHeight : props.style?.height, - marginTop: scale !== 1 ? -marginFromScaling + frameSize : props.style?.marginTop, - marginBottom: scale !== 1 ? -marginFromScaling + frameSize : props.style?.marginBottom, - transform: scale !== 1 ? `scale( ${scale} )` : props.style?.transform, - transition: 'all .3s' - }, - ref: (0,external_wp_compose_namespaceObject.useMergeRefs)([ref, setRef]), - tabIndex: tabIndex - // Correct doctype is required to enable rendering in standards - // mode. Also preload the styles to avoid a flash of unstyled - // content. - , - src: src, - title: (0,external_wp_i18n_namespaceObject.__)('Editor canvas'), - onKeyDown: event => { - // If the event originates from inside the iframe, it means - // it bubbled through the portal, but only with React - // events. We need to to bubble native events as well, - // though by doing so we also trigger another React event, - // so we need to stop the propagation of this event to avoid - // duplication. - if (event.currentTarget.ownerDocument !== event.target.ownerDocument) { - event.stopPropagation(); - bubbleEvent(event, window.KeyboardEvent, event.currentTarget); - } + if ((0,external_wp_blob_namespaceObject.isBlobURL)(media.url)) { + return; } - }, iframeDocument && (0,external_wp_element_namespaceObject.createPortal)( - // We want to prevent React events from bubbling throught the iframe - // we bubble these manually. - /* eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions */ - (0,external_wp_element_namespaceObject.createElement)("body", { - ref: bodyRef, - className: classnames_default()('block-editor-iframe__body', 'editor-styles-wrapper', ...bodyClasses) - }, contentResizeListener, (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.__experimentalStyleProvider, { - document: iframeDocument - }, children)), iframeDocument.documentElement)), tabIndex >= 0 && after); -} -function IframeIfReady(props, ref) { - const isInitialised = (0,external_wp_data_namespaceObject.useSelect)(select => select(store).getSettings().__internalIsInitialized, []); - - // We shouldn't render the iframe until the editor settings are initialised. - // The initial settings are needed to get the styles for the srcDoc, which - // cannot be changed after the iframe is mounted. srcDoc is used to to set - // the initial iframe HTML, which is required to avoid a flash of unstyled - // content. - if (!isInitialised) { - return null; - } - return (0,external_wp_element_namespaceObject.createElement)(Iframe, { - ...props, - forwardedRef: ref - }); -} -/* harmony default export */ var iframe = ((0,external_wp_element_namespaceObject.forwardRef)(IframeIfReady)); - -;// CONCATENATED MODULE: ./node_modules/colord/index.mjs -var colord_r={grad:.9,turn:360,rad:360/(2*Math.PI)},t=function(r){return"string"==typeof r?r.length>0:"number"==typeof r},n=function(r,t,n){return void 0===t&&(t=0),void 0===n&&(n=Math.pow(10,t)),Math.round(n*r)/n+0},e=function(r,t,n){return void 0===t&&(t=0),void 0===n&&(n=1),r>n?n:r>t?r:t},colord_u=function(r){return(r=isFinite(r)?r%360:0)>0?r:r+360},a=function(r){return{r:e(r.r,0,255),g:e(r.g,0,255),b:e(r.b,0,255),a:e(r.a)}},o=function(r){return{r:n(r.r),g:n(r.g),b:n(r.b),a:n(r.a,3)}},colord_i=/^#([0-9a-f]{3,8})$/i,s=function(r){var t=r.toString(16);return t.length<2?"0"+t:t},colord_h=function(r){var t=r.r,n=r.g,e=r.b,u=r.a,a=Math.max(t,n,e),o=a-Math.min(t,n,e),i=o?a===t?(n-e)/o:a===n?2+(e-t)/o:4+(t-n)/o:0;return{h:60*(i<0?i+6:i),s:a?o/a*100:0,v:a/255*100,a:u}},colord_b=function(r){var t=r.h,n=r.s,e=r.v,u=r.a;t=t/360*6,n/=100,e/=100;var a=Math.floor(t),o=e*(1-n),i=e*(1-(t-a)*n),s=e*(1-(1-t+a)*n),h=a%6;return{r:255*[e,i,o,o,s,e][h],g:255*[s,e,e,i,o,o][h],b:255*[o,o,s,e,e,i][h],a:u}},colord_g=function(r){return{h:colord_u(r.h),s:e(r.s,0,100),l:e(r.l,0,100),a:e(r.a)}},colord_d=function(r){return{h:n(r.h),s:n(r.s),l:n(r.l),a:n(r.a,3)}},colord_f=function(r){return colord_b((n=(t=r).s,{h:t.h,s:(n*=((e=t.l)<50?e:100-e)/100)>0?2*n/(e+n)*100:0,v:e+n,a:t.a}));var t,n,e},colord_c=function(r){return{h:(t=colord_h(r)).h,s:(u=(200-(n=t.s))*(e=t.v)/100)>0&&u<200?n*e/100/(u<=100?u:200-u)*100:0,l:u/2,a:t.a};var t,n,e,u},colord_l=/^hsla?\(\s*([+-]?\d*\.?\d+)(deg|rad|grad|turn)?\s*,\s*([+-]?\d*\.?\d+)%\s*,\s*([+-]?\d*\.?\d+)%\s*(?:,\s*([+-]?\d*\.?\d+)(%)?\s*)?\)$/i,colord_p=/^hsla?\(\s*([+-]?\d*\.?\d+)(deg|rad|grad|turn)?\s+([+-]?\d*\.?\d+)%\s+([+-]?\d*\.?\d+)%\s*(?:\/\s*([+-]?\d*\.?\d+)(%)?\s*)?\)$/i,colord_v=/^rgba?\(\s*([+-]?\d*\.?\d+)(%)?\s*,\s*([+-]?\d*\.?\d+)(%)?\s*,\s*([+-]?\d*\.?\d+)(%)?\s*(?:,\s*([+-]?\d*\.?\d+)(%)?\s*)?\)$/i,colord_m=/^rgba?\(\s*([+-]?\d*\.?\d+)(%)?\s+([+-]?\d*\.?\d+)(%)?\s+([+-]?\d*\.?\d+)(%)?\s*(?:\/\s*([+-]?\d*\.?\d+)(%)?\s*)?\)$/i,colord_y={string:[[function(r){var t=colord_i.exec(r);return t?(r=t[1]).length<=4?{r:parseInt(r[0]+r[0],16),g:parseInt(r[1]+r[1],16),b:parseInt(r[2]+r[2],16),a:4===r.length?n(parseInt(r[3]+r[3],16)/255,2):1}:6===r.length||8===r.length?{r:parseInt(r.substr(0,2),16),g:parseInt(r.substr(2,2),16),b:parseInt(r.substr(4,2),16),a:8===r.length?n(parseInt(r.substr(6,2),16)/255,2):1}:null:null},"hex"],[function(r){var t=colord_v.exec(r)||colord_m.exec(r);return t?t[2]!==t[4]||t[4]!==t[6]?null:a({r:Number(t[1])/(t[2]?100/255:1),g:Number(t[3])/(t[4]?100/255:1),b:Number(t[5])/(t[6]?100/255:1),a:void 0===t[7]?1:Number(t[7])/(t[8]?100:1)}):null},"rgb"],[function(t){var n=colord_l.exec(t)||colord_p.exec(t);if(!n)return null;var e,u,a=colord_g({h:(e=n[1],u=n[2],void 0===u&&(u="deg"),Number(e)*(colord_r[u]||1)),s:Number(n[3]),l:Number(n[4]),a:void 0===n[5]?1:Number(n[5])/(n[6]?100:1)});return colord_f(a)},"hsl"]],object:[[function(r){var n=r.r,e=r.g,u=r.b,o=r.a,i=void 0===o?1:o;return t(n)&&t(e)&&t(u)?a({r:Number(n),g:Number(e),b:Number(u),a:Number(i)}):null},"rgb"],[function(r){var n=r.h,e=r.s,u=r.l,a=r.a,o=void 0===a?1:a;if(!t(n)||!t(e)||!t(u))return null;var i=colord_g({h:Number(n),s:Number(e),l:Number(u),a:Number(o)});return colord_f(i)},"hsl"],[function(r){var n=r.h,a=r.s,o=r.v,i=r.a,s=void 0===i?1:i;if(!t(n)||!t(a)||!t(o))return null;var h=function(r){return{h:colord_u(r.h),s:e(r.s,0,100),v:e(r.v,0,100),a:e(r.a)}}({h:Number(n),s:Number(a),v:Number(o),a:Number(s)});return colord_b(h)},"hsv"]]},colord_N=function(r,t){for(var n=0;n=.5},r.prototype.toHex=function(){return r=o(this.rgba),t=r.r,e=r.g,u=r.b,i=(a=r.a)<1?s(n(255*a)):"","#"+s(t)+s(e)+s(u)+i;var r,t,e,u,a,i},r.prototype.toRgb=function(){return o(this.rgba)},r.prototype.toRgbString=function(){return r=o(this.rgba),t=r.r,n=r.g,e=r.b,(u=r.a)<1?"rgba("+t+", "+n+", "+e+", "+u+")":"rgb("+t+", "+n+", "+e+")";var r,t,n,e,u},r.prototype.toHsl=function(){return colord_d(colord_c(this.rgba))},r.prototype.toHslString=function(){return r=colord_d(colord_c(this.rgba)),t=r.h,n=r.s,e=r.l,(u=r.a)<1?"hsla("+t+", "+n+"%, "+e+"%, "+u+")":"hsl("+t+", "+n+"%, "+e+"%)";var r,t,n,e,u},r.prototype.toHsv=function(){return r=colord_h(this.rgba),{h:n(r.h),s:n(r.s),v:n(r.v),a:n(r.a,3)};var r},r.prototype.invert=function(){return colord_w({r:255-(r=this.rgba).r,g:255-r.g,b:255-r.b,a:r.a});var r},r.prototype.saturate=function(r){return void 0===r&&(r=.1),colord_w(colord_M(this.rgba,r))},r.prototype.desaturate=function(r){return void 0===r&&(r=.1),colord_w(colord_M(this.rgba,-r))},r.prototype.grayscale=function(){return colord_w(colord_M(this.rgba,-1))},r.prototype.lighten=function(r){return void 0===r&&(r=.1),colord_w(colord_$(this.rgba,r))},r.prototype.darken=function(r){return void 0===r&&(r=.1),colord_w(colord_$(this.rgba,-r))},r.prototype.rotate=function(r){return void 0===r&&(r=15),this.hue(this.hue()+r)},r.prototype.alpha=function(r){return"number"==typeof r?colord_w({r:(t=this.rgba).r,g:t.g,b:t.b,a:r}):n(this.rgba.a,3);var t},r.prototype.hue=function(r){var t=colord_c(this.rgba);return"number"==typeof r?colord_w({h:r,s:t.s,l:t.l,a:t.a}):n(t.h)},r.prototype.isEqual=function(r){return this.toHex()===colord_w(r).toHex()},r}(),colord_w=function(r){return r instanceof colord_j?r:new colord_j(r)},colord_S=[],colord_k=function(r){r.forEach(function(r){colord_S.indexOf(r)<0&&(r(colord_j,colord_y),colord_S.push(r))})},colord_E=function(){return new colord_j({r:255*Math.random(),g:255*Math.random(),b:255*Math.random()})}; - -;// CONCATENATED MODULE: ./node_modules/colord/plugins/names.mjs -/* harmony default export */ function names(e,f){var a={white:"#ffffff",bisque:"#ffe4c4",blue:"#0000ff",cadetblue:"#5f9ea0",chartreuse:"#7fff00",chocolate:"#d2691e",coral:"#ff7f50",antiquewhite:"#faebd7",aqua:"#00ffff",azure:"#f0ffff",whitesmoke:"#f5f5f5",papayawhip:"#ffefd5",plum:"#dda0dd",blanchedalmond:"#ffebcd",black:"#000000",gold:"#ffd700",goldenrod:"#daa520",gainsboro:"#dcdcdc",cornsilk:"#fff8dc",cornflowerblue:"#6495ed",burlywood:"#deb887",aquamarine:"#7fffd4",beige:"#f5f5dc",crimson:"#dc143c",cyan:"#00ffff",darkblue:"#00008b",darkcyan:"#008b8b",darkgoldenrod:"#b8860b",darkkhaki:"#bdb76b",darkgray:"#a9a9a9",darkgreen:"#006400",darkgrey:"#a9a9a9",peachpuff:"#ffdab9",darkmagenta:"#8b008b",darkred:"#8b0000",darkorchid:"#9932cc",darkorange:"#ff8c00",darkslateblue:"#483d8b",gray:"#808080",darkslategray:"#2f4f4f",darkslategrey:"#2f4f4f",deeppink:"#ff1493",deepskyblue:"#00bfff",wheat:"#f5deb3",firebrick:"#b22222",floralwhite:"#fffaf0",ghostwhite:"#f8f8ff",darkviolet:"#9400d3",magenta:"#ff00ff",green:"#008000",dodgerblue:"#1e90ff",grey:"#808080",honeydew:"#f0fff0",hotpink:"#ff69b4",blueviolet:"#8a2be2",forestgreen:"#228b22",lawngreen:"#7cfc00",indianred:"#cd5c5c",indigo:"#4b0082",fuchsia:"#ff00ff",brown:"#a52a2a",maroon:"#800000",mediumblue:"#0000cd",lightcoral:"#f08080",darkturquoise:"#00ced1",lightcyan:"#e0ffff",ivory:"#fffff0",lightyellow:"#ffffe0",lightsalmon:"#ffa07a",lightseagreen:"#20b2aa",linen:"#faf0e6",mediumaquamarine:"#66cdaa",lemonchiffon:"#fffacd",lime:"#00ff00",khaki:"#f0e68c",mediumseagreen:"#3cb371",limegreen:"#32cd32",mediumspringgreen:"#00fa9a",lightskyblue:"#87cefa",lightblue:"#add8e6",midnightblue:"#191970",lightpink:"#ffb6c1",mistyrose:"#ffe4e1",moccasin:"#ffe4b5",mintcream:"#f5fffa",lightslategray:"#778899",lightslategrey:"#778899",navajowhite:"#ffdead",navy:"#000080",mediumvioletred:"#c71585",powderblue:"#b0e0e6",palegoldenrod:"#eee8aa",oldlace:"#fdf5e6",paleturquoise:"#afeeee",mediumturquoise:"#48d1cc",mediumorchid:"#ba55d3",rebeccapurple:"#663399",lightsteelblue:"#b0c4de",mediumslateblue:"#7b68ee",thistle:"#d8bfd8",tan:"#d2b48c",orchid:"#da70d6",mediumpurple:"#9370db",purple:"#800080",pink:"#ffc0cb",skyblue:"#87ceeb",springgreen:"#00ff7f",palegreen:"#98fb98",red:"#ff0000",yellow:"#ffff00",slateblue:"#6a5acd",lavenderblush:"#fff0f5",peru:"#cd853f",palevioletred:"#db7093",violet:"#ee82ee",teal:"#008080",slategray:"#708090",slategrey:"#708090",aliceblue:"#f0f8ff",darkseagreen:"#8fbc8f",darkolivegreen:"#556b2f",greenyellow:"#adff2f",seagreen:"#2e8b57",seashell:"#fff5ee",tomato:"#ff6347",silver:"#c0c0c0",sienna:"#a0522d",lavender:"#e6e6fa",lightgreen:"#90ee90",orange:"#ffa500",orangered:"#ff4500",steelblue:"#4682b4",royalblue:"#4169e1",turquoise:"#40e0d0",yellowgreen:"#9acd32",salmon:"#fa8072",saddlebrown:"#8b4513",sandybrown:"#f4a460",rosybrown:"#bc8f8f",darksalmon:"#e9967a",lightgoldenrodyellow:"#fafad2",snow:"#fffafa",lightgrey:"#d3d3d3",lightgray:"#d3d3d3",dimgray:"#696969",dimgrey:"#696969",olivedrab:"#6b8e23",olive:"#808000"},r={};for(var d in a)r[a[d]]=d;var l={};e.prototype.toName=function(f){if(!(this.rgba.a||this.rgba.r||this.rgba.g||this.rgba.b))return"transparent";var d,i,n=r[this.toHex()];if(n)return n;if(null==f?void 0:f.closest){var o=this.toRgb(),t=1/0,b="black";if(!l.length)for(var c in a)l[c]=new e(a[c]).toRgb();for(var g in a){var u=(d=o,i=l[g],Math.pow(d.r-i.r,2)+Math.pow(d.g-i.g,2)+Math.pow(d.b-i.b,2));u d?(u+.05)/(d+.05):(d+.05)/(u+.05),void 0===(a=2)&&(a=0),void 0===i&&(i=Math.pow(10,a)),Math.floor(i*n)/i+0},o.prototype.isReadable=function(o,t){return void 0===o&&(o="#FFF"),void 0===t&&(t={}),this.contrast(o)>=(e=void 0===(i=(r=t).size)?"normal":i,"AAA"===(a=void 0===(n=r.level)?"AA":n)&&"normal"===e?7:"AA"===a&&"large"===e?3:4.5);var r,n,a,i,e}} - -// EXTERNAL MODULE: ./node_modules/traverse/index.js -var traverse = __webpack_require__(3124); -var traverse_default = /*#__PURE__*/__webpack_require__.n(traverse); -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/utils/transform-styles/ast/parse.js -/* eslint-disable @wordpress/no-unused-vars-before-return */ - -// Adapted from https://github.com/reworkcss/css -// because we needed to remove source map support. - -// http://www.w3.org/TR/CSS21/grammar.htm -// https://github.com/visionmedia/css-parse/pull/49#issuecomment-30088027 -const commentre = /\/\*[^*]*\*+([^/*][^*]*\*+)*\//g; -/* harmony default export */ function parse(css, options) { - options = options || {}; - - /** - * Positional. - */ - let lineno = 1; - let column = 1; - - /** - * Update lineno and column based on `str`. - */ - - function updatePosition(str) { - const lines = str.match(/\n/g); - if (lines) { - lineno += lines.length; + // For media selections originated from a file upload. + if (media.media_type && media.media_type !== IMAGE_BACKGROUND_TYPE || !media.media_type && media.type && media.type !== IMAGE_BACKGROUND_TYPE) { + onUploadError((0,external_wp_i18n_namespaceObject.__)('Only images can be used as a background image.')); + return; } - const i = str.lastIndexOf('\n'); - // eslint-disable-next-line no-bitwise - column = ~i ? str.length - i : column + str.length; - } - - /** - * Mark position and patch `node.position`. - */ - - function position() { - const start = { - line: lineno, - column - }; - return function (node) { - node.position = new Position(start); - whitespace(); - return node; + const newStyle = { + ...style, + background: { + ...style?.background, + backgroundImage: { + url: media.url, + id: media.id, + source: 'file', + title: media.title || undefined + } + } }; - } - - /** - * Store position information for a node - */ - - function Position(start) { - this.start = start; - this.end = { - line: lineno, - column + const newAttributes = { + style: utils_cleanEmptyObject(newStyle) }; - this.source = options.source; - } - - /** - * Non-enumerable source string - */ - - Position.prototype.content = css; - - /** - * Error `msg`. - */ - - const errorsList = []; - function error(msg) { - const err = new Error(options.source + ':' + lineno + ':' + column + ': ' + msg); - err.reason = msg; - err.filename = options.source; - err.line = lineno; - err.column = column; - err.source = css; - if (options.silent) { - errorsList.push(err); - } else { - throw err; - } - } - - /** - * Parse stylesheet. - */ - - function stylesheet() { - const rulesList = rules(); + setAttributes(newAttributes); + }; + const onFilesDrop = filesList => { + mediaUpload({ + allowedTypes: ['image'], + filesList, + onFileChange([image]) { + if ((0,external_wp_blob_namespaceObject.isBlobURL)(image?.url)) { + return; + } + onSelectMedia(image); + }, + onError: onUploadError + }); + }; + const resetAllFilter = (0,external_wp_element_namespaceObject.useCallback)(previousValue => { return { - type: 'stylesheet', - stylesheet: { - source: options.source, - rules: rulesList, - parsingErrors: errorsList + ...previousValue, + style: { + ...previousValue.style, + background: undefined } }; - } - - /** - * Opening brace. - */ - - function open() { - return match(/^{\s*/); - } - - /** - * Closing brace. - */ - - function close() { - return match(/^}/); - } - - /** - * Parse ruleset. - */ - - function rules() { - let node; - const accumulator = []; - whitespace(); - comments(accumulator); - while (css.length && css.charAt(0) !== '}' && (node = atrule() || rule())) { - if (node !== false) { - accumulator.push(node); - comments(accumulator); - } + }, []); + const hasValue = hasBackgroundImageValue(style); + return (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalToolsPanelItem, { + className: "single-column", + hasValue: () => hasValue, + label: (0,external_wp_i18n_namespaceObject.__)('Background image'), + onDeselect: () => resetBackgroundImage(style, setAttributes), + isShownByDefault: isShownByDefault, + resetAllFilter: resetAllFilter, + panelId: clientId + }, (0,external_React_.createElement)("div", { + className: "block-editor-hooks__background__inspector-media-replace-container", + ref: replaceContainerRef + }, (0,external_React_.createElement)(media_replace_flow, { + mediaId: id, + mediaURL: url, + allowedTypes: [IMAGE_BACKGROUND_TYPE], + accept: "image/*", + onSelect: onSelectMedia, + name: (0,external_React_.createElement)(InspectorImagePreview, { + label: (0,external_wp_i18n_namespaceObject.__)('Background image'), + filename: title, + url: url + }), + variant: "secondary" + }, hasValue && (0,external_React_.createElement)(external_wp_components_namespaceObject.MenuItem, { + onClick: () => { + const [toggleButton] = external_wp_dom_namespaceObject.focus.tabbable.find(replaceContainerRef.current); + // Focus the toggle button and close the dropdown menu. + // This ensures similar behaviour as to selecting an image, where the dropdown is + // closed and focus is redirected to the dropdown toggle button. + toggleButton?.focus(); + toggleButton?.click(); + resetBackgroundImage(style, setAttributes); } - return accumulator; + }, (0,external_wp_i18n_namespaceObject.__)('Reset '))), (0,external_React_.createElement)(external_wp_components_namespaceObject.DropZone, { + onFilesDrop: onFilesDrop, + label: (0,external_wp_i18n_namespaceObject.__)('Drop to upload') + }))); +} +function backgroundSizeHelpText(value) { + if (value === 'cover' || value === undefined) { + return (0,external_wp_i18n_namespaceObject.__)('Image covers the space evenly.'); } - - /** - * Match `re` and return captures. - */ - - function match(re) { - const m = re.exec(css); - if (!m) { - return; - } - const str = m[0]; - updatePosition(str); - css = css.slice(str.length); - return m; + if (value === 'contain') { + return (0,external_wp_i18n_namespaceObject.__)('Image is contained without distortion.'); } - - /** - * Parse whitespace. - */ - - function whitespace() { - match(/^\s*/); + return (0,external_wp_i18n_namespaceObject.__)('Specify a fixed width.'); +} +const coordsToBackgroundPosition = value => { + if (!value || isNaN(value.x) && isNaN(value.y)) { + return undefined; } - - /** - * Parse comments; - */ - - function comments(accumulator) { - let c; - accumulator = accumulator || []; - // eslint-disable-next-line no-cond-assign - while (c = comment()) { - if (c !== false) { - accumulator.push(c); + const x = isNaN(value.x) ? 0.5 : value.x; + const y = isNaN(value.y) ? 0.5 : value.y; + return `${x * 100}% ${y * 100}%`; +}; +const backgroundPositionToCoords = value => { + if (!value) { + return { + x: undefined, + y: undefined + }; + } + let [x, y] = value.split(' ').map(v => parseFloat(v) / 100); + x = isNaN(x) ? undefined : x; + y = isNaN(y) ? x : y; + return { + x, + y + }; +}; +function BackgroundSizePanelItem({ + clientId, + isShownByDefault, + setAttributes +}) { + const style = (0,external_wp_data_namespaceObject.useSelect)(select => select(store).getBlockAttributes(clientId)?.style, [clientId]); + const sizeValue = style?.background?.backgroundSize; + const repeatValue = style?.background?.backgroundRepeat; + + // An `undefined` value is treated as `cover` by the toggle group control. + // An empty string is treated as `auto` by the toggle group control. This + // allows a user to select "Size" and then enter a custom value, with an + // empty value being treated as `auto`. + const currentValueForToggle = sizeValue !== undefined && sizeValue !== 'cover' && sizeValue !== 'contain' || sizeValue === '' ? 'auto' : sizeValue || 'cover'; + + // If the current value is `cover` and the repeat value is `undefined`, then + // the toggle should be unchecked as the default state. Otherwise, the toggle + // should reflect the current repeat value. + const repeatCheckedValue = repeatValue === 'no-repeat' || currentValueForToggle === 'cover' && repeatValue === undefined ? false : true; + const hasValue = hasBackgroundSizeValue(style); + const resetAllFilter = (0,external_wp_element_namespaceObject.useCallback)(previousValue => { + return { + ...previousValue, + style: { + ...previousValue.style, + background: { + ...previousValue.style?.background, + backgroundRepeat: undefined, + backgroundSize: undefined + } } + }; + }, []); + const updateBackgroundSize = next => { + // When switching to 'contain' toggle the repeat off. + let nextRepeat = repeatValue; + if (next === 'contain') { + nextRepeat = 'no-repeat'; } - return accumulator; - } - - /** - * Parse comment. - */ - - function comment() { - const pos = position(); - if ('/' !== css.charAt(0) || '*' !== css.charAt(1)) { - return; + if ((currentValueForToggle === 'cover' || currentValueForToggle === 'contain') && next === 'auto') { + nextRepeat = undefined; } - let i = 2; - while ('' !== css.charAt(i) && ('*' !== css.charAt(i) || '/' !== css.charAt(i + 1))) { - ++i; - } - i += 2; - if ('' === css.charAt(i - 1)) { - return error('End of comment missing'); - } - const str = css.slice(2, i - 2); - column += 2; - updatePosition(str); - css = css.slice(i); - column += 2; - return pos({ - type: 'comment', - comment: str + setAttributes({ + style: utils_cleanEmptyObject({ + ...style, + background: { + ...style?.background, + backgroundRepeat: nextRepeat, + backgroundSize: next + } + }) }); - } - - /** - * Parse selector. - */ - - function selector() { - const m = match(/^([^{]+)/); - if (!m) { - return; - } - // FIXME: Remove all comments from selectors http://ostermiller.org/findcomment.html - return trim(m[0]).replace(/\/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*\/+/g, '').replace(/"(?:\\"|[^"])*"|'(?:\\'|[^'])*'/g, function (matched) { - return matched.replace(/,/g, '\u200C'); - }).split(/\s*(?![^(]*\)),\s*/).map(function (s) { - return s.replace(/\u200C/g, ','); + }; + const updateBackgroundPosition = next => { + setAttributes({ + style: utils_cleanEmptyObject({ + ...style, + background: { + ...style?.background, + backgroundPosition: coordsToBackgroundPosition(next) + } + }) }); - } - - /** - * Parse declaration. - */ - - function declaration() { - const pos = position(); - - // prop. - let prop = match(/^(\*?[-#\/\*\\\w]+(\[[0-9a-z_-]+\])?)\s*/); - if (!prop) { - return; - } - prop = trim(prop[0]); - - // : - if (!match(/^:\s*/)) { - return error("property missing ':'"); - } - - // val. - const val = match(/^((?:'(?:\\'|.)*?'|"(?:\\"|.)*?"|\([^\)]*?\)|[^};])+)/); - const ret = pos({ - type: 'declaration', - property: prop.replace(commentre, ''), - value: val ? trim(val[0]).replace(commentre, '') : '' + }; + const toggleIsRepeated = () => { + setAttributes({ + style: utils_cleanEmptyObject({ + ...style, + background: { + ...style?.background, + backgroundRepeat: repeatCheckedValue === true ? 'no-repeat' : undefined + } + }) }); - - // ; - match(/^[;\s]*/); - return ret; - } - - /** - * Parse declarations. - */ - - function declarations() { - const decls = []; - if (!open()) { - return error("missing '{'"); - } - comments(decls); - - // declarations. - let decl; - // eslint-disable-next-line no-cond-assign - while (decl = declaration()) { - if (decl !== false) { - decls.push(decl); - comments(decls); - } - } - if (!close()) { - return error("missing '}'"); - } - return decls; + }; + return (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalVStack, { + as: external_wp_components_namespaceObject.__experimentalToolsPanelItem, + spacing: 2, + className: "single-column", + hasValue: () => hasValue, + label: (0,external_wp_i18n_namespaceObject.__)('Size'), + onDeselect: () => resetBackgroundSize(style, setAttributes), + isShownByDefault: isShownByDefault, + resetAllFilter: resetAllFilter, + panelId: clientId + }, (0,external_React_.createElement)(external_wp_components_namespaceObject.FocalPointPicker, { + __next40pxDefaultSize: true, + label: (0,external_wp_i18n_namespaceObject.__)('Position'), + url: style?.background?.backgroundImage?.url, + value: backgroundPositionToCoords(style?.background?.backgroundPosition), + onChange: updateBackgroundPosition + }), (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalToggleGroupControl, { + size: '__unstable-large', + label: (0,external_wp_i18n_namespaceObject.__)('Size'), + value: currentValueForToggle, + onChange: updateBackgroundSize, + isBlock: true, + help: backgroundSizeHelpText(sizeValue) + }, (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalToggleGroupControlOption, { + key: 'cover', + value: 'cover', + label: (0,external_wp_i18n_namespaceObject.__)('Cover') + }), (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalToggleGroupControlOption, { + key: 'contain', + value: 'contain', + label: (0,external_wp_i18n_namespaceObject.__)('Contain') + }), (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalToggleGroupControlOption, { + key: 'fixed', + value: 'auto', + label: (0,external_wp_i18n_namespaceObject.__)('Fixed') + })), sizeValue !== undefined && sizeValue !== 'cover' && sizeValue !== 'contain' ? (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalUnitControl, { + size: '__unstable-large', + onChange: updateBackgroundSize, + value: sizeValue + }) : null, currentValueForToggle !== 'cover' && (0,external_React_.createElement)(external_wp_components_namespaceObject.ToggleControl, { + label: (0,external_wp_i18n_namespaceObject.__)('Repeat'), + checked: repeatCheckedValue, + onChange: toggleIsRepeated + })); +} +function BackgroundImagePanel(props) { + const [backgroundImage, backgroundSize] = use_settings_useSettings('background.backgroundImage', 'background.backgroundSize'); + if (!backgroundImage || !hasBackgroundSupport(props.name, 'backgroundImage')) { + return null; } + const showBackgroundSize = !!(backgroundSize && hasBackgroundSupport(props.name, 'backgroundSize')); + const defaultControls = (0,external_wp_blocks_namespaceObject.getBlockSupport)(props.name, [BACKGROUND_SUPPORT_KEY, '__experimentalDefaultControls']); + return (0,external_React_.createElement)(inspector_controls, { + group: "background" + }, (0,external_React_.createElement)(BackgroundImagePanelItem, { + isShownByDefault: defaultControls?.backgroundImage, + ...props + }), showBackgroundSize && (0,external_React_.createElement)(BackgroundSizePanelItem, { + isShownByDefault: defaultControls?.backgroundSize, + ...props + })); +} - /** - * Parse keyframe. - */ +;// CONCATENATED MODULE: ./node_modules/colord/index.mjs +var r={grad:.9,turn:360,rad:360/(2*Math.PI)},t=function(r){return"string"==typeof r?r.length>0:"number"==typeof r},n=function(r,t,n){return void 0===t&&(t=0),void 0===n&&(n=Math.pow(10,t)),Math.round(n*r)/n+0},e=function(r,t,n){return void 0===t&&(t=0),void 0===n&&(n=1),r>n?n:r>t?r:t},u=function(r){return(r=isFinite(r)?r%360:0)>0?r:r+360},a=function(r){return{r:e(r.r,0,255),g:e(r.g,0,255),b:e(r.b,0,255),a:e(r.a)}},o=function(r){return{r:n(r.r),g:n(r.g),b:n(r.b),a:n(r.a,3)}},i=/^#([0-9a-f]{3,8})$/i,s=function(r){var t=r.toString(16);return t.length<2?"0"+t:t},h=function(r){var t=r.r,n=r.g,e=r.b,u=r.a,a=Math.max(t,n,e),o=a-Math.min(t,n,e),i=o?a===t?(n-e)/o:a===n?2+(e-t)/o:4+(t-n)/o:0;return{h:60*(i<0?i+6:i),s:a?o/a*100:0,v:a/255*100,a:u}},b=function(r){var t=r.h,n=r.s,e=r.v,u=r.a;t=t/360*6,n/=100,e/=100;var a=Math.floor(t),o=e*(1-n),i=e*(1-(t-a)*n),s=e*(1-(1-t+a)*n),h=a%6;return{r:255*[e,i,o,o,s,e][h],g:255*[s,e,e,i,o,o][h],b:255*[o,o,s,e,e,i][h],a:u}},g=function(r){return{h:u(r.h),s:e(r.s,0,100),l:e(r.l,0,100),a:e(r.a)}},d=function(r){return{h:n(r.h),s:n(r.s),l:n(r.l),a:n(r.a,3)}},f=function(r){return b((n=(t=r).s,{h:t.h,s:(n*=((e=t.l)<50?e:100-e)/100)>0?2*n/(e+n)*100:0,v:e+n,a:t.a}));var t,n,e},c=function(r){return{h:(t=h(r)).h,s:(u=(200-(n=t.s))*(e=t.v)/100)>0&&u<200?n*e/100/(u<=100?u:200-u)*100:0,l:u/2,a:t.a};var t,n,e,u},l=/^hsla?\(\s*([+-]?\d*\.?\d+)(deg|rad|grad|turn)?\s*,\s*([+-]?\d*\.?\d+)%\s*,\s*([+-]?\d*\.?\d+)%\s*(?:,\s*([+-]?\d*\.?\d+)(%)?\s*)?\)$/i,p=/^hsla?\(\s*([+-]?\d*\.?\d+)(deg|rad|grad|turn)?\s+([+-]?\d*\.?\d+)%\s+([+-]?\d*\.?\d+)%\s*(?:\/\s*([+-]?\d*\.?\d+)(%)?\s*)?\)$/i,v=/^rgba?\(\s*([+-]?\d*\.?\d+)(%)?\s*,\s*([+-]?\d*\.?\d+)(%)?\s*,\s*([+-]?\d*\.?\d+)(%)?\s*(?:,\s*([+-]?\d*\.?\d+)(%)?\s*)?\)$/i,m=/^rgba?\(\s*([+-]?\d*\.?\d+)(%)?\s+([+-]?\d*\.?\d+)(%)?\s+([+-]?\d*\.?\d+)(%)?\s*(?:\/\s*([+-]?\d*\.?\d+)(%)?\s*)?\)$/i,y={string:[[function(r){var t=i.exec(r);return t?(r=t[1]).length<=4?{r:parseInt(r[0]+r[0],16),g:parseInt(r[1]+r[1],16),b:parseInt(r[2]+r[2],16),a:4===r.length?n(parseInt(r[3]+r[3],16)/255,2):1}:6===r.length||8===r.length?{r:parseInt(r.substr(0,2),16),g:parseInt(r.substr(2,2),16),b:parseInt(r.substr(4,2),16),a:8===r.length?n(parseInt(r.substr(6,2),16)/255,2):1}:null:null},"hex"],[function(r){var t=v.exec(r)||m.exec(r);return t?t[2]!==t[4]||t[4]!==t[6]?null:a({r:Number(t[1])/(t[2]?100/255:1),g:Number(t[3])/(t[4]?100/255:1),b:Number(t[5])/(t[6]?100/255:1),a:void 0===t[7]?1:Number(t[7])/(t[8]?100:1)}):null},"rgb"],[function(t){var n=l.exec(t)||p.exec(t);if(!n)return null;var e,u,a=g({h:(e=n[1],u=n[2],void 0===u&&(u="deg"),Number(e)*(r[u]||1)),s:Number(n[3]),l:Number(n[4]),a:void 0===n[5]?1:Number(n[5])/(n[6]?100:1)});return f(a)},"hsl"]],object:[[function(r){var n=r.r,e=r.g,u=r.b,o=r.a,i=void 0===o?1:o;return t(n)&&t(e)&&t(u)?a({r:Number(n),g:Number(e),b:Number(u),a:Number(i)}):null},"rgb"],[function(r){var n=r.h,e=r.s,u=r.l,a=r.a,o=void 0===a?1:a;if(!t(n)||!t(e)||!t(u))return null;var i=g({h:Number(n),s:Number(e),l:Number(u),a:Number(o)});return f(i)},"hsl"],[function(r){var n=r.h,a=r.s,o=r.v,i=r.a,s=void 0===i?1:i;if(!t(n)||!t(a)||!t(o))return null;var h=function(r){return{h:u(r.h),s:e(r.s,0,100),v:e(r.v,0,100),a:e(r.a)}}({h:Number(n),s:Number(a),v:Number(o),a:Number(s)});return b(h)},"hsv"]]},N=function(r,t){for(var n=0;n =.5},r.prototype.toHex=function(){return r=o(this.rgba),t=r.r,e=r.g,u=r.b,i=(a=r.a)<1?s(n(255*a)):"","#"+s(t)+s(e)+s(u)+i;var r,t,e,u,a,i},r.prototype.toRgb=function(){return o(this.rgba)},r.prototype.toRgbString=function(){return r=o(this.rgba),t=r.r,n=r.g,e=r.b,(u=r.a)<1?"rgba("+t+", "+n+", "+e+", "+u+")":"rgb("+t+", "+n+", "+e+")";var r,t,n,e,u},r.prototype.toHsl=function(){return d(c(this.rgba))},r.prototype.toHslString=function(){return r=d(c(this.rgba)),t=r.h,n=r.s,e=r.l,(u=r.a)<1?"hsla("+t+", "+n+"%, "+e+"%, "+u+")":"hsl("+t+", "+n+"%, "+e+"%)";var r,t,n,e,u},r.prototype.toHsv=function(){return r=h(this.rgba),{h:n(r.h),s:n(r.s),v:n(r.v),a:n(r.a,3)};var r},r.prototype.invert=function(){return w({r:255-(r=this.rgba).r,g:255-r.g,b:255-r.b,a:r.a});var r},r.prototype.saturate=function(r){return void 0===r&&(r=.1),w(M(this.rgba,r))},r.prototype.desaturate=function(r){return void 0===r&&(r=.1),w(M(this.rgba,-r))},r.prototype.grayscale=function(){return w(M(this.rgba,-1))},r.prototype.lighten=function(r){return void 0===r&&(r=.1),w($(this.rgba,r))},r.prototype.darken=function(r){return void 0===r&&(r=.1),w($(this.rgba,-r))},r.prototype.rotate=function(r){return void 0===r&&(r=15),this.hue(this.hue()+r)},r.prototype.alpha=function(r){return"number"==typeof r?w({r:(t=this.rgba).r,g:t.g,b:t.b,a:r}):n(this.rgba.a,3);var t},r.prototype.hue=function(r){var t=c(this.rgba);return"number"==typeof r?w({h:r,s:t.s,l:t.l,a:t.a}):n(t.h)},r.prototype.isEqual=function(r){return this.toHex()===w(r).toHex()},r}(),w=function(r){return r instanceof colord_j?r:new colord_j(r)},S=[],k=function(r){r.forEach(function(r){S.indexOf(r)<0&&(r(colord_j,y),S.push(r))})},E=function(){return new colord_j({r:255*Math.random(),g:255*Math.random(),b:255*Math.random()})}; - function keyframe() { - let m; - const vals = []; - const pos = position(); +;// CONCATENATED MODULE: ./node_modules/colord/plugins/names.mjs +/* harmony default export */ function names(e,f){var a={white:"#ffffff",bisque:"#ffe4c4",blue:"#0000ff",cadetblue:"#5f9ea0",chartreuse:"#7fff00",chocolate:"#d2691e",coral:"#ff7f50",antiquewhite:"#faebd7",aqua:"#00ffff",azure:"#f0ffff",whitesmoke:"#f5f5f5",papayawhip:"#ffefd5",plum:"#dda0dd",blanchedalmond:"#ffebcd",black:"#000000",gold:"#ffd700",goldenrod:"#daa520",gainsboro:"#dcdcdc",cornsilk:"#fff8dc",cornflowerblue:"#6495ed",burlywood:"#deb887",aquamarine:"#7fffd4",beige:"#f5f5dc",crimson:"#dc143c",cyan:"#00ffff",darkblue:"#00008b",darkcyan:"#008b8b",darkgoldenrod:"#b8860b",darkkhaki:"#bdb76b",darkgray:"#a9a9a9",darkgreen:"#006400",darkgrey:"#a9a9a9",peachpuff:"#ffdab9",darkmagenta:"#8b008b",darkred:"#8b0000",darkorchid:"#9932cc",darkorange:"#ff8c00",darkslateblue:"#483d8b",gray:"#808080",darkslategray:"#2f4f4f",darkslategrey:"#2f4f4f",deeppink:"#ff1493",deepskyblue:"#00bfff",wheat:"#f5deb3",firebrick:"#b22222",floralwhite:"#fffaf0",ghostwhite:"#f8f8ff",darkviolet:"#9400d3",magenta:"#ff00ff",green:"#008000",dodgerblue:"#1e90ff",grey:"#808080",honeydew:"#f0fff0",hotpink:"#ff69b4",blueviolet:"#8a2be2",forestgreen:"#228b22",lawngreen:"#7cfc00",indianred:"#cd5c5c",indigo:"#4b0082",fuchsia:"#ff00ff",brown:"#a52a2a",maroon:"#800000",mediumblue:"#0000cd",lightcoral:"#f08080",darkturquoise:"#00ced1",lightcyan:"#e0ffff",ivory:"#fffff0",lightyellow:"#ffffe0",lightsalmon:"#ffa07a",lightseagreen:"#20b2aa",linen:"#faf0e6",mediumaquamarine:"#66cdaa",lemonchiffon:"#fffacd",lime:"#00ff00",khaki:"#f0e68c",mediumseagreen:"#3cb371",limegreen:"#32cd32",mediumspringgreen:"#00fa9a",lightskyblue:"#87cefa",lightblue:"#add8e6",midnightblue:"#191970",lightpink:"#ffb6c1",mistyrose:"#ffe4e1",moccasin:"#ffe4b5",mintcream:"#f5fffa",lightslategray:"#778899",lightslategrey:"#778899",navajowhite:"#ffdead",navy:"#000080",mediumvioletred:"#c71585",powderblue:"#b0e0e6",palegoldenrod:"#eee8aa",oldlace:"#fdf5e6",paleturquoise:"#afeeee",mediumturquoise:"#48d1cc",mediumorchid:"#ba55d3",rebeccapurple:"#663399",lightsteelblue:"#b0c4de",mediumslateblue:"#7b68ee",thistle:"#d8bfd8",tan:"#d2b48c",orchid:"#da70d6",mediumpurple:"#9370db",purple:"#800080",pink:"#ffc0cb",skyblue:"#87ceeb",springgreen:"#00ff7f",palegreen:"#98fb98",red:"#ff0000",yellow:"#ffff00",slateblue:"#6a5acd",lavenderblush:"#fff0f5",peru:"#cd853f",palevioletred:"#db7093",violet:"#ee82ee",teal:"#008080",slategray:"#708090",slategrey:"#708090",aliceblue:"#f0f8ff",darkseagreen:"#8fbc8f",darkolivegreen:"#556b2f",greenyellow:"#adff2f",seagreen:"#2e8b57",seashell:"#fff5ee",tomato:"#ff6347",silver:"#c0c0c0",sienna:"#a0522d",lavender:"#e6e6fa",lightgreen:"#90ee90",orange:"#ffa500",orangered:"#ff4500",steelblue:"#4682b4",royalblue:"#4169e1",turquoise:"#40e0d0",yellowgreen:"#9acd32",salmon:"#fa8072",saddlebrown:"#8b4513",sandybrown:"#f4a460",rosybrown:"#bc8f8f",darksalmon:"#e9967a",lightgoldenrodyellow:"#fafad2",snow:"#fffafa",lightgrey:"#d3d3d3",lightgray:"#d3d3d3",dimgray:"#696969",dimgrey:"#696969",olivedrab:"#6b8e23",olive:"#808000"},r={};for(var d in a)r[a[d]]=d;var l={};e.prototype.toName=function(f){if(!(this.rgba.a||this.rgba.r||this.rgba.g||this.rgba.b))return"transparent";var d,i,n=r[this.toHex()];if(n)return n;if(null==f?void 0:f.closest){var o=this.toRgb(),t=1/0,b="black";if(!l.length)for(var c in a)l[c]=new e(a[c]).toRgb();for(var g in a){var u=(d=o,i=l[g],Math.pow(d.r-i.r,2)+Math.pow(d.g-i.g,2)+Math.pow(d.b-i.b,2));u d?(u+.05)/(d+.05):(d+.05)/(u+.05),void 0===(a=2)&&(a=0),void 0===i&&(i=Math.pow(10,a)),Math.floor(i*n)/i+0},o.prototype.isReadable=function(o,t){return void 0===o&&(o="#FFF"),void 0===t&&(t={}),this.contrast(o)>=(e=void 0===(i=(r=t).size)?"normal":i,"AAA"===(a=void 0===(n=r.level)?"AA":n)&&"normal"===e?7:"AA"===a&&"large"===e?3:4.5);var r,n,a,i,e}} - /** - * Parse keyframes. - */ +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/colors/utils.js +/** + * External dependencies + */ - function atkeyframes() { - const pos = position(); - let m = match(/^@([-\w]+)?keyframes\s*/); - if (!m) { - return; - } - const vendor = m[1]; - // identifier - m = match(/^([-\w]+)\s*/); - if (!m) { - return error('@keyframes missing name'); - } - const name = m[1]; - if (!open()) { - return error("@keyframes missing '{'"); - } - let frame; - let frames = comments(); - // eslint-disable-next-line no-cond-assign - while (frame = keyframe()) { - frames.push(frame); - frames = frames.concat(comments()); - } - if (!close()) { - return error("@keyframes missing '}'"); - } - return pos({ - type: 'keyframes', - name, - vendor, - keyframes: frames - }); - } - /** - * Parse supports. - */ - function atsupports() { - const pos = position(); - const m = match(/^@supports *([^{]+)/); - if (!m) { - return; - } - const supports = trim(m[1]); - if (!open()) { - return error("@supports missing '{'"); - } - const style = comments().concat(rules()); - if (!close()) { - return error("@supports missing '}'"); - } - return pos({ - type: 'supports', - supports, - rules: style - }); - } +/** + * WordPress dependencies + */ - /** - * Parse host. - */ - function athost() { - const pos = position(); - const m = match(/^@host\s*/); - if (!m) { - return; - } - if (!open()) { - return error("@host missing '{'"); - } - const style = comments().concat(rules()); - if (!close()) { - return error("@host missing '}'"); - } - return pos({ - type: 'host', - rules: style - }); - } +/** + * Internal dependencies + */ - /** - * Parse media. - */ +k([names, a11y]); - function atmedia() { - const pos = position(); - const m = match(/^@media *([^{]+)/); - if (!m) { - return; - } - const media = trim(m[1]); - if (!open()) { - return error("@media missing '{'"); - } - const style = comments().concat(rules()); - if (!close()) { - return error("@media missing '}'"); +/** + * Provided an array of color objects as set by the theme or by the editor defaults, + * and the values of the defined color or custom color returns a color object describing the color. + * + * @param {Array} colors Array of color objects as set by the theme or by the editor defaults. + * @param {?string} definedColor A string containing the color slug. + * @param {?string} customColor A string containing the customColor value. + * + * @return {?Object} If definedColor is passed and the name is found in colors, + * the color object exactly as set by the theme or editor defaults is returned. + * Otherwise, an object that just sets the color is defined. + */ +const getColorObjectByAttributeValues = (colors, definedColor, customColor) => { + if (definedColor) { + const colorObj = colors?.find(color => color.slug === definedColor); + if (colorObj) { + return colorObj; } - return pos({ - type: 'media', - media, - rules: style - }); } + return { + color: customColor + }; +}; - /** - * Parse container. - */ +/** + * Provided an array of color objects as set by the theme or by the editor defaults, and a color value returns the color object matching that value or undefined. + * + * @param {Array} colors Array of color objects as set by the theme or by the editor defaults. + * @param {?string} colorValue A string containing the color value. + * + * @return {?Object} Color object included in the colors array whose color property equals colorValue. + * Returns undefined if no color object matches this requirement. + */ +const getColorObjectByColorValue = (colors, colorValue) => { + return colors?.find(color => color.color === colorValue); +}; - function atcontainer() { - const pos = position(); - const m = match(/^@container *([^{]+)/); - if (!m) { - return; - } - const container = trim(m[1]); - if (!open()) { - return error("@container missing '{'"); - } - const style = comments().concat(rules()); - if (!close()) { - return error("@container missing '}'"); - } - return pos({ - type: 'container', - container, - rules: style - }); +/** + * Returns a class based on the context a color is being used and its slug. + * + * @param {string} colorContextName Context/place where color is being used e.g: background, text etc... + * @param {string} colorSlug Slug of the color. + * + * @return {?string} String with the class corresponding to the color in the provided context. + * Returns undefined if either colorContextName or colorSlug are not provided. + */ +function getColorClassName(colorContextName, colorSlug) { + if (!colorContextName || !colorSlug) { + return undefined; } + const { + kebabCase + } = unlock(external_wp_components_namespaceObject.privateApis); + return `has-${kebabCase(colorSlug)}-${colorContextName}`; +} - /** - * Parse custom-media. - */ +/** + * Given an array of color objects and a color value returns the color value of the most readable color in the array. + * + * @param {Array} colors Array of color objects as set by the theme or by the editor defaults. + * @param {?string} colorValue A string containing the color value. + * + * @return {string} String with the color value of the most readable color. + */ +function getMostReadableColor(colors, colorValue) { + const colordColor = w(colorValue); + const getColorContrast = ({ + color + }) => colordColor.contrast(color); + const maxContrast = Math.max(...colors.map(getColorContrast)); + return colors.find(color => getColorContrast(color) === maxContrast).color; +} - function atcustommedia() { - const pos = position(); - const m = match(/^@custom-media\s+(--[^\s]+)\s*([^{;]+);/); - if (!m) { - return; - } - return pos({ - type: 'custom-media', - name: trim(m[1]), - media: trim(m[2]) - }); - } +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/colors-gradients/use-multiple-origin-colors-and-gradients.js +/** + * WordPress dependencies + */ - /** - * Parse paged media. - */ - function atpage() { - const pos = position(); - const m = match(/^@page */); - if (!m) { - return; - } - const sel = selector() || []; - if (!open()) { - return error("@page missing '{'"); - } - let decls = comments(); - // declarations. - let decl; - // eslint-disable-next-line no-cond-assign - while (decl = declaration()) { - decls.push(decl); - decls = decls.concat(comments()); - } - if (!close()) { - return error("@page missing '}'"); - } - return pos({ - type: 'page', - selectors: sel, - declarations: decls - }); - } +/** + * Internal dependencies + */ - /** - * Parse document. - */ - function atdocument() { - const pos = position(); - const m = match(/^@([-\w]+)?document *([^{]+)/); - if (!m) { - return; - } - const vendor = trim(m[1]); - const doc = trim(m[2]); - if (!open()) { - return error("@document missing '{'"); +/** + * Retrieves color and gradient related settings. + * + * The arrays for colors and gradients are made up of color palettes from each + * origin i.e. "Core", "Theme", and "User". + * + * @return {Object} Color and gradient related settings. + */ +function useMultipleOriginColorsAndGradients() { + const [enableCustomColors, customColors, themeColors, defaultColors, shouldDisplayDefaultColors, enableCustomGradients, customGradients, themeGradients, defaultGradients, shouldDisplayDefaultGradients] = use_settings_useSettings('color.custom', 'color.palette.custom', 'color.palette.theme', 'color.palette.default', 'color.defaultPalette', 'color.customGradient', 'color.gradients.custom', 'color.gradients.theme', 'color.gradients.default', 'color.defaultGradients'); + const colorGradientSettings = { + disableCustomColors: !enableCustomColors, + disableCustomGradients: !enableCustomGradients + }; + colorGradientSettings.colors = (0,external_wp_element_namespaceObject.useMemo)(() => { + const result = []; + if (themeColors && themeColors.length) { + result.push({ + name: (0,external_wp_i18n_namespaceObject._x)('Theme', 'Indicates this palette comes from the theme.'), + colors: themeColors + }); } - const style = comments().concat(rules()); - if (!close()) { - return error("@document missing '}'"); + if (shouldDisplayDefaultColors && defaultColors && defaultColors.length) { + result.push({ + name: (0,external_wp_i18n_namespaceObject._x)('Default', 'Indicates this palette comes from WordPress.'), + colors: defaultColors + }); } - return pos({ - type: 'document', - document: doc, - vendor, - rules: style - }); - } - - /** - * Parse font-face. - */ - - function atfontface() { - const pos = position(); - const m = match(/^@font-face\s*/); - if (!m) { - return; + if (customColors && customColors.length) { + result.push({ + name: (0,external_wp_i18n_namespaceObject._x)('Custom', 'Indicates this palette comes from the theme.'), + colors: customColors + }); } - if (!open()) { - return error("@font-face missing '{'"); + return result; + }, [customColors, themeColors, defaultColors, shouldDisplayDefaultColors]); + colorGradientSettings.gradients = (0,external_wp_element_namespaceObject.useMemo)(() => { + const result = []; + if (themeGradients && themeGradients.length) { + result.push({ + name: (0,external_wp_i18n_namespaceObject._x)('Theme', 'Indicates this palette comes from the theme.'), + gradients: themeGradients + }); } - let decls = comments(); - - // declarations. - let decl; - // eslint-disable-next-line no-cond-assign - while (decl = declaration()) { - decls.push(decl); - decls = decls.concat(comments()); + if (shouldDisplayDefaultGradients && defaultGradients && defaultGradients.length) { + result.push({ + name: (0,external_wp_i18n_namespaceObject._x)('Default', 'Indicates this palette comes from WordPress.'), + gradients: defaultGradients + }); } - if (!close()) { - return error("@font-face missing '}'"); + if (customGradients && customGradients.length) { + result.push({ + name: (0,external_wp_i18n_namespaceObject._x)('Custom', 'Indicates this palette is created by the user.'), + gradients: customGradients + }); } - return pos({ - type: 'font-face', - declarations: decls - }); - } - - /** - * Parse import - */ - - const atimport = _compileAtrule('import'); - - /** - * Parse charset - */ - - const atcharset = _compileAtrule('charset'); - - /** - * Parse namespace - */ - - const atnamespace = _compileAtrule('namespace'); - - /** - * Parse non-block at-rules - */ - - function _compileAtrule(name) { - const re = new RegExp('^@' + name + '\\s*([^;]+);'); - return function () { - const pos = position(); - const m = match(re); - if (!m) { - return; - } - const ret = { - type: name - }; - ret[name] = m[1].trim(); - return pos(ret); - }; - } - - /** - * Parse at rule. - */ + return result; + }, [customGradients, themeGradients, defaultGradients, shouldDisplayDefaultGradients]); + colorGradientSettings.hasColorsOrGradients = !!colorGradientSettings.colors.length || !!colorGradientSettings.gradients.length; + return colorGradientSettings; +} - function atrule() { - if (css[0] !== '@') { - return; - } - return atkeyframes() || atmedia() || atcontainer() || atcustommedia() || atsupports() || atimport() || atcharset() || atnamespace() || atdocument() || atpage() || athost() || atfontface(); - } +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/border-radius-control/utils.js +/** + * WordPress dependencies + */ - /** - * Parse rule. - */ - function rule() { - const pos = position(); - const sel = selector(); - if (!sel) { - return error('selector missing'); - } - comments(); - return pos({ - type: 'rule', - selectors: sel, - declarations: declarations() - }); - } - return addParent(stylesheet()); +/** + * Gets the (non-undefined) item with the highest occurrence within an array + * Based in part on: https://stackoverflow.com/a/20762713 + * + * Undefined values are always sorted to the end by `sort`, so this function + * returns the first element, to always prioritize real values over undefined + * values. + * + * See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#description + * + * @param {Array } inputArray Array of items to check. + * @return {any} The item with the most occurrences. + */ +function utils_mode(inputArray) { + const arr = [...inputArray]; + return arr.sort((a, b) => inputArray.filter(v => v === b).length - inputArray.filter(v => v === a).length).shift(); } /** - * Trim `str`. + * Returns the most common CSS unit from the current CSS unit selections. + * + * - If a single flat border radius is set, its unit will be used + * - If individual corner selections, the most common of those will be used + * - Failing any unit selections a default of 'px' is returned. + * + * @param {Object} selectedUnits Unit selections for flat radius & each corner. + * @return {string} Most common CSS unit from current selections. Default: `px`. */ - -function trim(str) { - return str ? str.replace(/^\s+|\s+$/g, '') : ''; +function getAllUnit(selectedUnits = {}) { + const { + flat, + ...cornerUnits + } = selectedUnits; + return flat || utils_mode(Object.values(cornerUnits).filter(Boolean)) || 'px'; } /** - * Adds non-enumerable parent node reference to each node. + * Gets the 'all' input value and unit from values data. + * + * @param {Object|string} values Radius values. + * @return {string} A value + unit for the 'all' input. */ - -function addParent(obj, parent) { - const isNode = obj && typeof obj.type === 'string'; - const childParent = isNode ? obj : parent; - for (const k in obj) { - const value = obj[k]; - if (Array.isArray(value)) { - value.forEach(function (v) { - addParent(v, childParent); - }); - } else if (value && typeof value === 'object') { - addParent(value, childParent); - } - } - if (isNode) { - Object.defineProperty(obj, 'parent', { - configurable: true, - writable: true, - enumerable: false, - value: parent || null - }); +function getAllValue(values = {}) { + /** + * Border radius support was originally a single pixel value. + * + * To maintain backwards compatibility treat this case as the all value. + */ + if (typeof values === 'string') { + return values; } - return obj; + const parsedQuantitiesAndUnits = Object.values(values).map(value => (0,external_wp_components_namespaceObject.__experimentalParseQuantityAndUnitFromRawValue)(value)); + const allValues = parsedQuantitiesAndUnits.map(value => { + var _value$; + return (_value$ = value[0]) !== null && _value$ !== void 0 ? _value$ : ''; + }); + const allUnits = parsedQuantitiesAndUnits.map(value => value[1]); + const value = allValues.every(v => v === allValues[0]) ? allValues[0] : ''; + const unit = utils_mode(allUnits); + const allValue = value === 0 || value ? `${value}${unit}` : undefined; + return allValue; } -/* eslint-enable @wordpress/no-unused-vars-before-return */ - -// EXTERNAL MODULE: ./node_modules/inherits/inherits_browser.js -var inherits_browser = __webpack_require__(8575); -var inherits_browser_default = /*#__PURE__*/__webpack_require__.n(inherits_browser); -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/utils/transform-styles/ast/stringify/compiler.js -// Adapted from https://github.com/reworkcss/css -// because we needed to remove source map support. - /** - * Expose `Compiler`. + * Checks to determine if values are mixed. + * + * @param {Object} values Radius values. + * @return {boolean} Whether values are mixed. */ - -/* harmony default export */ var compiler = (Compiler); +function hasMixedValues(values = {}) { + const allValue = getAllValue(values); + const isMixed = typeof values === 'string' ? false : isNaN(parseFloat(allValue)); + return isMixed; +} /** - * Initialize a compiler. + * Checks to determine if values are defined. + * + * @param {Object} values Radius values. + * @return {boolean} Whether values are mixed. */ +function hasDefinedValues(values) { + if (!values) { + return false; + } -function Compiler(opts) { - this.options = opts || {}; -} + // A string value represents a shorthand value. + if (typeof values === 'string') { + return true; + } -/** - * Emit `str` - */ + // An object represents longhand border radius values, if any are set + // flag values as being defined. + const filteredValues = Object.values(values).filter(value => { + return !!value || value === 0; + }); + return !!filteredValues.length; +} -Compiler.prototype.emit = function (str) { - return str; -}; +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/border-radius-control/all-input-control.js /** - * Visit `node`. + * WordPress dependencies */ -Compiler.prototype.visit = function (node) { - return this[node.type](node); -}; + /** - * Map visit over array of `nodes`, optionally using a `delim` + * Internal dependencies */ -Compiler.prototype.mapVisit = function (nodes, delim) { - let buf = ''; - delim = delim || ''; - for (let i = 0, length = nodes.length; i < length; i++) { - buf += this.visit(nodes[i]); - if (delim && i < length - 1) { - buf += this.emit(delim); - } +function AllInputControl({ + onChange, + selectedUnits, + setSelectedUnits, + values, + ...props +}) { + let allValue = getAllValue(values); + if (allValue === undefined) { + // If we don't have any value set the unit to any current selection + // or the most common unit from the individual radii values. + allValue = getAllUnit(selectedUnits); } - return buf; -}; + const hasValues = hasDefinedValues(values); + const isMixed = hasValues && hasMixedValues(values); + const allPlaceholder = isMixed ? (0,external_wp_i18n_namespaceObject.__)('Mixed') : null; -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/utils/transform-styles/ast/stringify/compress.js -// Adapted from https://github.com/reworkcss/css -// because we needed to remove source map support. + // Filter out CSS-unit-only values to prevent invalid styles. + const handleOnChange = next => { + const isNumeric = !isNaN(parseFloat(next)); + const nextValue = isNumeric ? next : undefined; + onChange(nextValue); + }; -/** - * External dependencies - */ + // Store current unit selection for use as fallback for individual + // radii controls. + const handleOnUnitChange = unit => { + setSelectedUnits({ + topLeft: unit, + topRight: unit, + bottomLeft: unit, + bottomRight: unit + }); + }; + return (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalUnitControl, { + ...props, + "aria-label": (0,external_wp_i18n_namespaceObject.__)('Border radius'), + disableUnits: isMixed, + isOnly: true, + value: allValue, + onChange: handleOnChange, + onUnitChange: handleOnUnitChange, + placeholder: allPlaceholder, + size: '__unstable-large' + }); +} +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/border-radius-control/input-controls.js /** - * Internal dependencies + * WordPress dependencies */ -/** - * Expose compiler. - */ +const CORNERS = { + topLeft: (0,external_wp_i18n_namespaceObject.__)('Top left'), + topRight: (0,external_wp_i18n_namespaceObject.__)('Top right'), + bottomLeft: (0,external_wp_i18n_namespaceObject.__)('Bottom left'), + bottomRight: (0,external_wp_i18n_namespaceObject.__)('Bottom right') +}; +function BoxInputControls({ + onChange, + selectedUnits, + setSelectedUnits, + values: valuesProp, + ...props +}) { + const createHandleOnChange = corner => next => { + if (!onChange) { + return; + } -/* harmony default export */ var compress = (compress_Compiler); + // Filter out CSS-unit-only values to prevent invalid styles. + const isNumeric = !isNaN(parseFloat(next)); + const nextValue = isNumeric ? next : undefined; + onChange({ + ...values, + [corner]: nextValue + }); + }; + const createHandleOnUnitChange = side => next => { + const newUnits = { + ...selectedUnits + }; + newUnits[side] = next; + setSelectedUnits(newUnits); + }; -/** - * Initialize a new `Compiler`. - */ + // For shorthand style & backwards compatibility, handle flat string value. + const values = typeof valuesProp !== 'string' ? valuesProp : { + topLeft: valuesProp, + topRight: valuesProp, + bottomLeft: valuesProp, + bottomRight: valuesProp + }; -function compress_Compiler(options) { - compiler.call(this, options); + // Controls are wrapped in tooltips as visible labels aren't desired here. + // Tooltip rendering also requires the UnitControl to be wrapped. See: + // https://github.com/WordPress/gutenberg/pull/24966#issuecomment-685875026 + return (0,external_React_.createElement)("div", { + className: "components-border-radius-control__input-controls-wrapper" + }, Object.entries(CORNERS).map(([corner, label]) => { + const [parsedQuantity, parsedUnit] = (0,external_wp_components_namespaceObject.__experimentalParseQuantityAndUnitFromRawValue)(values[corner]); + const computedUnit = values[corner] ? parsedUnit : selectedUnits[corner] || selectedUnits.flat; + return (0,external_React_.createElement)(external_wp_components_namespaceObject.Tooltip, { + text: label, + placement: "top", + key: corner + }, (0,external_React_.createElement)("div", { + className: "components-border-radius-control__tooltip-wrapper" + }, (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalUnitControl, { + ...props, + "aria-label": label, + value: [parsedQuantity, computedUnit].join(''), + onChange: createHandleOnChange(corner), + onUnitChange: createHandleOnUnitChange(corner), + size: '__unstable-large' + }))); + })); } -/** - * Inherit from `Base.prototype`. - */ - -inherits_browser_default()(compress_Compiler, compiler); +;// CONCATENATED MODULE: ./node_modules/@wordpress/icons/build-module/library/link.js /** - * Compile `node`. + * WordPress dependencies */ -compress_Compiler.prototype.compile = function (node) { - return node.stylesheet.rules.map(this.visit, this).join(''); -}; - -/** - * Visit comment node. - */ +const link_link = (0,external_React_.createElement)(external_wp_primitives_namespaceObject.SVG, { + xmlns: "http://www.w3.org/2000/svg", + viewBox: "0 0 24 24" +}, (0,external_React_.createElement)(external_wp_primitives_namespaceObject.Path, { + d: "M10 17.389H8.444A5.194 5.194 0 1 1 8.444 7H10v1.5H8.444a3.694 3.694 0 0 0 0 7.389H10v1.5ZM14 7h1.556a5.194 5.194 0 0 1 0 10.39H14v-1.5h1.556a3.694 3.694 0 0 0 0-7.39H14V7Zm-4.5 6h5v-1.5h-5V13Z" +})); +/* harmony default export */ const library_link = (link_link); -compress_Compiler.prototype.comment = function (node) { - return this.emit('', node.position); -}; +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/border-radius-control/linked-button.js /** - * Visit import node. + * WordPress dependencies */ -compress_Compiler.prototype.import = function (node) { - return this.emit('@import ' + node.import + ';', node.position); -}; - -/** - * Visit media node. - */ -compress_Compiler.prototype.media = function (node) { - return this.emit('@media ' + node.media, node.position) + this.emit('{') + this.mapVisit(node.rules) + this.emit('}'); -}; -/** - * Visit container node. - */ +function LinkedButton({ + isLinked, + ...props +}) { + const label = isLinked ? (0,external_wp_i18n_namespaceObject.__)('Unlink radii') : (0,external_wp_i18n_namespaceObject.__)('Link radii'); + return (0,external_React_.createElement)(external_wp_components_namespaceObject.Tooltip, { + text: label + }, (0,external_React_.createElement)(external_wp_components_namespaceObject.Button, { + ...props, + className: "component-border-radius-control__linked-button", + size: "small", + icon: isLinked ? library_link : link_off, + iconSize: 24, + "aria-label": label + })); +} -compress_Compiler.prototype.container = function (node) { - return this.emit('@container ' + node.container, node.position) + this.emit('{') + this.mapVisit(node.rules) + this.emit('}'); -}; +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/border-radius-control/index.js /** - * Visit document node. + * WordPress dependencies */ -compress_Compiler.prototype.document = function (node) { - const doc = '@' + (node.vendor || '') + 'document ' + node.document; - return this.emit(doc, node.position) + this.emit('{') + this.mapVisit(node.rules) + this.emit('}'); -}; -/** - * Visit charset node. - */ -compress_Compiler.prototype.charset = function (node) { - return this.emit('@charset ' + node.charset + ';', node.position); -}; /** - * Visit namespace node. + * Internal dependencies */ -compress_Compiler.prototype.namespace = function (node) { - return this.emit('@namespace ' + node.namespace + ';', node.position); -}; -/** - * Visit supports node. - */ -compress_Compiler.prototype.supports = function (node) { - return this.emit('@supports ' + node.supports, node.position) + this.emit('{') + this.mapVisit(node.rules) + this.emit('}'); -}; -/** - * Visit keyframes node. - */ -compress_Compiler.prototype.keyframes = function (node) { - return this.emit('@' + (node.vendor || '') + 'keyframes ' + node.name, node.position) + this.emit('{') + this.mapVisit(node.keyframes) + this.emit('}'); +const border_radius_control_DEFAULT_VALUES = { + topLeft: undefined, + topRight: undefined, + bottomLeft: undefined, + bottomRight: undefined }; - -/** - * Visit keyframe node. - */ - -compress_Compiler.prototype.keyframe = function (node) { - const decls = node.declarations; - return this.emit(node.values.join(','), node.position) + this.emit('{') + this.mapVisit(decls) + this.emit('}'); +const MIN_BORDER_RADIUS_VALUE = 0; +const MAX_BORDER_RADIUS_VALUES = { + px: 100, + em: 20, + rem: 20 }; /** - * Visit page node. + * Control to display border radius options. + * + * @param {Object} props Component props. + * @param {Function} props.onChange Callback to handle onChange. + * @param {Object} props.values Border radius values. + * + * @return {Element} Custom border radius control. */ +function BorderRadiusControl({ + onChange, + values +}) { + const [isLinked, setIsLinked] = (0,external_wp_element_namespaceObject.useState)(!hasDefinedValues(values) || !hasMixedValues(values)); -compress_Compiler.prototype.page = function (node) { - const sel = node.selectors.length ? node.selectors.join(', ') : ''; - return this.emit('@page ' + sel, node.position) + this.emit('{') + this.mapVisit(node.declarations) + this.emit('}'); -}; - -/** - * Visit font-face node. - */ + // Tracking selected units via internal state allows filtering of CSS unit + // only values from being saved while maintaining preexisting unit selection + // behaviour. Filtering CSS unit only values prevents invalid style values. + const [selectedUnits, setSelectedUnits] = (0,external_wp_element_namespaceObject.useState)({ + flat: typeof values === 'string' ? (0,external_wp_components_namespaceObject.__experimentalParseQuantityAndUnitFromRawValue)(values)[1] : undefined, + topLeft: (0,external_wp_components_namespaceObject.__experimentalParseQuantityAndUnitFromRawValue)(values?.topLeft)[1], + topRight: (0,external_wp_components_namespaceObject.__experimentalParseQuantityAndUnitFromRawValue)(values?.topRight)[1], + bottomLeft: (0,external_wp_components_namespaceObject.__experimentalParseQuantityAndUnitFromRawValue)(values?.bottomLeft)[1], + bottomRight: (0,external_wp_components_namespaceObject.__experimentalParseQuantityAndUnitFromRawValue)(values?.bottomRight)[1] + }); + const [availableUnits] = use_settings_useSettings('spacing.units'); + const units = (0,external_wp_components_namespaceObject.__experimentalUseCustomUnits)({ + availableUnits: availableUnits || ['px', 'em', 'rem'] + }); + const unit = getAllUnit(selectedUnits); + const unitConfig = units && units.find(item => item.value === unit); + const step = unitConfig?.step || 1; + const [allValue] = (0,external_wp_components_namespaceObject.__experimentalParseQuantityAndUnitFromRawValue)(getAllValue(values)); + const toggleLinked = () => setIsLinked(!isLinked); + const handleSliderChange = next => { + onChange(next !== undefined ? `${next}${unit}` : undefined); + }; + return (0,external_React_.createElement)("fieldset", { + className: "components-border-radius-control" + }, (0,external_React_.createElement)(external_wp_components_namespaceObject.BaseControl.VisualLabel, { + as: "legend" + }, (0,external_wp_i18n_namespaceObject.__)('Radius')), (0,external_React_.createElement)("div", { + className: "components-border-radius-control__wrapper" + }, isLinked ? (0,external_React_.createElement)(external_React_.Fragment, null, (0,external_React_.createElement)(AllInputControl, { + className: "components-border-radius-control__unit-control", + values: values, + min: MIN_BORDER_RADIUS_VALUE, + onChange: onChange, + selectedUnits: selectedUnits, + setSelectedUnits: setSelectedUnits, + units: units + }), (0,external_React_.createElement)(external_wp_components_namespaceObject.RangeControl, { + label: (0,external_wp_i18n_namespaceObject.__)('Border radius'), + hideLabelFromVision: true, + className: "components-border-radius-control__range-control", + value: allValue !== null && allValue !== void 0 ? allValue : '', + min: MIN_BORDER_RADIUS_VALUE, + max: MAX_BORDER_RADIUS_VALUES[unit], + initialPosition: 0, + withInputField: false, + onChange: handleSliderChange, + step: step, + __nextHasNoMarginBottom: true + })) : (0,external_React_.createElement)(BoxInputControls, { + min: MIN_BORDER_RADIUS_VALUE, + onChange: onChange, + selectedUnits: selectedUnits, + setSelectedUnits: setSelectedUnits, + values: values || border_radius_control_DEFAULT_VALUES, + units: units + }), (0,external_React_.createElement)(LinkedButton, { + onClick: toggleLinked, + isLinked: isLinked + }))); +} -compress_Compiler.prototype['font-face'] = function (node) { - return this.emit('@font-face', node.position) + this.emit('{') + this.mapVisit(node.declarations) + this.emit('}'); -}; +;// CONCATENATED MODULE: ./node_modules/@wordpress/icons/build-module/library/check.js /** - * Visit host node. + * WordPress dependencies */ -compress_Compiler.prototype.host = function (node) { - return this.emit('@host', node.position) + this.emit('{') + this.mapVisit(node.rules) + this.emit('}'); -}; - -/** - * Visit custom-media node. - */ +const check_check = (0,external_React_.createElement)(external_wp_primitives_namespaceObject.SVG, { + xmlns: "http://www.w3.org/2000/svg", + viewBox: "0 0 24 24" +}, (0,external_React_.createElement)(external_wp_primitives_namespaceObject.Path, { + d: "M16.7 7.1l-6.3 8.5-3.3-2.5-.9 1.2 4.5 3.4L17.9 8z" +})); +/* harmony default export */ const library_check = (check_check); -compress_Compiler.prototype['custom-media'] = function (node) { - return this.emit('@custom-media ' + node.name + ' ' + node.media + ';', node.position); -}; +;// CONCATENATED MODULE: ./node_modules/@wordpress/icons/build-module/library/shadow.js /** - * Visit rule node. + * WordPress dependencies */ -compress_Compiler.prototype.rule = function (node) { - const decls = node.declarations; - if (!decls.length) { - return ''; - } - return this.emit(node.selectors.join(','), node.position) + this.emit('{') + this.mapVisit(decls) + this.emit('}'); -}; +const shadow = (0,external_React_.createElement)(external_wp_primitives_namespaceObject.SVG, { + viewBox: "0 0 24 24", + xmlns: "http://www.w3.org/2000/svg" +}, (0,external_React_.createElement)(external_wp_primitives_namespaceObject.Path, { + d: "M12 8c-2.2 0-4 1.8-4 4s1.8 4 4 4 4-1.8 4-4-1.8-4-4-4zm0 6.5c-1.4 0-2.5-1.1-2.5-2.5s1.1-2.5 2.5-2.5 2.5 1.1 2.5 2.5-1.1 2.5-2.5 2.5zM12.8 3h-1.5v3h1.5V3zm-1.6 18h1.5v-3h-1.5v3zm6.8-9.8v1.5h3v-1.5h-3zm-12 0H3v1.5h3v-1.5zm9.7 5.6 2.1 2.1 1.1-1.1-2.1-2.1-1.1 1.1zM8.3 7.2 6.2 5.1 5.1 6.2l2.1 2.1 1.1-1.1zM5.1 17.8l1.1 1.1 2.1-2.1-1.1-1.1-2.1 2.1zM18.9 6.2l-1.1-1.1-2.1 2.1 1.1 1.1 2.1-2.1z" +})); +/* harmony default export */ const library_shadow = (shadow); + +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/global-styles/shadow-panel-components.js /** - * Visit declaration node. + * WordPress dependencies */ -compress_Compiler.prototype.declaration = function (node) { - return this.emit(node.property + ':' + node.value, node.position) + this.emit(';'); -}; -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/utils/transform-styles/ast/stringify/identity.js -/* eslint-disable @wordpress/no-unused-vars-before-return */ -// Adapted from https://github.com/reworkcss/css -// because we needed to remove source map support. + /** * External dependencies @@ -21687,481 +25162,1033 @@ compress_Compiler.prototype.declaration = function (node) { /** - * Expose compiler. - */ - -/* harmony default export */ var stringify_identity = (identity_Compiler); - -/** - * Initialize a new `Compiler`. + * Shared reference to an empty array for cases where it is important to avoid + * returning a new array reference on every invocation. + * + * @type {Array} */ - -function identity_Compiler(options) { - options = options || {}; - compiler.call(this, options); - this.indentation = options.indent; +const shadow_panel_components_EMPTY_ARRAY = []; +function ShadowPopoverContainer({ + shadow, + onShadowChange, + settings +}) { + const shadows = useShadowPresets(settings); + return (0,external_React_.createElement)("div", { + className: "block-editor-global-styles__shadow-popover-container" + }, (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalVStack, { + spacing: 4 + }, (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalHeading, { + level: 5 + }, (0,external_wp_i18n_namespaceObject.__)('Drop shadow')), (0,external_React_.createElement)(ShadowPresets, { + presets: shadows, + activeShadow: shadow, + onSelect: onShadowChange + }), (0,external_React_.createElement)("div", { + className: "block-editor-global-styles__clear-shadow" + }, (0,external_React_.createElement)(external_wp_components_namespaceObject.Button, { + variant: "tertiary", + onClick: () => onShadowChange(undefined) + }, (0,external_wp_i18n_namespaceObject.__)('Clear'))))); +} +function ShadowPresets({ + presets, + activeShadow, + onSelect +}) { + const { + CompositeV2: Composite, + useCompositeStoreV2: useCompositeStore + } = unlock(external_wp_components_namespaceObject.privateApis); + const compositeStore = useCompositeStore(); + return !presets ? null : (0,external_React_.createElement)(Composite, { + store: compositeStore, + role: "listbox", + className: "block-editor-global-styles__shadow__list", + "aria-label": (0,external_wp_i18n_namespaceObject.__)('Drop shadows') + }, presets.map(({ + name, + slug, + shadow + }) => (0,external_React_.createElement)(ShadowIndicator, { + key: slug, + label: name, + isActive: shadow === activeShadow, + type: slug === 'unset' ? 'unset' : 'preset', + onSelect: () => onSelect(shadow === activeShadow ? undefined : shadow), + shadow: shadow + }))); +} +function ShadowIndicator({ + type, + label, + isActive, + onSelect, + shadow +}) { + const { + CompositeItemV2: CompositeItem + } = unlock(external_wp_components_namespaceObject.privateApis); + return (0,external_React_.createElement)(CompositeItem, { + role: "option", + "aria-label": label, + "aria-selected": isActive, + className: classnames_default()('block-editor-global-styles__shadow__item', { + 'is-active': isActive + }), + render: (0,external_React_.createElement)(external_wp_components_namespaceObject.Button, { + className: classnames_default()('block-editor-global-styles__shadow-indicator', { + unset: type === 'unset' + }), + onClick: onSelect, + label: label, + style: { + boxShadow: shadow + }, + showTooltip: true + }, isActive && (0,external_React_.createElement)(build_module_icon, { + icon: library_check + })) + }); +} +function ShadowPopover({ + shadow, + onShadowChange, + settings +}) { + const popoverProps = { + placement: 'left-start', + offset: 36, + shift: true + }; + return (0,external_React_.createElement)(external_wp_components_namespaceObject.Dropdown, { + popoverProps: popoverProps, + className: "block-editor-global-styles__shadow-dropdown", + renderToggle: renderShadowToggle(), + renderContent: () => (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalDropdownContentWrapper, { + paddingSize: "medium" + }, (0,external_React_.createElement)(ShadowPopoverContainer, { + shadow: shadow, + onShadowChange: onShadowChange, + settings: settings + })) + }); +} +function renderShadowToggle() { + return ({ + onToggle, + isOpen + }) => { + const toggleProps = { + onClick: onToggle, + className: classnames_default()({ + 'is-open': isOpen + }), + 'aria-expanded': isOpen + }; + return (0,external_React_.createElement)(external_wp_components_namespaceObject.Button, { + ...toggleProps + }, (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalHStack, { + justify: "flex-start" + }, (0,external_React_.createElement)(build_module_icon, { + className: "block-editor-global-styles__toggle-icon", + icon: library_shadow, + size: 24 + }), (0,external_React_.createElement)(external_wp_components_namespaceObject.FlexItem, null, (0,external_wp_i18n_namespaceObject.__)('Drop shadow')))); + }; +} +function useShadowPresets(settings) { + return (0,external_wp_element_namespaceObject.useMemo)(() => { + var _settings$shadow$pres; + if (!settings?.shadow) { + return shadow_panel_components_EMPTY_ARRAY; + } + const defaultPresetsEnabled = settings?.shadow?.defaultPresets; + const { + default: defaultShadows, + theme: themeShadows + } = (_settings$shadow$pres = settings?.shadow?.presets) !== null && _settings$shadow$pres !== void 0 ? _settings$shadow$pres : {}; + const unsetShadow = { + name: (0,external_wp_i18n_namespaceObject.__)('Unset'), + slug: 'unset', + shadow: 'none' + }; + const shadowPresets = [...(defaultPresetsEnabled && defaultShadows || shadow_panel_components_EMPTY_ARRAY), ...(themeShadows || shadow_panel_components_EMPTY_ARRAY)]; + if (shadowPresets.length) { + shadowPresets.unshift(unsetShadow); + } + return shadowPresets; + }, [settings]); } -/** - * Inherit from `Base.prototype`. - */ - -inherits_browser_default()(identity_Compiler, compiler); +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/global-styles/border-panel.js /** - * Compile `node`. + * WordPress dependencies */ -identity_Compiler.prototype.compile = function (node) { - return this.stylesheet(node); -}; -/** - * Visit stylesheet node. - */ -identity_Compiler.prototype.stylesheet = function (node) { - return this.mapVisit(node.stylesheet.rules, '\n\n'); -}; /** - * Visit comment node. + * Internal dependencies */ -identity_Compiler.prototype.comment = function (node) { - return this.emit(this.indent() + '/*' + node.comment + '*/', node.position); -}; -/** - * Visit import node. - */ -identity_Compiler.prototype.import = function (node) { - return this.emit('@import ' + node.import + ';', node.position); -}; -/** - * Visit media node. - */ -identity_Compiler.prototype.media = function (node) { - return this.emit('@media ' + node.media, node.position) + this.emit(' {\n' + this.indent(1)) + this.mapVisit(node.rules, '\n\n') + this.emit(this.indent(-1) + '\n}'); -}; -/** - * Visit container node. - */ -identity_Compiler.prototype.container = function (node) { - return this.emit('@container ' + node.container, node.position) + this.emit(' {\n' + this.indent(1)) + this.mapVisit(node.rules, '\n\n') + this.emit(this.indent(-1) + '\n}'); +function useHasBorderPanel(settings) { + const controls = Object.values(useHasBorderPanelControls(settings)); + return controls.some(Boolean); +} +function useHasBorderPanelControls(settings) { + const controls = { + hasBorderColor: useHasBorderColorControl(settings), + hasBorderRadius: useHasBorderRadiusControl(settings), + hasBorderStyle: useHasBorderStyleControl(settings), + hasBorderWidth: useHasBorderWidthControl(settings), + hasShadow: useHasShadowControl(settings) + }; + return controls; +} +function useHasBorderColorControl(settings) { + return settings?.border?.color; +} +function useHasBorderRadiusControl(settings) { + return settings?.border?.radius; +} +function useHasBorderStyleControl(settings) { + return settings?.border?.style; +} +function useHasBorderWidthControl(settings) { + return settings?.border?.width; +} +function useHasShadowControl(settings) { + const shadows = useShadowPresets(settings); + return !!settings?.shadow && shadows.length > 0; +} +function BorderToolsPanel({ + resetAllFilter, + onChange, + value, + panelId, + children, + label +}) { + const resetAll = () => { + const updatedValue = resetAllFilter(value); + onChange(updatedValue); + }; + return (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalToolsPanel, { + label: label, + resetAll: resetAll, + panelId: panelId, + dropdownMenuProps: TOOLSPANEL_DROPDOWNMENU_PROPS + }, children); +} +const border_panel_DEFAULT_CONTROLS = { + radius: true, + color: true, + width: true, + shadow: false }; +function BorderPanel({ + as: Wrapper = BorderToolsPanel, + value, + onChange, + inheritedValue = value, + settings, + panelId, + name, + defaultControls = border_panel_DEFAULT_CONTROLS +}) { + var _settings$shadow$pres, _overrideOrigins; + const colors = useColorsPerOrigin(settings); + const decodeValue = (0,external_wp_element_namespaceObject.useCallback)(rawValue => getValueFromVariable({ + settings + }, '', rawValue), [settings]); + const encodeColorValue = colorValue => { + const allColors = colors.flatMap(({ + colors: originColors + }) => originColors); + const colorObject = allColors.find(({ + color + }) => color === colorValue); + return colorObject ? 'var:preset|color|' + colorObject.slug : colorValue; + }; + const border = (0,external_wp_element_namespaceObject.useMemo)(() => { + if ((0,external_wp_components_namespaceObject.__experimentalHasSplitBorders)(inheritedValue?.border)) { + const borderValue = { + ...inheritedValue?.border + }; + ['top', 'right', 'bottom', 'left'].forEach(side => { + borderValue[side] = { + ...borderValue[side], + color: decodeValue(borderValue[side]?.color) + }; + }); + return borderValue; + } + return { + ...inheritedValue?.border, + color: inheritedValue?.border?.color ? decodeValue(inheritedValue?.border?.color) : undefined + }; + }, [inheritedValue?.border, decodeValue]); + const setBorder = newBorder => onChange({ + ...value, + border: newBorder + }); + const showBorderColor = useHasBorderColorControl(settings); + const showBorderStyle = useHasBorderStyleControl(settings); + const showBorderWidth = useHasBorderWidthControl(settings); -/** - * Visit document node. - */ + // Border radius. + const showBorderRadius = useHasBorderRadiusControl(settings); + const borderRadiusValues = decodeValue(border?.radius); + const setBorderRadius = newBorderRadius => setBorder({ + ...border, + radius: newBorderRadius + }); + const hasBorderRadius = () => { + const borderValues = value?.border?.radius; + if (typeof borderValues === 'object') { + return Object.entries(borderValues).some(Boolean); + } + return !!borderValues; + }; + const hasShadowControl = useHasShadowControl(settings); -identity_Compiler.prototype.document = function (node) { - const doc = '@' + (node.vendor || '') + 'document ' + node.document; - return this.emit(doc, node.position) + this.emit(' ' + ' {\n' + this.indent(1)) + this.mapVisit(node.rules, '\n\n') + this.emit(this.indent(-1) + '\n}'); -}; + // Shadow + const shadow = decodeValue(inheritedValue?.shadow); + const shadowPresets = (_settings$shadow$pres = settings?.shadow?.presets) !== null && _settings$shadow$pres !== void 0 ? _settings$shadow$pres : {}; + const overriddenShadowPresets = (_overrideOrigins = overrideOrigins(shadowPresets)) !== null && _overrideOrigins !== void 0 ? _overrideOrigins : []; + const setShadow = newValue => { + const slug = overriddenShadowPresets?.find(({ + shadow: shadowName + }) => shadowName === newValue)?.slug; + onChange(setImmutably(value, ['shadow'], slug ? `var:preset|shadow|${slug}` : newValue || undefined)); + }; + const hasShadow = () => !!value?.shadow; + const resetShadow = () => setShadow(undefined); + const resetBorder = () => { + if (hasBorderRadius()) { + return setBorder({ + radius: value?.border?.radius + }); + } + setBorder(undefined); + }; + const onBorderChange = newBorder => { + // Ensure we have a visible border style when a border width or + // color is being selected. + const updatedBorder = { + ...newBorder + }; + if ((0,external_wp_components_namespaceObject.__experimentalHasSplitBorders)(updatedBorder)) { + ['top', 'right', 'bottom', 'left'].forEach(side => { + if (updatedBorder[side]) { + updatedBorder[side] = { + ...updatedBorder[side], + color: encodeColorValue(updatedBorder[side]?.color) + }; + } + }); + } else if (updatedBorder) { + updatedBorder.color = encodeColorValue(updatedBorder.color); + } -/** - * Visit charset node. - */ + // As radius is maintained separately to color, style, and width + // maintain its value. Undefined values here will be cleaned when + // global styles are saved. + setBorder({ + radius: border?.radius, + ...updatedBorder + }); + }; + const resetAllFilter = (0,external_wp_element_namespaceObject.useCallback)(previousValue => { + return { + ...previousValue, + border: undefined, + shadow: undefined + }; + }, []); + const showBorderByDefault = defaultControls?.color || defaultControls?.width; + const hasBorderControl = showBorderColor || showBorderStyle || showBorderWidth || showBorderRadius; + const label = useBorderPanelLabel({ + blockName: name, + hasShadowControl, + hasBorderControl + }); + return (0,external_React_.createElement)(Wrapper, { + resetAllFilter: resetAllFilter, + value: value, + onChange: onChange, + panelId: panelId, + label: label + }, (showBorderWidth || showBorderColor) && (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalToolsPanelItem, { + hasValue: () => (0,external_wp_components_namespaceObject.__experimentalIsDefinedBorder)(value?.border), + label: (0,external_wp_i18n_namespaceObject.__)('Border'), + onDeselect: () => resetBorder(), + isShownByDefault: showBorderByDefault, + panelId: panelId + }, (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalBorderBoxControl, { + colors: colors, + enableAlpha: true, + enableStyle: showBorderStyle, + onChange: onBorderChange, + popoverOffset: 40, + popoverPlacement: "left-start", + value: border, + __experimentalIsRenderedInSidebar: true, + size: '__unstable-large', + hideLabelFromVision: !hasShadowControl, + label: (0,external_wp_i18n_namespaceObject.__)('Border') + })), showBorderRadius && (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalToolsPanelItem, { + hasValue: hasBorderRadius, + label: (0,external_wp_i18n_namespaceObject.__)('Radius'), + onDeselect: () => setBorderRadius(undefined), + isShownByDefault: defaultControls.radius, + panelId: panelId + }, (0,external_React_.createElement)(BorderRadiusControl, { + values: borderRadiusValues, + onChange: newValue => { + setBorderRadius(newValue || undefined); + } + })), hasShadowControl && (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalToolsPanelItem, { + label: (0,external_wp_i18n_namespaceObject.__)('Shadow'), + hasValue: hasShadow, + onDeselect: resetShadow, + isShownByDefault: defaultControls.shadow, + panelId: panelId + }, hasBorderControl ? (0,external_React_.createElement)(external_wp_components_namespaceObject.BaseControl.VisualLabel, { + as: "legend" + }, (0,external_wp_i18n_namespaceObject.__)('Shadow')) : null, (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalItemGroup, { + isBordered: true, + isSeparated: true + }, (0,external_React_.createElement)(ShadowPopover, { + shadow: shadow, + onShadowChange: setShadow, + settings: settings + })))); +} -identity_Compiler.prototype.charset = function (node) { - return this.emit('@charset ' + node.charset + ';', node.position); -}; +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/hooks/border.js /** - * Visit namespace node. + * External dependencies */ -identity_Compiler.prototype.namespace = function (node) { - return this.emit('@namespace ' + node.namespace + ';', node.position); -}; /** - * Visit supports node. + * WordPress dependencies */ -identity_Compiler.prototype.supports = function (node) { - return this.emit('@supports ' + node.supports, node.position) + this.emit(' {\n' + this.indent(1)) + this.mapVisit(node.rules, '\n\n') + this.emit(this.indent(-1) + '\n}'); -}; -/** - * Visit keyframes node. - */ -identity_Compiler.prototype.keyframes = function (node) { - return this.emit('@' + (node.vendor || '') + 'keyframes ' + node.name, node.position) + this.emit(' {\n' + this.indent(1)) + this.mapVisit(node.keyframes, '\n') + this.emit(this.indent(-1) + '}'); -}; -/** - * Visit keyframe node. - */ -identity_Compiler.prototype.keyframe = function (node) { - const decls = node.declarations; - return this.emit(this.indent()) + this.emit(node.values.join(', '), node.position) + this.emit(' {\n' + this.indent(1)) + this.mapVisit(decls, '\n') + this.emit(this.indent(-1) + '\n' + this.indent() + '}\n'); -}; /** - * Visit page node. + * Internal dependencies */ -identity_Compiler.prototype.page = function (node) { - const sel = node.selectors.length ? node.selectors.join(', ') + ' ' : ''; - return this.emit('@page ' + sel, node.position) + this.emit('{\n') + this.emit(this.indent(1)) + this.mapVisit(node.declarations, '\n') + this.emit(this.indent(-1)) + this.emit('\n}'); -}; -/** - * Visit font-face node. - */ -identity_Compiler.prototype['font-face'] = function (node) { - return this.emit('@font-face ', node.position) + this.emit('{\n') + this.emit(this.indent(1)) + this.mapVisit(node.declarations, '\n') + this.emit(this.indent(-1)) + this.emit('\n}'); -}; -/** - * Visit host node. - */ -identity_Compiler.prototype.host = function (node) { - return this.emit('@host', node.position) + this.emit(' {\n' + this.indent(1)) + this.mapVisit(node.rules, '\n\n') + this.emit(this.indent(-1) + '\n}'); -}; -/** - * Visit custom-media node. - */ -identity_Compiler.prototype['custom-media'] = function (node) { - return this.emit('@custom-media ' + node.name + ' ' + node.media + ';', node.position); +const BORDER_SUPPORT_KEY = '__experimentalBorder'; +const SHADOW_SUPPORT_KEY = 'shadow'; +const getColorByProperty = (colors, property, value) => { + let matchedColor; + colors.some(origin => origin.colors.some(color => { + if (color[property] === value) { + matchedColor = color; + return true; + } + return false; + })); + return matchedColor; }; - -/** - * Visit rule node. - */ - -identity_Compiler.prototype.rule = function (node) { - const indent = this.indent(); - const decls = node.declarations; - if (!decls.length) { - return ''; +const getMultiOriginColor = ({ + colors, + namedColor, + customColor +}) => { + // Search each origin (default, theme, or user) for matching color by name. + if (namedColor) { + const colorObject = getColorByProperty(colors, 'slug', namedColor); + if (colorObject) { + return colorObject; + } } - return this.emit(node.selectors.map(function (s) { - return indent + s; - }).join(',\n'), node.position) + this.emit(' {\n') + this.emit(this.indent(1)) + this.mapVisit(decls, '\n') + this.emit(this.indent(-1)) + this.emit('\n' + this.indent() + '}'); -}; -/** - * Visit declaration node. - */ + // Skip if no custom color or matching named color. + if (!customColor) { + return { + color: undefined + }; + } -identity_Compiler.prototype.declaration = function (node) { - return this.emit(this.indent()) + this.emit(node.property + ': ' + node.value, node.position) + this.emit(';'); + // Attempt to find color via custom color value or build new object. + const colorObject = getColorByProperty(colors, 'color', customColor); + return colorObject ? colorObject : { + color: customColor + }; }; - -/** - * Increase, decrease or return current indentation. - */ - -identity_Compiler.prototype.indent = function (level) { - this.level = this.level || 1; - if (null !== level) { - this.level += level; - return ''; +function getColorSlugFromVariable(value) { + const namedColor = /var:preset\|color\|(.+)/.exec(value); + if (namedColor && namedColor[1]) { + return namedColor[1]; } - return Array(this.level).join(this.indentation || ' '); -}; - -/* eslint-enable @wordpress/no-unused-vars-before-return */ - -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/utils/transform-styles/ast/stringify/index.js -// Adapted from https://github.com/reworkcss/css -// because we needed to remove source map support. - -/** - * Internal dependencies - */ - - + return null; +} +function styleToAttributes(style) { + if ((0,external_wp_components_namespaceObject.__experimentalHasSplitBorders)(style?.border)) { + return { + style, + borderColor: undefined + }; + } + const borderColorValue = style?.border?.color; + const borderColorSlug = borderColorValue?.startsWith('var:preset|color|') ? borderColorValue.substring('var:preset|color|'.length) : undefined; + const updatedStyle = { + ...style + }; + updatedStyle.border = { + ...updatedStyle.border, + color: borderColorSlug ? undefined : borderColorValue + }; + return { + style: utils_cleanEmptyObject(updatedStyle), + borderColor: borderColorSlug + }; +} +function attributesToStyle(attributes) { + if ((0,external_wp_components_namespaceObject.__experimentalHasSplitBorders)(attributes.style?.border)) { + return attributes.style; + } + return { + ...attributes.style, + border: { + ...attributes.style?.border, + color: attributes.borderColor ? 'var:preset|color|' + attributes.borderColor : attributes.style?.border?.color + } + }; +} +function BordersInspectorControl({ + label, + children, + resetAllFilter +}) { + const attributesResetAllFilter = (0,external_wp_element_namespaceObject.useCallback)(attributes => { + const existingStyle = attributesToStyle(attributes); + const updatedStyle = resetAllFilter(existingStyle); + return { + ...attributes, + ...styleToAttributes(updatedStyle) + }; + }, [resetAllFilter]); + return (0,external_React_.createElement)(inspector_controls, { + group: "border", + resetAllFilter: attributesResetAllFilter, + label: label + }, children); +} +function border_BorderPanel({ + clientId, + name, + setAttributes, + settings +}) { + const isEnabled = useHasBorderPanel(settings); + function selector(select) { + const { + style, + borderColor + } = select(store).getBlockAttributes(clientId) || {}; + return { + style, + borderColor + }; + } + const { + style, + borderColor + } = (0,external_wp_data_namespaceObject.useSelect)(selector, [clientId]); + const value = (0,external_wp_element_namespaceObject.useMemo)(() => { + return attributesToStyle({ + style, + borderColor + }); + }, [style, borderColor]); + const onChange = newStyle => { + setAttributes(styleToAttributes(newStyle)); + }; + if (!isEnabled) { + return null; + } + const defaultControls = { + ...(0,external_wp_blocks_namespaceObject.getBlockSupport)(name, [BORDER_SUPPORT_KEY, '__experimentalDefaultControls']), + ...(0,external_wp_blocks_namespaceObject.getBlockSupport)(name, [SHADOW_SUPPORT_KEY, '__experimentalDefaultControls']) + }; + return (0,external_React_.createElement)(BorderPanel, { + as: BordersInspectorControl, + panelId: clientId, + settings: settings, + value: value, + onChange: onChange, + defaultControls: defaultControls + }); +} /** - * Stringfy the given AST `node`. - * - * Options: + * Determine whether there is block support for border properties. * - * - `compress` space-optimized output - * - `sourcemap` return an object with `.code` and `.map` + * @param {string} blockName Block name. + * @param {string} feature Border feature to check support for. * - * @param {Object} node - * @param {Object} [options] - * @return {string} - */ - -/* harmony default export */ function stringify(node, options) { - options = options || {}; - const compiler = options.compress ? new compress(options) : new stringify_identity(options); - const code = compiler.compile(node); - return code; -} - -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/utils/transform-styles/traverse.js -/** - * External dependencies - */ - - -/** - * Internal dependencies + * @return {boolean} Whether there is support. */ - -function traverseCSS(css, callback) { - try { - const parsed = parse(css); - const updated = traverse_default().map(parsed, function (node) { - if (!node) { - return node; - } - const updatedNode = callback(node); - return this.update(updatedNode); - }); - return stringify(updated); - } catch (err) { - // eslint-disable-next-line no-console - console.warn('Error while traversing the CSS: ' + err); - return null; +function hasBorderSupport(blockName, feature = 'any') { + if (external_wp_element_namespaceObject.Platform.OS !== 'web') { + return false; + } + const support = (0,external_wp_blocks_namespaceObject.getBlockSupport)(blockName, BORDER_SUPPORT_KEY); + if (support === true) { + return true; + } + if (feature === 'any') { + return !!(support?.color || support?.radius || support?.width || support?.style); } + return !!support?.[feature]; } -/* harmony default export */ var transform_styles_traverse = (traverseCSS); -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/utils/transform-styles/transforms/url-rewrite.js /** - * Return `true` if the given path is http/https. + * Determine whether there is block support for shadow properties. * - * @param {string} filePath path + * @param {string} blockName Block name. * - * @return {boolean} is remote path. + * @return {boolean} Whether there is support. */ -function isRemotePath(filePath) { - return /^(?:https?:)?\/\//.test(filePath); +function hasShadowSupport(blockName) { + return hasBlockSupport(blockName, SHADOW_SUPPORT_KEY); +} +function useBorderPanelLabel({ + blockName, + hasBorderControl, + hasShadowControl +} = {}) { + const settings = useBlockSettings(blockName); + const controls = useHasBorderPanelControls(settings); + if (!hasBorderControl && !hasShadowControl && blockName) { + hasBorderControl = controls?.hasBorderColor || controls?.hasBorderStyle || controls?.hasBorderWidth || controls?.hasBorderRadius; + hasShadowControl = controls?.hasShadow; + } + if (hasBorderControl && hasShadowControl) { + return (0,external_wp_i18n_namespaceObject.__)('Border & Shadow'); + } + if (hasShadowControl) { + return (0,external_wp_i18n_namespaceObject.__)('Shadow'); + } + return (0,external_wp_i18n_namespaceObject.__)('Border'); } /** - * Return `true` if the given filePath is an absolute url. + * Returns a new style object where the specified border attribute has been + * removed. * - * @param {string} filePath path + * @param {Object} style Styles from block attributes. + * @param {string} attribute The border style attribute to clear. * - * @return {boolean} is absolute path. + * @return {Object} Style object with the specified attribute removed. */ -function isAbsolutePath(filePath) { - return /^\/(?!\/)/.test(filePath); +function removeBorderAttribute(style, attribute) { + return cleanEmptyObject({ + ...style, + border: { + ...style?.border, + [attribute]: undefined + } + }); } /** - * Whether or not the url should be inluded. + * Filters registered block settings, extending attributes to include + * `borderColor` if needed. * - * @param {Object} meta url meta info + * @param {Object} settings Original block settings. * - * @return {boolean} is valid. + * @return {Object} Updated block settings. */ -function isValidURL(meta) { - // Ignore hashes or data uris. - if (meta.value.indexOf('data:') === 0 || meta.value.indexOf('#') === 0) { - return false; - } - if (isAbsolutePath(meta.value)) { - return false; +function addAttributes(settings) { + if (!hasBorderSupport(settings, 'color')) { + return settings; } - // Do not handle the http/https urls if `includeRemote` is false. - if (isRemotePath(meta.value)) { - return false; + // Allow blocks to specify default value if needed. + if (settings.attributes.borderColor) { + return settings; } - return true; + + // Add new borderColor attribute to block settings. + return { + ...settings, + attributes: { + ...settings.attributes, + borderColor: { + type: 'string' + } + } + }; } /** - * Get the absolute path of the url, relative to the basePath + * Override props assigned to save component to inject border color. * - * @param {string} str the url - * @param {string} baseURL base URL + * @param {Object} props Additional props applied to save element. + * @param {Object|string} blockNameOrType Block type definition. + * @param {Object} attributes Block's attributes. * - * @return {string} the full path to the file + * @return {Object} Filtered props to apply to save element. */ -function getResourcePath(str, baseURL) { - return new URL(str, baseURL).toString(); +function border_addSaveProps(props, blockNameOrType, attributes) { + if (!hasBorderSupport(blockNameOrType, 'color') || shouldSkipSerialization(blockNameOrType, BORDER_SUPPORT_KEY, 'color')) { + return props; + } + const borderClasses = getBorderClasses(attributes); + const newClassName = classnames_default()(props.className, borderClasses); + + // If we are clearing the last of the previous classes in `className` + // set it to `undefined` to avoid rendering empty DOM attributes. + props.className = newClassName ? newClassName : undefined; + return props; } /** - * Process the single `url()` pattern + * Generates a CSS class name consisting of all the applicable border color + * classes given the current block attributes. * - * @param {string} baseURL the base URL for relative URLs. + * @param {Object} attributes Block's attributes. * - * @return {Promise} the Promise. + * @return {string} CSS class name. */ -function processURL(baseURL) { - return meta => ({ - ...meta, - newUrl: 'url(' + meta.before + meta.quote + getResourcePath(meta.value, baseURL) + meta.quote + meta.after + ')' +function getBorderClasses(attributes) { + const { + borderColor, + style + } = attributes; + const borderColorClass = getColorClassName('border-color', borderColor); + return classnames_default()({ + 'has-border-color': borderColor || style?.border?.color, + [borderColorClass]: !!borderColorClass + }); +} +function border_useBlockProps({ + name, + borderColor, + style +}) { + const { + colors + } = useMultipleOriginColorsAndGradients(); + if (!hasBorderSupport(name, 'color') || shouldSkipSerialization(name, BORDER_SUPPORT_KEY, 'color')) { + return {}; + } + const { + color: borderColorValue + } = getMultiOriginColor({ + colors, + namedColor: borderColor + }); + const { + color: borderTopColor + } = getMultiOriginColor({ + colors, + namedColor: getColorSlugFromVariable(style?.border?.top?.color) + }); + const { + color: borderRightColor + } = getMultiOriginColor({ + colors, + namedColor: getColorSlugFromVariable(style?.border?.right?.color) + }); + const { + color: borderBottomColor + } = getMultiOriginColor({ + colors, + namedColor: getColorSlugFromVariable(style?.border?.bottom?.color) + }); + const { + color: borderLeftColor + } = getMultiOriginColor({ + colors, + namedColor: getColorSlugFromVariable(style?.border?.left?.color) + }); + const extraStyles = { + borderTopColor: borderTopColor || borderColorValue, + borderRightColor: borderRightColor || borderColorValue, + borderBottomColor: borderBottomColor || borderColorValue, + borderLeftColor: borderLeftColor || borderColorValue + }; + return border_addSaveProps({ + style: utils_cleanEmptyObject(extraStyles) || {} + }, name, { + borderColor, + style }); } +/* harmony default export */ const border = ({ + useBlockProps: border_useBlockProps, + addSaveProps: border_addSaveProps, + attributeKeys: ['borderColor', 'style'], + hasSupport(name) { + return hasBorderSupport(name, 'color'); + } +}); +(0,external_wp_hooks_namespaceObject.addFilter)('blocks.registerBlockType', 'core/border/addAttributes', addAttributes); +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/gradients/use-gradient.js /** - * Get all `url()`s, and return the meta info - * - * @param {string} value decl.value. - * - * @return {Array} the urls. + * WordPress dependencies */ -function getURLs(value) { - const reg = /url\((\s*)(['"]?)(.+?)\2(\s*)\)/g; - let match; - const URLs = []; - while ((match = reg.exec(value)) !== null) { - const meta = { - source: match[0], - before: match[1], - quote: match[2], - value: match[3], - after: match[4] - }; - if (isValidURL(meta)) { - URLs.push(meta); - } + + + +/** + * Internal dependencies + */ + + + +function __experimentalGetGradientClass(gradientSlug) { + if (!gradientSlug) { + return undefined; } - return URLs; + return `has-${gradientSlug}-gradient-background`; } /** - * Replace the raw value's `url()` segment to the new value + * Retrieves the gradient value per slug. * - * @param {string} raw the raw value. - * @param {Array} URLs the URLs to replace. + * @param {Array} gradients Gradient Palette + * @param {string} slug Gradient slug * - * @return {string} the new value. + * @return {string} Gradient value. */ -function replaceURLs(raw, URLs) { - URLs.forEach(item => { - raw = raw.replace(item.source, item.newUrl); - }); - return raw; +function getGradientValueBySlug(gradients, slug) { + const gradient = gradients?.find(g => g.slug === slug); + return gradient && gradient.gradient; +} +function __experimentalGetGradientObjectByGradientValue(gradients, value) { + const gradient = gradients?.find(g => g.gradient === value); + return gradient; +} + +/** + * Retrieves the gradient slug per slug. + * + * @param {Array} gradients Gradient Palette + * @param {string} value Gradient value + * @return {string} Gradient slug. + */ +function getGradientSlugByValue(gradients, value) { + const gradient = __experimentalGetGradientObjectByGradientValue(gradients, value); + return gradient && gradient.slug; } -const rewrite = rootURL => node => { - if (node.type === 'declaration') { - const updatedURLs = getURLs(node.value).map(processURL(rootURL)); +function __experimentalUseGradient({ + gradientAttribute = 'gradient', + customGradientAttribute = 'customGradient' +} = {}) { + const { + clientId + } = useBlockEditContext(); + const [userGradientPalette, themeGradientPalette, defaultGradientPalette] = use_settings_useSettings('color.gradients.custom', 'color.gradients.theme', 'color.gradients.default'); + const allGradients = (0,external_wp_element_namespaceObject.useMemo)(() => [...(userGradientPalette || []), ...(themeGradientPalette || []), ...(defaultGradientPalette || [])], [userGradientPalette, themeGradientPalette, defaultGradientPalette]); + const { + gradient, + customGradient + } = (0,external_wp_data_namespaceObject.useSelect)(select => { + const { + getBlockAttributes + } = select(store); + const attributes = getBlockAttributes(clientId) || {}; return { - ...node, - value: replaceURLs(node.value, updatedURLs) + customGradient: attributes[customGradientAttribute], + gradient: attributes[gradientAttribute] }; + }, [clientId, gradientAttribute, customGradientAttribute]); + const { + updateBlockAttributes + } = (0,external_wp_data_namespaceObject.useDispatch)(store); + const setGradient = (0,external_wp_element_namespaceObject.useCallback)(newGradientValue => { + const slug = getGradientSlugByValue(allGradients, newGradientValue); + if (slug) { + updateBlockAttributes(clientId, { + [gradientAttribute]: slug, + [customGradientAttribute]: undefined + }); + return; + } + updateBlockAttributes(clientId, { + [gradientAttribute]: undefined, + [customGradientAttribute]: newGradientValue + }); + }, [allGradients, clientId, updateBlockAttributes]); + const gradientClass = __experimentalGetGradientClass(gradient); + let gradientValue; + if (gradient) { + gradientValue = getGradientValueBySlug(allGradients, gradient); + } else { + gradientValue = customGradient; } - return node; -}; -/* harmony default export */ var url_rewrite = (rewrite); + return { + gradientClass, + gradientValue, + setGradient + }; +} + +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/colors-gradients/control.js -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/utils/transform-styles/transforms/wrap.js /** - * @constant string IS_ROOT_TAG Regex to check if the selector is a root tag selector. + * External dependencies */ -const IS_ROOT_TAG = /^(body|html|:root).*$/; + /** - * Creates a callback to modify selectors so they only apply within a certain - * namespace. - * - * @param {string} namespace Namespace to prefix selectors with. - * @param {string[]} ignore Selectors to not prefix. - * - * @return {(node: Object) => Object} Callback to wrap selectors. + * WordPress dependencies */ -const wrap = (namespace, ignore = []) => node => { - /** - * Updates selector if necessary. - * - * @param {string} selector Selector to modify. - * - * @return {string} Updated selector. - */ - const updateSelector = selector => { - if (ignore.includes(selector.trim())) { - return selector; - } - // Skip the update when a selector already has a namespace + space (" "). - if (selector.trim().startsWith(`${namespace} `)) { - return selector; - } - // Anything other than a root tag is always prefixed. - { - if (!selector.match(IS_ROOT_TAG)) { - return namespace + ' ' + selector; - } - } - // HTML and Body elements cannot be contained within our container so lets extract their styles. - return selector.replace(/^(body|html|:root)/, namespace); +/** + * Internal dependencies + */ + + +const colorsAndGradientKeys = ['colors', 'disableCustomColors', 'gradients', 'disableCustomGradients']; +const TAB_IDS = { + color: 'color', + gradient: 'gradient' +}; +function ColorGradientControlInner({ + colors, + gradients, + disableCustomColors, + disableCustomGradients, + __experimentalIsRenderedInSidebar, + className, + label, + onColorChange, + onGradientChange, + colorValue, + gradientValue, + clearable, + showTitle = true, + enableAlpha, + headingLevel +}) { + const canChooseAColor = onColorChange && (colors && colors.length > 0 || !disableCustomColors); + const canChooseAGradient = onGradientChange && (gradients && gradients.length > 0 || !disableCustomGradients); + if (!canChooseAColor && !canChooseAGradient) { + return null; + } + const tabPanels = { + [TAB_IDS.color]: (0,external_React_.createElement)(external_wp_components_namespaceObject.ColorPalette, { + value: colorValue, + onChange: canChooseAGradient ? newColor => { + onColorChange(newColor); + onGradientChange(); + } : onColorChange, + colors, + disableCustomColors, + __experimentalIsRenderedInSidebar: __experimentalIsRenderedInSidebar, + clearable: clearable, + enableAlpha: enableAlpha, + headingLevel: headingLevel + }), + [TAB_IDS.gradient]: (0,external_React_.createElement)(external_wp_components_namespaceObject.GradientPicker, { + value: gradientValue, + onChange: canChooseAColor ? newGradient => { + onGradientChange(newGradient); + onColorChange(); + } : onGradientChange, + gradients, + disableCustomGradients, + __experimentalIsRenderedInSidebar: __experimentalIsRenderedInSidebar, + clearable: clearable, + headingLevel: headingLevel + }) }; - if (node.type === 'rule') { - return { - ...node, - selectors: node.selectors.map(updateSelector) - }; - } - return node; -}; -/* harmony default export */ var transforms_wrap = (wrap); - -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/utils/transform-styles/index.js -/** - * WordPress dependencies - */ - - -/** - * Internal dependencies - */ - - - + const renderPanelType = type => (0,external_React_.createElement)("div", { + className: "block-editor-color-gradient-control__panel" + }, tabPanels[type]); -/** - * Applies a series of CSS rule transforms to wrap selectors inside a given class and/or rewrite URLs depending on the parameters passed. - * - * @param {Object|Array} styles CSS rules. - * @param {string} wrapperClassName Wrapper Class Name. - * @return {Array} converted rules. - */ -const transform_styles_transformStyles = (styles, wrapperClassName = '') => { - return Object.values(styles !== null && styles !== void 0 ? styles : []).map(({ - css, - baseURL - }) => { - const transforms = []; - if (wrapperClassName) { - transforms.push(transforms_wrap(wrapperClassName)); - } - if (baseURL) { - transforms.push(url_rewrite(baseURL)); - } - if (transforms.length) { - return transform_styles_traverse(css, (0,external_wp_compose_namespaceObject.compose)(transforms)); - } - return css; + // Unlocking `Tabs` too early causes the `unlock` method to receive an empty + // object, due to circular dependencies. + // See https://github.com/WordPress/gutenberg/issues/52692 + const { + Tabs + } = unlock(external_wp_components_namespaceObject.privateApis); + return (0,external_React_.createElement)(external_wp_components_namespaceObject.BaseControl, { + __nextHasNoMarginBottom: true, + className: classnames_default()('block-editor-color-gradient-control', className) + }, (0,external_React_.createElement)("fieldset", { + className: "block-editor-color-gradient-control__fieldset" + }, (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalVStack, { + spacing: 1 + }, showTitle && (0,external_React_.createElement)("legend", null, (0,external_React_.createElement)("div", { + className: "block-editor-color-gradient-control__color-indicator" + }, (0,external_React_.createElement)(external_wp_components_namespaceObject.BaseControl.VisualLabel, null, label))), canChooseAColor && canChooseAGradient && (0,external_React_.createElement)("div", null, (0,external_React_.createElement)(Tabs, { + initialTabId: gradientValue ? TAB_IDS.gradient : !!canChooseAColor && TAB_IDS.color + }, (0,external_React_.createElement)(Tabs.TabList, null, (0,external_React_.createElement)(Tabs.Tab, { + tabId: TAB_IDS.color + }, (0,external_wp_i18n_namespaceObject.__)('Solid')), (0,external_React_.createElement)(Tabs.Tab, { + tabId: TAB_IDS.gradient + }, (0,external_wp_i18n_namespaceObject.__)('Gradient'))), (0,external_React_.createElement)(Tabs.TabPanel, { + tabId: TAB_IDS.color, + className: 'block-editor-color-gradient-control__panel', + focusable: false + }, tabPanels.color), (0,external_React_.createElement)(Tabs.TabPanel, { + tabId: TAB_IDS.gradient, + className: 'block-editor-color-gradient-control__panel', + focusable: false + }, tabPanels.gradient))), !canChooseAGradient && renderPanelType(TAB_IDS.color), !canChooseAColor && renderPanelType(TAB_IDS.gradient)))); +} +function ColorGradientControlSelect(props) { + const [colors, gradients, customColors, customGradients] = use_settings_useSettings('color.palette', 'color.gradients', 'color.custom', 'color.customGradient'); + return (0,external_React_.createElement)(ColorGradientControlInner, { + colors: colors, + gradients: gradients, + disableCustomColors: !customColors, + disableCustomGradients: !customGradients, + ...props }); -}; -/* harmony default export */ var transform_styles = (transform_styles_transformStyles); +} +function ColorGradientControl(props) { + if (colorsAndGradientKeys.every(key => props.hasOwnProperty(key))) { + return (0,external_React_.createElement)(ColorGradientControlInner, { + ...props + }); + } + return (0,external_React_.createElement)(ColorGradientControlSelect, { + ...props + }); +} +/* harmony default export */ const control = (ColorGradientControl); -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/editor-styles/index.js +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/global-styles/color-panel.js /** * External dependencies */ - - /** * WordPress dependencies */ @@ -22175,724 +26202,790 @@ const transform_styles_transformStyles = (styles, wrapperClassName = '') => { -colord_k([names, a11y]); -function useDarkThemeBodyClassName(styles, scope) { - return (0,external_wp_element_namespaceObject.useCallback)(node => { - if (!node) { - return; - } - const { - ownerDocument - } = node; - const { - defaultView, - body - } = ownerDocument; - const canvas = scope ? ownerDocument.querySelector(scope) : body; - let backgroundColor; - if (!canvas) { - // The real .editor-styles-wrapper element might not exist in the - // DOM, so calculate the background color by creating a fake - // wrapper. - const tempCanvas = ownerDocument.createElement('div'); - tempCanvas.classList.add('editor-styles-wrapper'); - body.appendChild(tempCanvas); - backgroundColor = defaultView?.getComputedStyle(tempCanvas, null).getPropertyValue('background-color'); - body.removeChild(tempCanvas); - } else { - backgroundColor = defaultView?.getComputedStyle(canvas, null).getPropertyValue('background-color'); - } - const colordBackgroundColor = colord_w(backgroundColor); - // If background is transparent, it should be treated as light color. - if (colordBackgroundColor.luminance() > 0.5 || colordBackgroundColor.alpha() === 0) { - body.classList.remove('is-dark-theme'); - } else { - body.classList.add('is-dark-theme'); - } - }, [styles, scope]); + + +function useHasColorPanel(settings) { + const hasTextPanel = useHasTextPanel(settings); + const hasBackgroundPanel = useHasBackgroundPanel(settings); + const hasLinkPanel = useHasLinkPanel(settings); + const hasHeadingPanel = useHasHeadingPanel(settings); + const hasButtonPanel = useHasButtonPanel(settings); + const hasCaptionPanel = useHasCaptionPanel(settings); + return hasTextPanel || hasBackgroundPanel || hasLinkPanel || hasHeadingPanel || hasButtonPanel || hasCaptionPanel; } -function EditorStyles({ - styles, - scope +function useHasTextPanel(settings) { + const colors = useColorsPerOrigin(settings); + return settings?.color?.text && (colors?.length > 0 || settings?.color?.custom); +} +function useHasLinkPanel(settings) { + const colors = useColorsPerOrigin(settings); + return settings?.color?.link && (colors?.length > 0 || settings?.color?.custom); +} +function useHasCaptionPanel(settings) { + const colors = useColorsPerOrigin(settings); + return settings?.color?.caption && (colors?.length > 0 || settings?.color?.custom); +} +function useHasHeadingPanel(settings) { + const colors = useColorsPerOrigin(settings); + const gradients = useGradientsPerOrigin(settings); + return settings?.color?.heading && (colors?.length > 0 || settings?.color?.custom || gradients?.length > 0 || settings?.color?.customGradient); +} +function useHasButtonPanel(settings) { + const colors = useColorsPerOrigin(settings); + const gradients = useGradientsPerOrigin(settings); + return settings?.color?.button && (colors?.length > 0 || settings?.color?.custom || gradients?.length > 0 || settings?.color?.customGradient); +} +function useHasBackgroundPanel(settings) { + const colors = useColorsPerOrigin(settings); + const gradients = useGradientsPerOrigin(settings); + return settings?.color?.background && (colors?.length > 0 || settings?.color?.custom || gradients?.length > 0 || settings?.color?.customGradient); +} +function ColorToolsPanel({ + resetAllFilter, + onChange, + value, + panelId, + children }) { - const overrides = (0,external_wp_data_namespaceObject.useSelect)(select => unlock(select(store)).getStyleOverrides(), []); - const [transformedStyles, transformedSvgs] = (0,external_wp_element_namespaceObject.useMemo)(() => { - const _styles = Object.values(styles !== null && styles !== void 0 ? styles : []); - for (const [id, override] of overrides) { - const index = _styles.findIndex(({ - id: _id - }) => id === _id); - const overrideWithId = { - ...override, - id + const resetAll = () => { + const updatedValue = resetAllFilter(value); + onChange(updatedValue); + }; + return (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalToolsPanel, { + label: (0,external_wp_i18n_namespaceObject.__)('Color'), + resetAll: resetAll, + panelId: panelId, + hasInnerWrapper: true, + className: "color-block-support-panel", + __experimentalFirstVisibleItemClass: "first", + __experimentalLastVisibleItemClass: "last", + dropdownMenuProps: TOOLSPANEL_DROPDOWNMENU_PROPS + }, (0,external_React_.createElement)("div", { + className: "color-block-support-panel__inner-wrapper" + }, children)); +} +const color_panel_DEFAULT_CONTROLS = { + text: true, + background: true, + link: true, + heading: true, + button: true, + caption: true +}; +const popoverProps = { + placement: 'left-start', + offset: 36, + shift: true +}; +const LabeledColorIndicators = ({ + indicators, + label +}) => (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalHStack, { + justify: "flex-start" +}, (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalZStack, { + isLayered: false, + offset: -8 +}, indicators.map((indicator, index) => (0,external_React_.createElement)(external_wp_components_namespaceObject.Flex, { + key: index, + expanded: false +}, (0,external_React_.createElement)(external_wp_components_namespaceObject.ColorIndicator, { + colorValue: indicator +})))), (0,external_React_.createElement)(external_wp_components_namespaceObject.FlexItem, { + className: "block-editor-panel-color-gradient-settings__color-name", + title: label +}, label)); +function ColorPanelTab({ + isGradient, + inheritedValue, + userValue, + setValue, + colorGradientControlSettings +}) { + return (0,external_React_.createElement)(control, { + ...colorGradientControlSettings, + showTitle: false, + enableAlpha: true, + __experimentalIsRenderedInSidebar: true, + colorValue: isGradient ? undefined : inheritedValue, + gradientValue: isGradient ? inheritedValue : undefined, + onColorChange: isGradient ? undefined : setValue, + onGradientChange: isGradient ? setValue : undefined, + clearable: inheritedValue === userValue, + headingLevel: 3 + }); +} +function ColorPanelDropdown({ + label, + hasValue, + resetValue, + isShownByDefault, + indicators, + tabs, + colorGradientControlSettings, + panelId +}) { + const currentTab = tabs.find(tab => tab.userValue !== undefined); + // Unlocking `Tabs` too early causes the `unlock` method to receive an empty + // object, due to circular dependencies. + // See https://github.com/WordPress/gutenberg/issues/52692 + const { + Tabs + } = unlock(external_wp_components_namespaceObject.privateApis); + return (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalToolsPanelItem, { + className: "block-editor-tools-panel-color-gradient-settings__item", + hasValue: hasValue, + label: label, + onDeselect: resetValue, + isShownByDefault: isShownByDefault, + panelId: panelId + }, (0,external_React_.createElement)(external_wp_components_namespaceObject.Dropdown, { + popoverProps: popoverProps, + className: "block-editor-tools-panel-color-gradient-settings__dropdown", + renderToggle: ({ + onToggle, + isOpen + }) => { + const toggleProps = { + onClick: onToggle, + className: classnames_default()('block-editor-panel-color-gradient-settings__dropdown', { + 'is-open': isOpen + }), + 'aria-expanded': isOpen, + 'aria-label': (0,external_wp_i18n_namespaceObject.sprintf)( /* translators: %s is the type of color property, e.g., "background" */ + (0,external_wp_i18n_namespaceObject.__)('Color %s styles'), label) }; - if (index === -1) { - _styles.push(overrideWithId); - } else { - _styles[index] = overrideWithId; - } + return (0,external_React_.createElement)(external_wp_components_namespaceObject.Button, { + ...toggleProps + }, (0,external_React_.createElement)(LabeledColorIndicators, { + indicators: indicators, + label: label + })); + }, + renderContent: () => (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalDropdownContentWrapper, { + paddingSize: "none" + }, (0,external_React_.createElement)("div", { + className: "block-editor-panel-color-gradient-settings__dropdown-content" + }, tabs.length === 1 && (0,external_React_.createElement)(ColorPanelTab, { + ...tabs[0], + colorGradientControlSettings: colorGradientControlSettings + }), tabs.length > 1 && (0,external_React_.createElement)(Tabs, { + initialTabId: currentTab?.key + }, (0,external_React_.createElement)(Tabs.TabList, null, tabs.map(tab => (0,external_React_.createElement)(Tabs.Tab, { + key: tab.key, + tabId: tab.key + }, tab.label))), tabs.map(tab => { + return (0,external_React_.createElement)(Tabs.TabPanel, { + key: tab.key, + tabId: tab.key, + focusable: false + }, (0,external_React_.createElement)(ColorPanelTab, { + ...tab, + colorGradientControlSettings: colorGradientControlSettings + })); + })))) + })); +} +function ColorPanel({ + as: Wrapper = ColorToolsPanel, + value, + onChange, + inheritedValue = value, + settings, + panelId, + defaultControls = color_panel_DEFAULT_CONTROLS, + children +}) { + const colors = useColorsPerOrigin(settings); + const gradients = useGradientsPerOrigin(settings); + const areCustomSolidsEnabled = settings?.color?.custom; + const areCustomGradientsEnabled = settings?.color?.customGradient; + const hasSolidColors = colors.length > 0 || areCustomSolidsEnabled; + const hasGradientColors = gradients.length > 0 || areCustomGradientsEnabled; + const decodeValue = rawValue => getValueFromVariable({ + settings + }, '', rawValue); + const encodeColorValue = colorValue => { + const allColors = colors.flatMap(({ + colors: originColors + }) => originColors); + const colorObject = allColors.find(({ + color + }) => color === colorValue); + return colorObject ? 'var:preset|color|' + colorObject.slug : colorValue; + }; + const encodeGradientValue = gradientValue => { + const allGradients = gradients.flatMap(({ + gradients: originGradients + }) => originGradients); + const gradientObject = allGradients.find(({ + gradient + }) => gradient === gradientValue); + return gradientObject ? 'var:preset|gradient|' + gradientObject.slug : gradientValue; + }; + + // BackgroundColor + const showBackgroundPanel = useHasBackgroundPanel(settings); + const backgroundColor = decodeValue(inheritedValue?.color?.background); + const userBackgroundColor = decodeValue(value?.color?.background); + const gradient = decodeValue(inheritedValue?.color?.gradient); + const userGradient = decodeValue(value?.color?.gradient); + const hasBackground = () => !!userBackgroundColor || !!userGradient; + const setBackgroundColor = newColor => { + const newValue = setImmutably(value, ['color', 'background'], encodeColorValue(newColor)); + newValue.color.gradient = undefined; + onChange(newValue); + }; + const setGradient = newGradient => { + const newValue = setImmutably(value, ['color', 'gradient'], encodeGradientValue(newGradient)); + newValue.color.background = undefined; + onChange(newValue); + }; + const resetBackground = () => { + const newValue = setImmutably(value, ['color', 'background'], undefined); + newValue.color.gradient = undefined; + onChange(newValue); + }; + + // Links + const showLinkPanel = useHasLinkPanel(settings); + const linkColor = decodeValue(inheritedValue?.elements?.link?.color?.text); + const userLinkColor = decodeValue(value?.elements?.link?.color?.text); + const setLinkColor = newColor => { + onChange(setImmutably(value, ['elements', 'link', 'color', 'text'], encodeColorValue(newColor))); + }; + const hoverLinkColor = decodeValue(inheritedValue?.elements?.link?.[':hover']?.color?.text); + const userHoverLinkColor = decodeValue(value?.elements?.link?.[':hover']?.color?.text); + const setHoverLinkColor = newColor => { + onChange(setImmutably(value, ['elements', 'link', ':hover', 'color', 'text'], encodeColorValue(newColor))); + }; + const hasLink = () => !!userLinkColor || !!userHoverLinkColor; + const resetLink = () => { + let newValue = setImmutably(value, ['elements', 'link', ':hover', 'color', 'text'], undefined); + newValue = setImmutably(newValue, ['elements', 'link', 'color', 'text'], undefined); + onChange(newValue); + }; + + // Text Color + const showTextPanel = useHasTextPanel(settings); + const textColor = decodeValue(inheritedValue?.color?.text); + const userTextColor = decodeValue(value?.color?.text); + const hasTextColor = () => !!userTextColor; + const setTextColor = newColor => { + let changedObject = setImmutably(value, ['color', 'text'], encodeColorValue(newColor)); + if (textColor === linkColor) { + changedObject = setImmutably(changedObject, ['elements', 'link', 'color', 'text'], encodeColorValue(newColor)); } - return [transform_styles(_styles.filter(style => style?.css), scope), _styles.filter(style => style.__unstableType === 'svgs').map(style => style.assets).join('')]; - }, [styles, overrides, scope]); - return (0,external_wp_element_namespaceObject.createElement)(external_wp_element_namespaceObject.Fragment, null, (0,external_wp_element_namespaceObject.createElement)("style", { - ref: useDarkThemeBodyClassName(transformedStyles, scope) - }), transformedStyles.map((css, index) => (0,external_wp_element_namespaceObject.createElement)("style", { - key: index - }, css)), (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.SVG, { - xmlns: "http://www.w3.org/2000/svg", - viewBox: "0 0 0 0", - width: "0", - height: "0", - role: "none", - style: { - visibility: 'hidden', - position: 'absolute', - left: '-9999px', - overflow: 'hidden' + onChange(changedObject); + }; + const resetTextColor = () => setTextColor(undefined); + + // Elements + const elements = [{ + name: 'caption', + label: (0,external_wp_i18n_namespaceObject.__)('Captions'), + showPanel: useHasCaptionPanel(settings) + }, { + name: 'button', + label: (0,external_wp_i18n_namespaceObject.__)('Button'), + showPanel: useHasButtonPanel(settings) + }, { + name: 'heading', + label: (0,external_wp_i18n_namespaceObject.__)('Heading'), + showPanel: useHasHeadingPanel(settings) + }, { + name: 'h1', + label: (0,external_wp_i18n_namespaceObject.__)('H1'), + showPanel: useHasHeadingPanel(settings) + }, { + name: 'h2', + label: (0,external_wp_i18n_namespaceObject.__)('H2'), + showPanel: useHasHeadingPanel(settings) + }, { + name: 'h3', + label: (0,external_wp_i18n_namespaceObject.__)('H3'), + showPanel: useHasHeadingPanel(settings) + }, { + name: 'h4', + label: (0,external_wp_i18n_namespaceObject.__)('H4'), + showPanel: useHasHeadingPanel(settings) + }, { + name: 'h5', + label: (0,external_wp_i18n_namespaceObject.__)('H5'), + showPanel: useHasHeadingPanel(settings) + }, { + name: 'h6', + label: (0,external_wp_i18n_namespaceObject.__)('H6'), + showPanel: useHasHeadingPanel(settings) + }]; + const resetAllFilter = (0,external_wp_element_namespaceObject.useCallback)(previousValue => { + return { + ...previousValue, + color: undefined, + elements: { + ...previousValue?.elements, + link: { + ...previousValue?.elements?.link, + color: undefined, + ':hover': { + color: undefined + } + }, + ...elements.reduce((acc, element) => { + return { + ...acc, + [element.name]: { + ...previousValue?.elements?.[element.name], + color: undefined + } + }; + }, {}) + } + }; + }, []); + const items = [showTextPanel && { + key: 'text', + label: (0,external_wp_i18n_namespaceObject.__)('Text'), + hasValue: hasTextColor, + resetValue: resetTextColor, + isShownByDefault: defaultControls.text, + indicators: [textColor], + tabs: [{ + key: 'text', + label: (0,external_wp_i18n_namespaceObject.__)('Text'), + inheritedValue: textColor, + setValue: setTextColor, + userValue: userTextColor + }] + }, showBackgroundPanel && { + key: 'background', + label: (0,external_wp_i18n_namespaceObject.__)('Background'), + hasValue: hasBackground, + resetValue: resetBackground, + isShownByDefault: defaultControls.background, + indicators: [gradient !== null && gradient !== void 0 ? gradient : backgroundColor], + tabs: [hasSolidColors && { + key: 'background', + label: (0,external_wp_i18n_namespaceObject.__)('Solid'), + inheritedValue: backgroundColor, + setValue: setBackgroundColor, + userValue: userBackgroundColor + }, hasGradientColors && { + key: 'gradient', + label: (0,external_wp_i18n_namespaceObject.__)('Gradient'), + inheritedValue: gradient, + setValue: setGradient, + userValue: userGradient, + isGradient: true + }].filter(Boolean) + }, showLinkPanel && { + key: 'link', + label: (0,external_wp_i18n_namespaceObject.__)('Link'), + hasValue: hasLink, + resetValue: resetLink, + isShownByDefault: defaultControls.link, + indicators: [linkColor, hoverLinkColor], + tabs: [{ + key: 'link', + label: (0,external_wp_i18n_namespaceObject.__)('Default'), + inheritedValue: linkColor, + setValue: setLinkColor, + userValue: userLinkColor + }, { + key: 'hover', + label: (0,external_wp_i18n_namespaceObject.__)('Hover'), + inheritedValue: hoverLinkColor, + setValue: setHoverLinkColor, + userValue: userHoverLinkColor + }] + }].filter(Boolean); + elements.forEach(({ + name, + label, + showPanel + }) => { + if (!showPanel) return; + const elementBackgroundColor = decodeValue(inheritedValue?.elements?.[name]?.color?.background); + const elementGradient = decodeValue(inheritedValue?.elements?.[name]?.color?.gradient); + const elementTextColor = decodeValue(inheritedValue?.elements?.[name]?.color?.text); + const elementBackgroundUserColor = decodeValue(value?.elements?.[name]?.color?.background); + const elementGradientUserColor = decodeValue(value?.elements?.[name]?.color?.gradient); + const elementTextUserColor = decodeValue(value?.elements?.[name]?.color?.text); + const hasElement = () => !!(elementTextUserColor || elementBackgroundUserColor || elementGradientUserColor); + const resetElement = () => { + const newValue = setImmutably(value, ['elements', name, 'color', 'background'], undefined); + newValue.elements[name].color.gradient = undefined; + newValue.elements[name].color.text = undefined; + onChange(newValue); + }; + const setElementTextColor = newTextColor => { + onChange(setImmutably(value, ['elements', name, 'color', 'text'], encodeColorValue(newTextColor))); + }; + const setElementBackgroundColor = newBackgroundColor => { + const newValue = setImmutably(value, ['elements', name, 'color', 'background'], encodeColorValue(newBackgroundColor)); + newValue.elements[name].color.gradient = undefined; + onChange(newValue); + }; + const setElementGradient = newGradient => { + const newValue = setImmutably(value, ['elements', name, 'color', 'gradient'], encodeGradientValue(newGradient)); + newValue.elements[name].color.background = undefined; + onChange(newValue); + }; + const supportsTextColor = true; + // Background color is not supported for `caption` + // as there isn't yet a way to set padding for the element. + const supportsBackground = name !== 'caption'; + items.push({ + key: name, + label, + hasValue: hasElement, + resetValue: resetElement, + isShownByDefault: defaultControls[name], + indicators: supportsTextColor && supportsBackground ? [elementTextColor, elementGradient !== null && elementGradient !== void 0 ? elementGradient : elementBackgroundColor] : [supportsTextColor ? elementTextColor : elementGradient !== null && elementGradient !== void 0 ? elementGradient : elementBackgroundColor], + tabs: [hasSolidColors && supportsTextColor && { + key: 'text', + label: (0,external_wp_i18n_namespaceObject.__)('Text'), + inheritedValue: elementTextColor, + setValue: setElementTextColor, + userValue: elementTextUserColor + }, hasSolidColors && supportsBackground && { + key: 'background', + label: (0,external_wp_i18n_namespaceObject.__)('Background'), + inheritedValue: elementBackgroundColor, + setValue: setElementBackgroundColor, + userValue: elementBackgroundUserColor + }, hasGradientColors && supportsBackground && { + key: 'gradient', + label: (0,external_wp_i18n_namespaceObject.__)('Gradient'), + inheritedValue: elementGradient, + setValue: setElementGradient, + userValue: elementGradientUserColor, + isGradient: true + }].filter(Boolean) + }); + }); + return (0,external_React_.createElement)(Wrapper, { + resetAllFilter: resetAllFilter, + value: value, + onChange: onChange, + panelId: panelId + }, items.map(item => (0,external_React_.createElement)(ColorPanelDropdown, { + key: item.key, + ...item, + colorGradientControlSettings: { + colors, + disableCustomColors: !areCustomSolidsEnabled, + gradients, + disableCustomGradients: !areCustomGradientsEnabled }, - dangerouslySetInnerHTML: { - __html: transformedSvgs - } - })); + panelId: panelId + })), children); } -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/block-preview/auto.js +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/contrast-checker/index.js /** - * WordPress dependencies + * External dependencies */ - /** - * Internal dependencies + * WordPress dependencies */ - - -// This is used to avoid rendering the block list if the sizes change. -let MemoizedBlockList; -const MAX_HEIGHT = 2000; -function ScaledBlockPreview({ - viewportWidth, - containerWidth, - minHeight, - additionalStyles = [] +k([names, a11y]); +function ContrastChecker({ + backgroundColor, + fallbackBackgroundColor, + fallbackTextColor, + fallbackLinkColor, + fontSize, + // Font size value in pixels. + isLargeText, + textColor, + linkColor, + enableAlphaChecker = false }) { - if (!viewportWidth) { - viewportWidth = containerWidth; + const currentBackgroundColor = backgroundColor || fallbackBackgroundColor; + + // Must have a background color. + if (!currentBackgroundColor) { + return null; } - const [contentResizeListener, { - height: contentHeight - }] = (0,external_wp_compose_namespaceObject.useResizeObserver)(); - const { - styles - } = (0,external_wp_data_namespaceObject.useSelect)(select => { - const settings = select(store).getSettings(); - return { - styles: settings.styles - }; - }, []); + const currentTextColor = textColor || fallbackTextColor; + const currentLinkColor = linkColor || fallbackLinkColor; - // Avoid scrollbars for pattern previews. - const editorStyles = (0,external_wp_element_namespaceObject.useMemo)(() => { - if (styles) { - return [...styles, { - css: 'body{height:auto;overflow:hidden;border:none;padding:0;}', - __unstableType: 'presets' - }, ...additionalStyles]; + // Must have at least one text color. + if (!currentTextColor && !currentLinkColor) { + return null; + } + const textColors = [{ + color: currentTextColor, + description: (0,external_wp_i18n_namespaceObject.__)('text color') + }, { + color: currentLinkColor, + description: (0,external_wp_i18n_namespaceObject.__)('link color') + }]; + const colordBackgroundColor = w(currentBackgroundColor); + const backgroundColorHasTransparency = colordBackgroundColor.alpha() < 1; + const backgroundColorBrightness = colordBackgroundColor.brightness(); + const isReadableOptions = { + level: 'AA', + size: isLargeText || isLargeText !== false && fontSize >= 24 ? 'large' : 'small' + }; + let message = ''; + let speakMessage = ''; + for (const item of textColors) { + // If there is no color, go no further. + if (!item.color) { + continue; } - return styles; - }, [styles, additionalStyles]); + const colordTextColor = w(item.color); + const isColordTextReadable = colordTextColor.isReadable(colordBackgroundColor, isReadableOptions); + const textHasTransparency = colordTextColor.alpha() < 1; - // Initialize on render instead of module top level, to avoid circular dependency issues. - MemoizedBlockList = MemoizedBlockList || (0,external_wp_compose_namespaceObject.pure)(BlockList); - const scale = containerWidth / viewportWidth; - const aspectRatio = contentHeight ? containerWidth / (contentHeight * scale) : 0; - return (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.Disabled, { - className: "block-editor-block-preview__content", - style: { - transform: `scale(${scale})`, - // Using width + aspect-ratio instead of height here triggers browsers' native - // handling of scrollbar's visibility. It prevents the flickering issue seen - // in https://github.com/WordPress/gutenberg/issues/52027. - // See https://github.com/WordPress/gutenberg/pull/52921 for more info. - aspectRatio, - maxHeight: contentHeight > MAX_HEIGHT ? MAX_HEIGHT * scale : undefined, - minHeight + // If the contrast is not readable. + if (!isColordTextReadable) { + // Don't show the message if the background or text is transparent. + if (backgroundColorHasTransparency || textHasTransparency) { + continue; + } + message = backgroundColorBrightness < colordTextColor.brightness() ? (0,external_wp_i18n_namespaceObject.sprintf)( + // translators: %s is a type of text color, e.g., "text color" or "link color". + (0,external_wp_i18n_namespaceObject.__)('This color combination may be hard for people to read. Try using a darker background color and/or a brighter %s.'), item.description) : (0,external_wp_i18n_namespaceObject.sprintf)( + // translators: %s is a type of text color, e.g., "text color" or "link color". + (0,external_wp_i18n_namespaceObject.__)('This color combination may be hard for people to read. Try using a brighter background color and/or a darker %s.'), item.description); + speakMessage = (0,external_wp_i18n_namespaceObject.__)('This color combination may be hard for people to read.'); + // Break from the loop when we have a contrast warning. + // These messages take priority over the transparency warning. + break; } - }, (0,external_wp_element_namespaceObject.createElement)(iframe, { - contentRef: (0,external_wp_compose_namespaceObject.useRefEffect)(bodyElement => { - const { - ownerDocument: { - documentElement - } - } = bodyElement; - documentElement.classList.add('block-editor-block-preview__content-iframe'); - documentElement.style.position = 'absolute'; - documentElement.style.width = '100%'; - // Necessary for contentResizeListener to work. - bodyElement.style.boxSizing = 'border-box'; - bodyElement.style.position = 'absolute'; - bodyElement.style.width = '100%'; - }, []), - "aria-hidden": true, - tabIndex: -1, - style: { - position: 'absolute', - width: viewportWidth, - height: contentHeight, - pointerEvents: 'none', - // This is a catch-all max-height for patterns. - // See: https://github.com/WordPress/gutenberg/pull/38175. - maxHeight: MAX_HEIGHT, - minHeight: scale !== 0 && scale < 1 && minHeight ? minHeight / scale : minHeight - } - }, (0,external_wp_element_namespaceObject.createElement)(EditorStyles, { - styles: editorStyles - }), contentResizeListener, (0,external_wp_element_namespaceObject.createElement)(MemoizedBlockList, { - renderAppender: false - }))); -} -function AutoBlockPreview(props) { - const [containerResizeListener, { - width: containerWidth - }] = (0,external_wp_compose_namespaceObject.useResizeObserver)(); - return (0,external_wp_element_namespaceObject.createElement)(external_wp_element_namespaceObject.Fragment, null, (0,external_wp_element_namespaceObject.createElement)("div", { - style: { - position: 'relative', - width: '100%', - height: 0 + // If there is no contrast warning and the text is transparent, + // show the transparent warning if alpha check is enabled. + if (textHasTransparency && enableAlphaChecker) { + message = (0,external_wp_i18n_namespaceObject.__)('Transparent text may be hard for people to read.'); + speakMessage = (0,external_wp_i18n_namespaceObject.__)('Transparent text may be hard for people to read.'); } - }, containerResizeListener), (0,external_wp_element_namespaceObject.createElement)("div", { - className: "block-editor-block-preview__container" - }, !!containerWidth && (0,external_wp_element_namespaceObject.createElement)(ScaledBlockPreview, { - ...props, - containerWidth: containerWidth - }))); -} - -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/block-preview/index.js - -/** - * External dependencies - */ - - -/** - * WordPress dependencies - */ - - - - - -/** - * Internal dependencies - */ - - - - - -function BlockPreview({ - blocks, - viewportWidth = 1200, - minHeight, - additionalStyles = [], - // Deprecated props: - __experimentalMinHeight, - __experimentalPadding -}) { - if (__experimentalMinHeight) { - minHeight = __experimentalMinHeight; - external_wp_deprecated_default()('The __experimentalMinHeight prop', { - since: '6.2', - version: '6.4', - alternative: 'minHeight' - }); - } - if (__experimentalPadding) { - additionalStyles = [...additionalStyles, { - css: `body { padding: ${__experimentalPadding}px; }` - }]; - external_wp_deprecated_default()('The __experimentalPadding prop of BlockPreview', { - since: '6.2', - version: '6.4', - alternative: 'additionalStyles' - }); } - const originalSettings = (0,external_wp_data_namespaceObject.useSelect)(select => select(store).getSettings(), []); - const settings = (0,external_wp_element_namespaceObject.useMemo)(() => ({ - ...originalSettings, - __unstableIsPreviewMode: true - }), [originalSettings]); - const renderedBlocks = (0,external_wp_element_namespaceObject.useMemo)(() => Array.isArray(blocks) ? blocks : [blocks], [blocks]); - if (!blocks || blocks.length === 0) { + if (!message) { return null; } - return (0,external_wp_element_namespaceObject.createElement)(ExperimentalBlockEditorProvider, { - value: renderedBlocks, - settings: settings - }, (0,external_wp_element_namespaceObject.createElement)(AutoBlockPreview, { - viewportWidth: viewportWidth, - minHeight: minHeight, - additionalStyles: additionalStyles - })); -} - -/** - * BlockPreview renders a preview of a block or array of blocks. - * - * @see https://github.com/WordPress/gutenberg/blob/HEAD/packages/block-editor/src/components/block-preview/README.md - * - * @param {Object} preview options for how the preview should be shown - * @param {Array|Object} preview.blocks A block instance (object) or an array of blocks to be previewed. - * @param {number} preview.viewportWidth Width of the preview container in pixels. Controls at what size the blocks will be rendered inside the preview. Default: 700. - * - * @return {WPComponent} The component to be rendered. - */ -/* harmony default export */ var block_preview = ((0,external_wp_element_namespaceObject.memo)(BlockPreview)); - -/** - * This hook is used to lightly mark an element as a block preview wrapper - * element. Call this hook and pass the returned props to the element to mark as - * a block preview wrapper, automatically rendering inner blocks as children. If - * you define a ref for the element, it is important to pass the ref to this - * hook, which the hook in turn will pass to the component through the props it - * returns. Optionally, you can also pass any other props through this hook, and - * they will be merged and returned. - * - * @param {Object} options Preview options. - * @param {WPBlock[]} options.blocks Block objects. - * @param {Object} options.props Optional. Props to pass to the element. Must contain - * the ref if one is defined. - * @param {Object} options.layout Layout settings to be used in the preview. - */ -function useBlockPreview({ - blocks, - props = {}, - layout -}) { - const originalSettings = (0,external_wp_data_namespaceObject.useSelect)(select => select(store).getSettings(), []); - const settings = (0,external_wp_element_namespaceObject.useMemo)(() => ({ - ...originalSettings, - styles: undefined, - // Clear styles included by the parent settings, as they are already output by the parent's EditorStyles. - __unstableIsPreviewMode: true - }), [originalSettings]); - const disabledRef = (0,external_wp_compose_namespaceObject.useDisabled)(); - const ref = (0,external_wp_compose_namespaceObject.useMergeRefs)([props.ref, disabledRef]); - const renderedBlocks = (0,external_wp_element_namespaceObject.useMemo)(() => Array.isArray(blocks) ? blocks : [blocks], [blocks]); - const children = (0,external_wp_element_namespaceObject.createElement)(ExperimentalBlockEditorProvider, { - value: renderedBlocks, - settings: settings - }, (0,external_wp_element_namespaceObject.createElement)(EditorStyles, null), (0,external_wp_element_namespaceObject.createElement)(BlockListItems, { - renderAppender: false, - layout: layout - })); - return { - ...props, - ref, - className: classnames_default()(props.className, 'block-editor-block-preview__live-content', 'components-disabled'), - children: blocks?.length ? children : null - }; -} - -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/inserter/preview-panel.js - -/** - * WordPress dependencies - */ - - - - -/** - * Internal dependencies - */ - -function InserterPreviewPanel({ - item -}) { - var _example$viewportWidt; - const { - name, - title, - icon, - description, - initialAttributes, - example - } = item; - const isReusable = (0,external_wp_blocks_namespaceObject.isReusableBlock)(item); - const blocks = (0,external_wp_element_namespaceObject.useMemo)(() => { - if (!example) { - return (0,external_wp_blocks_namespaceObject.createBlock)(name, initialAttributes); - } - return (0,external_wp_blocks_namespaceObject.getBlockFromExample)(name, { - attributes: { - ...example.attributes, - ...initialAttributes - }, - innerBlocks: example.innerBlocks - }); - }, [name, example, initialAttributes]); - return (0,external_wp_element_namespaceObject.createElement)("div", { - className: "block-editor-inserter__preview-container" - }, (0,external_wp_element_namespaceObject.createElement)("div", { - className: "block-editor-inserter__preview" - }, isReusable || example ? (0,external_wp_element_namespaceObject.createElement)("div", { - className: "block-editor-inserter__preview-content" - }, (0,external_wp_element_namespaceObject.createElement)(block_preview, { - blocks: blocks, - viewportWidth: (_example$viewportWidt = example?.viewportWidth) !== null && _example$viewportWidt !== void 0 ? _example$viewportWidt : 500, - additionalStyles: [{ - css: 'body { padding: 16px; }' - }] - })) : (0,external_wp_element_namespaceObject.createElement)("div", { - className: "block-editor-inserter__preview-content-missing" - }, (0,external_wp_i18n_namespaceObject.__)('No Preview Available.'))), !isReusable && (0,external_wp_element_namespaceObject.createElement)(block_card, { - title: title, - icon: icon, - description: description - })); + // Note: The `Notice` component can speak messages via its `spokenMessage` + // prop, but the contrast checker requires granular control over when the + // announcements are made. Notably, the message will be re-announced if a + // new color combination is selected and the contrast is still insufficient. + (0,external_wp_a11y_namespaceObject.speak)(speakMessage); + return (0,external_React_.createElement)("div", { + className: "block-editor-contrast-checker" + }, (0,external_React_.createElement)(external_wp_components_namespaceObject.Notice, { + spokenMessage: null, + status: "warning", + isDismissible: false + }, message)); } -/* harmony default export */ var preview_panel = (InserterPreviewPanel); -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/inserter-listbox/context.js /** - * WordPress dependencies + * @see https://github.com/WordPress/gutenberg/blob/HEAD/packages/block-editor/src/components/contrast-checker/README.md */ +/* harmony default export */ const contrast_checker = (ContrastChecker); -const InserterListboxContext = (0,external_wp_element_namespaceObject.createContext)(); -/* harmony default export */ var context = (InserterListboxContext); - -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/inserter-listbox/item.js +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/provider/block-refs-provider.js /** * WordPress dependencies */ - - -/** - * Internal dependencies - */ - -function InserterListboxItem({ - isFirst, - as: Component, - children, - ...props -}, ref) { - const state = (0,external_wp_element_namespaceObject.useContext)(context); - return (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.__unstableCompositeItem, { - ref: ref, - state: state, - role: "option" - // Use the CompositeItem `focusable` prop over Button's - // isFocusable. The latter was shown to cause an issue - // with tab order in the inserter list. - , - focusable: true, - ...props - }, htmlProps => { - const propsWithTabIndex = { - ...htmlProps, - tabIndex: isFirst ? 0 : htmlProps.tabIndex - }; - if (Component) { - return (0,external_wp_element_namespaceObject.createElement)(Component, { - ...propsWithTabIndex - }, children); - } - if (typeof children === 'function') { - return children(propsWithTabIndex); - } - return (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.Button, { - ...propsWithTabIndex - }, children); - }); +const BlockRefs = (0,external_wp_element_namespaceObject.createContext)({ + refs: new Map(), + callbacks: new Map() +}); +function BlockRefsProvider({ + children +}) { + const value = (0,external_wp_element_namespaceObject.useMemo)(() => ({ + refs: new Map(), + callbacks: new Map() + }), []); + return (0,external_React_.createElement)(BlockRefs.Provider, { + value: value + }, children); } -/* harmony default export */ var inserter_listbox_item = ((0,external_wp_element_namespaceObject.forwardRef)(InserterListboxItem)); - -;// CONCATENATED MODULE: ./node_modules/@wordpress/icons/build-module/library/drag-handle.js - -/** - * WordPress dependencies - */ - -const dragHandle = (0,external_wp_element_namespaceObject.createElement)(external_wp_primitives_namespaceObject.SVG, { - width: "24", - height: "24", - xmlns: "http://www.w3.org/2000/svg", - viewBox: "0 0 24 24" -}, (0,external_wp_element_namespaceObject.createElement)(external_wp_primitives_namespaceObject.Path, { - d: "M8 7h2V5H8v2zm0 6h2v-2H8v2zm0 6h2v-2H8v2zm6-14v2h2V5h-2zm0 8h2v-2h-2v2zm0 6h2v-2h-2v2z" -})); -/* harmony default export */ var drag_handle = (dragHandle); - -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/block-draggable/draggable-chip.js +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/block-list/use-block-props/use-block-refs.js /** * WordPress dependencies */ - /** * Internal dependencies */ -function BlockDraggableChip({ - count, - icon, - isPattern -}) { - const patternLabel = isPattern && (0,external_wp_i18n_namespaceObject.__)('Pattern'); - return (0,external_wp_element_namespaceObject.createElement)("div", { - className: "block-editor-block-draggable-chip-wrapper" - }, (0,external_wp_element_namespaceObject.createElement)("div", { - className: "block-editor-block-draggable-chip", - "data-testid": "block-draggable-chip" - }, (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.Flex, { - justify: "center", - className: "block-editor-block-draggable-chip__content" - }, (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.FlexItem, null, icon ? (0,external_wp_element_namespaceObject.createElement)(block_icon, { - icon: icon - }) : patternLabel || (0,external_wp_i18n_namespaceObject.sprintf)( /* translators: %d: Number of blocks. */ - (0,external_wp_i18n_namespaceObject._n)('%d block', '%d blocks', count), count)), (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.FlexItem, null, (0,external_wp_element_namespaceObject.createElement)(block_icon, { - icon: drag_handle - }))))); -} - -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/inserter-draggable-blocks/index.js - -/** - * WordPress dependencies - */ - +/** @typedef {import('@wordpress/element').RefCallback} RefCallback */ +/** @typedef {import('@wordpress/element').RefObject} RefObject */ /** - * Internal dependencies + * Provides a ref to the BlockRefs context. + * + * @param {string} clientId The client ID of the element ref. + * + * @return {RefCallback} Ref callback. */ - -const InserterDraggableBlocks = ({ - isEnabled, - blocks, - icon, - children, - isPattern -}) => { - const transferData = { - type: 'inserter', - blocks - }; - const blockTypeIcon = (0,external_wp_data_namespaceObject.useSelect)(select => { - const { - getBlockType - } = select(external_wp_blocks_namespaceObject.store); - return blocks.length === 1 && getBlockType(blocks[0].name)?.icon; - }, [blocks]); - return (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.Draggable, { - __experimentalTransferDataType: "wp-blocks", - transferData: transferData, - onDragStart: event => { - event.dataTransfer.setData('text/html', (0,external_wp_blocks_namespaceObject.serialize)(blocks)); - }, - __experimentalDragComponent: (0,external_wp_element_namespaceObject.createElement)(BlockDraggableChip, { - count: blocks.length, - icon: icon || !isPattern && blockTypeIcon, - isPattern: isPattern - }) - }, ({ - onDraggableStart, - onDraggableEnd - }) => { - return children({ - draggable: isEnabled, - onDragStart: isEnabled ? onDraggableStart : undefined, - onDragEnd: isEnabled ? onDraggableEnd : undefined +function useBlockRefProvider(clientId) { + const { + refs, + callbacks + } = (0,external_wp_element_namespaceObject.useContext)(BlockRefs); + const ref = (0,external_wp_element_namespaceObject.useRef)(); + (0,external_wp_element_namespaceObject.useLayoutEffect)(() => { + refs.set(ref, clientId); + return () => { + refs.delete(ref); + }; + }, [clientId]); + return (0,external_wp_compose_namespaceObject.useRefEffect)(element => { + // Update the ref in the provider. + ref.current = element; + // Call any update functions. + callbacks.forEach((id, setElement) => { + if (clientId === id) { + setElement(element); + } }); - }); -}; -/* harmony default export */ var inserter_draggable_blocks = (InserterDraggableBlocks); - -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/inserter-list-item/index.js + }, [clientId]); +} /** - * External dependencies + * Gets a ref pointing to the current block element. Continues to return a + * stable ref even if the block client ID changes. + * + * @param {string} clientId The client ID to get a ref for. + * + * @return {RefObject} A ref containing the element. */ +function useBlockRef(clientId) { + const { + refs + } = (0,external_wp_element_namespaceObject.useContext)(BlockRefs); + const freshClientId = (0,external_wp_element_namespaceObject.useRef)(); + freshClientId.current = clientId; + // Always return an object, even if no ref exists for a given client ID, so + // that `current` works at a later point. + return (0,external_wp_element_namespaceObject.useMemo)(() => ({ + get current() { + let element = null; + // Multiple refs may be created for a single block. Find the + // first that has an element set. + for (const [ref, id] of refs.entries()) { + if (id === freshClientId.current && ref.current) { + element = ref.current; + } + } + return element; + } + }), []); +} /** - * WordPress dependencies + * Return the element for a given client ID. Updates whenever the element + * changes, becomes available, or disappears. + * + * @param {string} clientId The client ID to an element for. + * + * @return {Element|null} The block's wrapper element. */ +function useBlockElement(clientId) { + const { + callbacks + } = (0,external_wp_element_namespaceObject.useContext)(BlockRefs); + const ref = useBlockRef(clientId); + const [element, setElement] = (0,external_wp_element_namespaceObject.useState)(null); + (0,external_wp_element_namespaceObject.useLayoutEffect)(() => { + if (!clientId) { + return; + } + callbacks.set(setElement, clientId); + return () => { + callbacks.delete(setElement); + }; + }, [clientId]); + return ref.current || element; +} - +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/hooks/contrast-checker.js /** - * Internal dependencies + * WordPress dependencies */ - -function InserterListItem({ - className, - isFirst, - item, - onSelect, - onHover, - isDraggable, - ...props -}) { - const isDragging = (0,external_wp_element_namespaceObject.useRef)(false); - const itemIconStyle = item.icon ? { - backgroundColor: item.icon.background, - color: item.icon.foreground - } : {}; - const blocks = (0,external_wp_element_namespaceObject.useMemo)(() => { - return [(0,external_wp_blocks_namespaceObject.createBlock)(item.name, item.initialAttributes, (0,external_wp_blocks_namespaceObject.createBlocksFromInnerBlocksTemplate)(item.innerBlocks))]; - }, [item.name, item.initialAttributes, item.initialAttributes]); - const isSynced = (0,external_wp_blocks_namespaceObject.isReusableBlock)(item) && item.syncStatus !== 'unsynced' || (0,external_wp_blocks_namespaceObject.isTemplatePart)(item); - return (0,external_wp_element_namespaceObject.createElement)(inserter_draggable_blocks, { - isEnabled: isDraggable && !item.disabled, - blocks: blocks, - icon: item.icon - }, ({ - draggable, - onDragStart, - onDragEnd - }) => (0,external_wp_element_namespaceObject.createElement)("div", { - className: classnames_default()('block-editor-block-types-list__list-item', { - 'is-synced': isSynced - }), - draggable: draggable, - onDragStart: event => { - isDragging.current = true; - if (onDragStart) { - onHover(null); - onDragStart(event); - } - }, - onDragEnd: event => { - isDragging.current = false; - if (onDragEnd) { - onDragEnd(event); - } - } - }, (0,external_wp_element_namespaceObject.createElement)(inserter_listbox_item, { - isFirst: isFirst, - className: classnames_default()('block-editor-block-types-list__item', className), - disabled: item.isDisabled, - onClick: event => { - event.preventDefault(); - onSelect(item, (0,external_wp_keycodes_namespaceObject.isAppleOS)() ? event.metaKey : event.ctrlKey); - onHover(null); - }, - onKeyDown: event => { - const { - keyCode - } = event; - if (keyCode === external_wp_keycodes_namespaceObject.ENTER) { - event.preventDefault(); - onSelect(item, (0,external_wp_keycodes_namespaceObject.isAppleOS)() ? event.metaKey : event.ctrlKey); - onHover(null); - } - }, - onMouseEnter: () => { - if (isDragging.current) { - return; - } - onHover(item); - }, - onMouseLeave: () => onHover(null), - ...props - }, (0,external_wp_element_namespaceObject.createElement)("span", { - className: "block-editor-block-types-list__item-icon", - style: itemIconStyle - }, (0,external_wp_element_namespaceObject.createElement)(block_icon, { - icon: item.icon, - showColors: true - })), (0,external_wp_element_namespaceObject.createElement)("span", { - className: "block-editor-block-types-list__item-title" - }, (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.__experimentalTruncate, { - numberOfLines: 3 - }, item.title))))); -} -/* harmony default export */ var inserter_list_item = ((0,external_wp_element_namespaceObject.memo)(InserterListItem)); - -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/inserter-listbox/group.js - /** - * WordPress dependencies + * Internal dependencies */ +function getComputedStyle(node) { + return node.ownerDocument.defaultView.getComputedStyle(node); +} +function BlockColorContrastChecker({ + clientId +}) { + const [detectedBackgroundColor, setDetectedBackgroundColor] = (0,external_wp_element_namespaceObject.useState)(); + const [detectedColor, setDetectedColor] = (0,external_wp_element_namespaceObject.useState)(); + const [detectedLinkColor, setDetectedLinkColor] = (0,external_wp_element_namespaceObject.useState)(); + const ref = useBlockRef(clientId); -function InserterListboxGroup(props, ref) { - const [shouldSpeak, setShouldSpeak] = (0,external_wp_element_namespaceObject.useState)(false); + // There are so many things that can change the color of a block + // So we perform this check on every render. + // eslint-disable-next-line react-hooks/exhaustive-deps (0,external_wp_element_namespaceObject.useEffect)(() => { - if (shouldSpeak) { - (0,external_wp_a11y_namespaceObject.speak)((0,external_wp_i18n_namespaceObject.__)('Use left and right arrow keys to move through blocks')); + if (!ref.current) { + return; } - }, [shouldSpeak]); - return (0,external_wp_element_namespaceObject.createElement)("div", { - ref: ref, - role: "listbox", - "aria-orientation": "horizontal", - onFocus: () => { - setShouldSpeak(true); - }, - onBlur: event => { - const focusingOutsideGroup = !event.currentTarget.contains(event.relatedTarget); - if (focusingOutsideGroup) { - setShouldSpeak(false); - } - }, - ...props + setDetectedColor(getComputedStyle(ref.current).color); + const firstLinkElement = ref.current?.querySelector('a'); + if (firstLinkElement && !!firstLinkElement.innerText) { + setDetectedLinkColor(getComputedStyle(firstLinkElement).color); + } + let backgroundColorNode = ref.current; + let backgroundColor = getComputedStyle(backgroundColorNode).backgroundColor; + while (backgroundColor === 'rgba(0, 0, 0, 0)' && backgroundColorNode.parentNode && backgroundColorNode.parentNode.nodeType === backgroundColorNode.parentNode.ELEMENT_NODE) { + backgroundColorNode = backgroundColorNode.parentNode; + backgroundColor = getComputedStyle(backgroundColorNode).backgroundColor; + } + setDetectedBackgroundColor(backgroundColor); + }); + return (0,external_React_.createElement)(contrast_checker, { + backgroundColor: detectedBackgroundColor, + textColor: detectedColor, + enableAlphaChecker: true, + linkColor: detectedLinkColor }); } -/* harmony default export */ var group = ((0,external_wp_element_namespaceObject.forwardRef)(InserterListboxGroup)); -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/inserter-listbox/row.js +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/hooks/color.js /** - * WordPress dependencies + * External dependencies */ - /** - * Internal dependencies + * WordPress dependencies */ -function InserterListboxRow(props, ref) { - const state = (0,external_wp_element_namespaceObject.useContext)(context); - return (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.__unstableCompositeGroup, { - state: state, - role: "presentation", - ref: ref, - ...props - }); -} -/* harmony default export */ var inserter_listbox_row = ((0,external_wp_element_namespaceObject.forwardRef)(InserterListboxRow)); -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/block-types-list/index.js -/** - * WordPress dependencies - */ /** @@ -22900,144 +26993,336 @@ function InserterListboxRow(props, ref) { */ -function chunk(array, size) { - const chunks = []; - for (let i = 0, j = array.length; i < j; i += size) { - chunks.push(array.slice(i, i + size)); - } - return chunks; -} -function BlockTypesList({ - items = [], - onSelect, - onHover = () => {}, - children, - label, - isDraggable = true -}) { - return (0,external_wp_element_namespaceObject.createElement)(group, { - className: "block-editor-block-types-list", - "aria-label": label - }, chunk(items, 3).map((row, i) => (0,external_wp_element_namespaceObject.createElement)(inserter_listbox_row, { - key: i - }, row.map((item, j) => (0,external_wp_element_namespaceObject.createElement)(inserter_list_item, { - key: item.id, - item: item, - className: (0,external_wp_blocks_namespaceObject.getBlockMenuDefaultClassName)(item.id), - onSelect: onSelect, - onHover: onHover, - isDraggable: isDraggable && !item.isDisabled, - isFirst: i === 0 && j === 0 - })))), children); -} -/* harmony default export */ var block_types_list = (BlockTypesList); -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/inserter/panel.js -/** - * WordPress dependencies - */ -function InserterPanel({ - title, - icon, - children -}) { - return (0,external_wp_element_namespaceObject.createElement)(external_wp_element_namespaceObject.Fragment, null, (0,external_wp_element_namespaceObject.createElement)("div", { - className: "block-editor-inserter__panel-header" - }, (0,external_wp_element_namespaceObject.createElement)("h2", { - className: "block-editor-inserter__panel-title" - }, title), (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.Icon, { - icon: icon - })), (0,external_wp_element_namespaceObject.createElement)("div", { - className: "block-editor-inserter__panel-content" - }, children)); -} -/* harmony default export */ var panel = (InserterPanel); -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/inserter/hooks/use-block-types-state.js -/** - * WordPress dependencies - */ +const COLOR_SUPPORT_KEY = 'color'; +const hasColorSupport = blockNameOrType => { + const colorSupport = (0,external_wp_blocks_namespaceObject.getBlockSupport)(blockNameOrType, COLOR_SUPPORT_KEY); + return colorSupport && (colorSupport.link === true || colorSupport.gradient === true || colorSupport.background !== false || colorSupport.text !== false); +}; +const hasLinkColorSupport = blockType => { + if (external_wp_element_namespaceObject.Platform.OS !== 'web') { + return false; + } + const colorSupport = (0,external_wp_blocks_namespaceObject.getBlockSupport)(blockType, COLOR_SUPPORT_KEY); + return colorSupport !== null && typeof colorSupport === 'object' && !!colorSupport.link; +}; +const hasGradientSupport = blockNameOrType => { + const colorSupport = (0,external_wp_blocks_namespaceObject.getBlockSupport)(blockNameOrType, COLOR_SUPPORT_KEY); + return colorSupport !== null && typeof colorSupport === 'object' && !!colorSupport.gradients; +}; +const hasBackgroundColorSupport = blockType => { + const colorSupport = (0,external_wp_blocks_namespaceObject.getBlockSupport)(blockType, COLOR_SUPPORT_KEY); + return colorSupport && colorSupport.background !== false; +}; +const hasTextColorSupport = blockType => { + const colorSupport = (0,external_wp_blocks_namespaceObject.getBlockSupport)(blockType, COLOR_SUPPORT_KEY); + return colorSupport && colorSupport.text !== false; +}; /** - * Internal dependencies + * Filters registered block settings, extending attributes to include + * `backgroundColor` and `textColor` attribute. + * + * @param {Object} settings Original block settings. + * + * @return {Object} Filtered block settings. */ +function color_addAttributes(settings) { + if (!hasColorSupport(settings)) { + return settings; + } + // Allow blocks to specify their own attribute definition with default values if needed. + if (!settings.attributes.backgroundColor) { + Object.assign(settings.attributes, { + backgroundColor: { + type: 'string' + } + }); + } + if (!settings.attributes.textColor) { + Object.assign(settings.attributes, { + textColor: { + type: 'string' + } + }); + } + if (hasGradientSupport(settings) && !settings.attributes.gradient) { + Object.assign(settings.attributes, { + gradient: { + type: 'string' + } + }); + } + return settings; +} /** - * Retrieves the block types inserter state. + * Override props assigned to save component to inject colors classnames. * - * @param {string=} rootClientId Insertion's root client ID. - * @param {Function} onInsert function called when inserter a list of blocks. - * @return {Array} Returns the block types state. (block types, categories, collections, onSelect handler) + * @param {Object} props Additional props applied to save element. + * @param {Object|string} blockNameOrType Block type. + * @param {Object} attributes Block attributes. + * + * @return {Object} Filtered props applied to save element. */ -const useBlockTypesState = (rootClientId, onInsert) => { +function color_addSaveProps(props, blockNameOrType, attributes) { + if (!hasColorSupport(blockNameOrType) || shouldSkipSerialization(blockNameOrType, COLOR_SUPPORT_KEY)) { + return props; + } + const hasGradient = hasGradientSupport(blockNameOrType); + + // I'd have preferred to avoid the "style" attribute usage here const { - categories, - collections, - items - } = (0,external_wp_data_namespaceObject.useSelect)(select => { - const { - getInserterItems - } = select(store); + backgroundColor, + textColor, + gradient, + style + } = attributes; + const shouldSerialize = feature => !shouldSkipSerialization(blockNameOrType, COLOR_SUPPORT_KEY, feature); + + // Primary color classes must come before the `has-text-color`, + // `has-background` and `has-link-color` classes to maintain backwards + // compatibility and avoid block invalidations. + const textClass = shouldSerialize('text') ? getColorClassName('color', textColor) : undefined; + const gradientClass = shouldSerialize('gradients') ? __experimentalGetGradientClass(gradient) : undefined; + const backgroundClass = shouldSerialize('background') ? getColorClassName('background-color', backgroundColor) : undefined; + const serializeHasBackground = shouldSerialize('background') || shouldSerialize('gradients'); + const hasBackground = backgroundColor || style?.color?.background || hasGradient && (gradient || style?.color?.gradient); + const newClassName = classnames_default()(props.className, textClass, gradientClass, { + // Don't apply the background class if there's a custom gradient. + [backgroundClass]: (!hasGradient || !style?.color?.gradient) && !!backgroundClass, + 'has-text-color': shouldSerialize('text') && (textColor || style?.color?.text), + 'has-background': serializeHasBackground && hasBackground, + 'has-link-color': shouldSerialize('link') && style?.elements?.link?.color + }); + props.className = newClassName ? newClassName : undefined; + return props; +} +function color_styleToAttributes(style) { + const textColorValue = style?.color?.text; + const textColorSlug = textColorValue?.startsWith('var:preset|color|') ? textColorValue.substring('var:preset|color|'.length) : undefined; + const backgroundColorValue = style?.color?.background; + const backgroundColorSlug = backgroundColorValue?.startsWith('var:preset|color|') ? backgroundColorValue.substring('var:preset|color|'.length) : undefined; + const gradientValue = style?.color?.gradient; + const gradientSlug = gradientValue?.startsWith('var:preset|gradient|') ? gradientValue.substring('var:preset|gradient|'.length) : undefined; + const updatedStyle = { + ...style + }; + updatedStyle.color = { + ...updatedStyle.color, + text: textColorSlug ? undefined : textColorValue, + background: backgroundColorSlug ? undefined : backgroundColorValue, + gradient: gradientSlug ? undefined : gradientValue + }; + return { + style: utils_cleanEmptyObject(updatedStyle), + textColor: textColorSlug, + backgroundColor: backgroundColorSlug, + gradient: gradientSlug + }; +} +function color_attributesToStyle(attributes) { + return { + ...attributes.style, + color: { + ...attributes.style?.color, + text: attributes.textColor ? 'var:preset|color|' + attributes.textColor : attributes.style?.color?.text, + background: attributes.backgroundColor ? 'var:preset|color|' + attributes.backgroundColor : attributes.style?.color?.background, + gradient: attributes.gradient ? 'var:preset|gradient|' + attributes.gradient : attributes.style?.color?.gradient + } + }; +} +function ColorInspectorControl({ + children, + resetAllFilter +}) { + const attributesResetAllFilter = (0,external_wp_element_namespaceObject.useCallback)(attributes => { + const existingStyle = color_attributesToStyle(attributes); + const updatedStyle = resetAllFilter(existingStyle); + return { + ...attributes, + ...color_styleToAttributes(updatedStyle) + }; + }, [resetAllFilter]); + return (0,external_React_.createElement)(inspector_controls, { + group: "color", + resetAllFilter: attributesResetAllFilter + }, children); +} +function ColorEdit({ + clientId, + name, + setAttributes, + settings +}) { + const isEnabled = useHasColorPanel(settings); + function selector(select) { const { - getCategories, - getCollections - } = select(external_wp_blocks_namespaceObject.store); + style, + textColor, + backgroundColor, + gradient + } = select(store).getBlockAttributes(clientId) || {}; return { - categories: getCategories(), - collections: getCollections(), - items: getInserterItems(rootClientId) + style, + textColor, + backgroundColor, + gradient }; - }, [rootClientId]); - const onSelectItem = (0,external_wp_element_namespaceObject.useCallback)(({ - name, - initialAttributes, - innerBlocks, - syncStatus, - content - }, shouldFocusBlock) => { - const insertedBlock = syncStatus === 'unsynced' ? (0,external_wp_blocks_namespaceObject.parse)(content, { - __unstableSkipMigrationLogs: true - }) : (0,external_wp_blocks_namespaceObject.createBlock)(name, initialAttributes, (0,external_wp_blocks_namespaceObject.createBlocksFromInnerBlocksTemplate)(innerBlocks)); - onInsert(insertedBlock, undefined, shouldFocusBlock); - }, [onInsert]); - return [items, categories, collections, onSelectItem]; + } + const { + style, + textColor, + backgroundColor, + gradient + } = (0,external_wp_data_namespaceObject.useSelect)(selector, [clientId]); + const value = (0,external_wp_element_namespaceObject.useMemo)(() => { + return color_attributesToStyle({ + style, + textColor, + backgroundColor, + gradient + }); + }, [style, textColor, backgroundColor, gradient]); + const onChange = newStyle => { + setAttributes(color_styleToAttributes(newStyle)); + }; + if (!isEnabled) { + return null; + } + const defaultControls = (0,external_wp_blocks_namespaceObject.getBlockSupport)(name, [COLOR_SUPPORT_KEY, '__experimentalDefaultControls']); + const enableContrastChecking = external_wp_element_namespaceObject.Platform.OS === 'web' && !value?.color?.gradient && (settings?.color?.text || settings?.color?.link) && + // Contrast checking is enabled by default. + // Deactivating it requires `enableContrastChecker` to have + // an explicit value of `false`. + false !== (0,external_wp_blocks_namespaceObject.getBlockSupport)(name, [COLOR_SUPPORT_KEY, 'enableContrastChecker']); + return (0,external_React_.createElement)(ColorPanel, { + as: ColorInspectorControl, + panelId: clientId, + settings: settings, + value: value, + onChange: onChange, + defaultControls: defaultControls, + enableContrastChecker: false !== (0,external_wp_blocks_namespaceObject.getBlockSupport)(name, [COLOR_SUPPORT_KEY, 'enableContrastChecker']) + }, enableContrastChecking && (0,external_React_.createElement)(BlockColorContrastChecker, { + clientId: clientId + })); +} +function color_useBlockProps({ + name, + backgroundColor, + textColor, + gradient, + style +}) { + const [userPalette, themePalette, defaultPalette] = use_settings_useSettings('color.palette.custom', 'color.palette.theme', 'color.palette.default'); + const colors = (0,external_wp_element_namespaceObject.useMemo)(() => [...(userPalette || []), ...(themePalette || []), ...(defaultPalette || [])], [userPalette, themePalette, defaultPalette]); + if (!hasColorSupport(name) || shouldSkipSerialization(name, COLOR_SUPPORT_KEY)) { + return {}; + } + const extraStyles = {}; + if (textColor && !shouldSkipSerialization(name, COLOR_SUPPORT_KEY, 'text')) { + extraStyles.color = getColorObjectByAttributeValues(colors, textColor)?.color; + } + if (backgroundColor && !shouldSkipSerialization(name, COLOR_SUPPORT_KEY, 'background')) { + extraStyles.backgroundColor = getColorObjectByAttributeValues(colors, backgroundColor)?.color; + } + const saveProps = color_addSaveProps({ + style: extraStyles + }, name, { + textColor, + backgroundColor, + gradient, + style + }); + const hasBackgroundValue = backgroundColor || style?.color?.background || gradient || style?.color?.gradient; + return { + ...saveProps, + className: classnames_default()(saveProps.className, + // Add background image classes in the editor, if not already handled by background color values. + !hasBackgroundValue && getBackgroundImageClasses(style)) + }; +} +/* harmony default export */ const color = ({ + useBlockProps: color_useBlockProps, + addSaveProps: color_addSaveProps, + attributeKeys: ['backgroundColor', 'textColor', 'gradient', 'style'], + hasSupport: hasColorSupport +}); +const MIGRATION_PATHS = { + linkColor: [['style', 'elements', 'link', 'color', 'text']], + textColor: [['textColor'], ['style', 'color', 'text']], + backgroundColor: [['backgroundColor'], ['style', 'color', 'background']], + gradient: [['gradient'], ['style', 'color', 'gradient']] }; -/* harmony default export */ var use_block_types_state = (useBlockTypesState); +function color_addTransforms(result, source, index, results) { + const destinationBlockType = result.name; + const activeSupports = { + linkColor: hasLinkColorSupport(destinationBlockType), + textColor: hasTextColorSupport(destinationBlockType), + backgroundColor: hasBackgroundColorSupport(destinationBlockType), + gradient: hasGradientSupport(destinationBlockType) + }; + return transformStyles(activeSupports, MIGRATION_PATHS, result, source, index, results); +} +(0,external_wp_hooks_namespaceObject.addFilter)('blocks.registerBlockType', 'core/color/addAttribute', color_addAttributes); +(0,external_wp_hooks_namespaceObject.addFilter)('blocks.switchToBlockType.transformedBlock', 'core/color/addTransforms', color_addTransforms); -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/inserter-listbox/index.js +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/font-family/index.js /** * WordPress dependencies */ + /** * Internal dependencies */ - - - -function InserterListbox({ - children +function FontFamilyControl({ + value = '', + onChange, + fontFamilies, + ...props }) { - const compositeState = (0,external_wp_components_namespaceObject.__unstableUseCompositeState)({ - shift: true, - wrap: 'horizontal' + const [blockLevelFontFamilies] = use_settings_useSettings('typography.fontFamilies'); + if (!fontFamilies) { + fontFamilies = blockLevelFontFamilies; + } + if (!fontFamilies || fontFamilies.length === 0) { + return null; + } + const options = [{ + value: '', + label: (0,external_wp_i18n_namespaceObject.__)('Default') + }, ...fontFamilies.map(({ + fontFamily, + name + }) => { + return { + value: fontFamily, + label: name || fontFamily + }; + })]; + return (0,external_React_.createElement)(external_wp_components_namespaceObject.SelectControl, { + label: (0,external_wp_i18n_namespaceObject.__)('Font'), + options: options, + value: value, + onChange: onChange, + labelPosition: "top", + ...props }); - return (0,external_wp_element_namespaceObject.createElement)(context.Provider, { - value: compositeState - }, children); } -/* harmony default export */ var inserter_listbox = (InserterListbox); -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/inserter/block-types-tab.js +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/font-appearance-control/index.js /** * WordPress dependencies @@ -23045,270 +27330,343 @@ function InserterListbox({ +const FONT_STYLES = [{ + name: (0,external_wp_i18n_namespaceObject._x)('Regular', 'font style'), + value: 'normal' +}, { + name: (0,external_wp_i18n_namespaceObject._x)('Italic', 'font style'), + value: 'italic' +}]; +const FONT_WEIGHTS = [{ + name: (0,external_wp_i18n_namespaceObject._x)('Thin', 'font weight'), + value: '100' +}, { + name: (0,external_wp_i18n_namespaceObject._x)('Extra Light', 'font weight'), + value: '200' +}, { + name: (0,external_wp_i18n_namespaceObject._x)('Light', 'font weight'), + value: '300' +}, { + name: (0,external_wp_i18n_namespaceObject._x)('Regular', 'font weight'), + value: '400' +}, { + name: (0,external_wp_i18n_namespaceObject._x)('Medium', 'font weight'), + value: '500' +}, { + name: (0,external_wp_i18n_namespaceObject._x)('Semi Bold', 'font weight'), + value: '600' +}, { + name: (0,external_wp_i18n_namespaceObject._x)('Bold', 'font weight'), + value: '700' +}, { + name: (0,external_wp_i18n_namespaceObject._x)('Extra Bold', 'font weight'), + value: '800' +}, { + name: (0,external_wp_i18n_namespaceObject._x)('Black', 'font weight'), + value: '900' +}]; /** - * Internal dependencies + * Adjusts font appearance field label in case either font styles or weights + * are disabled. + * + * @param {boolean} hasFontStyles Whether font styles are enabled and present. + * @param {boolean} hasFontWeights Whether font weights are enabled and present. + * @return {string} A label representing what font appearance is being edited. */ - - - - - -const getBlockNamespace = item => item.name.split('/')[0]; -const MAX_SUGGESTED_ITEMS = 6; +const getFontAppearanceLabel = (hasFontStyles, hasFontWeights) => { + if (!hasFontStyles) { + return (0,external_wp_i18n_namespaceObject.__)('Font weight'); + } + if (!hasFontWeights) { + return (0,external_wp_i18n_namespaceObject.__)('Font style'); + } + return (0,external_wp_i18n_namespaceObject.__)('Appearance'); +}; /** - * Shared reference to an empty array for cases where it is important to avoid - * returning a new array reference on every invocation and rerendering the component. + * Control to display unified font style and weight options. * - * @type {Array} + * @param {Object} props Component props. + * + * @return {Element} Font appearance control. */ -const block_types_tab_EMPTY_ARRAY = []; -function BlockTypesTab({ - rootClientId, - onInsert, - onHover, - showMostUsedBlocks -}) { - const [items, categories, collections, onSelectItem] = use_block_types_state(rootClientId, onInsert); - const suggestedItems = (0,external_wp_element_namespaceObject.useMemo)(() => { - return orderBy(items, 'frecency', 'desc').slice(0, MAX_SUGGESTED_ITEMS); - }, [items]); - const uncategorizedItems = (0,external_wp_element_namespaceObject.useMemo)(() => { - return items.filter(item => !item.category); - }, [items]); - const itemsPerCategory = (0,external_wp_element_namespaceObject.useMemo)(() => { - return (0,external_wp_compose_namespaceObject.pipe)(itemList => itemList.filter(item => item.category && item.category !== 'reusable'), itemList => itemList.reduce((acc, item) => { - const { - category - } = item; - if (!acc[category]) { - acc[category] = []; - } - acc[category].push(item); - return acc; - }, {}))(items); - }, [items]); - const itemsPerCollection = (0,external_wp_element_namespaceObject.useMemo)(() => { - // Create a new Object to avoid mutating collection. - const result = { - ...collections - }; - Object.keys(collections).forEach(namespace => { - result[namespace] = items.filter(item => getBlockNamespace(item) === namespace); - if (result[namespace].length === 0) { - delete result[namespace]; - } +function FontAppearanceControl(props) { + const { + onChange, + hasFontStyles = true, + hasFontWeights = true, + value: { + fontStyle, + fontWeight + }, + ...otherProps + } = props; + const hasStylesOrWeights = hasFontStyles || hasFontWeights; + const label = getFontAppearanceLabel(hasFontStyles, hasFontWeights); + const defaultOption = { + key: 'default', + name: (0,external_wp_i18n_namespaceObject.__)('Default'), + style: { + fontStyle: undefined, + fontWeight: undefined + } + }; + + // Combines both font style and weight options into a single dropdown. + const combineOptions = () => { + const combinedOptions = [defaultOption]; + FONT_STYLES.forEach(({ + name: styleName, + value: styleValue + }) => { + FONT_WEIGHTS.forEach(({ + name: weightName, + value: weightValue + }) => { + const optionName = styleValue === 'normal' ? weightName : (0,external_wp_i18n_namespaceObject.sprintf)( /* translators: 1: Font weight name. 2: Font style name. */ + (0,external_wp_i18n_namespaceObject.__)('%1$s %2$s'), weightName, styleName); + combinedOptions.push({ + key: `${styleValue}-${weightValue}`, + name: optionName, + style: { + fontStyle: styleValue, + fontWeight: weightValue + } + }); + }); }); - return result; - }, [items, collections]); + return combinedOptions; + }; - // Hide block preview on unmount. - (0,external_wp_element_namespaceObject.useEffect)(() => () => onHover(null), []); + // Generates select options for font styles only. + const styleOptions = () => { + const combinedOptions = [defaultOption]; + FONT_STYLES.forEach(({ + name, + value + }) => { + combinedOptions.push({ + key: value, + name, + style: { + fontStyle: value, + fontWeight: undefined + } + }); + }); + return combinedOptions; + }; - /** - * The inserter contains a big number of blocks and opening it is a costful operation. - * The rendering is the most costful part of it, in order to improve the responsiveness - * of the "opening" action, these lazy lists allow us to render the inserter category per category, - * once all the categories are rendered, we start rendering the collections and the uncategorized block types. - */ - const currentlyRenderedCategories = (0,external_wp_compose_namespaceObject.useAsyncList)(categories); - const didRenderAllCategories = categories.length === currentlyRenderedCategories.length; + // Generates select options for font weights only. + const weightOptions = () => { + const combinedOptions = [defaultOption]; + FONT_WEIGHTS.forEach(({ + name, + value + }) => { + combinedOptions.push({ + key: value, + name, + style: { + fontStyle: undefined, + fontWeight: value + } + }); + }); + return combinedOptions; + }; - // Async List requires an array. - const collectionEntries = (0,external_wp_element_namespaceObject.useMemo)(() => { - return Object.entries(collections); - }, [collections]); - const currentlyRenderedCollections = (0,external_wp_compose_namespaceObject.useAsyncList)(didRenderAllCategories ? collectionEntries : block_types_tab_EMPTY_ARRAY); - return (0,external_wp_element_namespaceObject.createElement)(inserter_listbox, null, (0,external_wp_element_namespaceObject.createElement)("div", null, showMostUsedBlocks && !!suggestedItems.length && (0,external_wp_element_namespaceObject.createElement)(panel, { - title: (0,external_wp_i18n_namespaceObject._x)('Most used', 'blocks') - }, (0,external_wp_element_namespaceObject.createElement)(block_types_list, { - items: suggestedItems, - onSelect: onSelectItem, - onHover: onHover, - label: (0,external_wp_i18n_namespaceObject._x)('Most used', 'blocks') - })), currentlyRenderedCategories.map(category => { - const categoryItems = itemsPerCategory[category.slug]; - if (!categoryItems || !categoryItems.length) { - return null; + // Map font styles and weights to select options. + const selectOptions = (0,external_wp_element_namespaceObject.useMemo)(() => { + if (hasFontStyles && hasFontWeights) { + return combineOptions(); } - return (0,external_wp_element_namespaceObject.createElement)(panel, { - key: category.slug, - title: category.title, - icon: category.icon - }, (0,external_wp_element_namespaceObject.createElement)(block_types_list, { - items: categoryItems, - onSelect: onSelectItem, - onHover: onHover, - label: category.title - })); - }), didRenderAllCategories && uncategorizedItems.length > 0 && (0,external_wp_element_namespaceObject.createElement)(panel, { - className: "block-editor-inserter__uncategorized-blocks-panel", - title: (0,external_wp_i18n_namespaceObject.__)('Uncategorized') - }, (0,external_wp_element_namespaceObject.createElement)(block_types_list, { - items: uncategorizedItems, - onSelect: onSelectItem, - onHover: onHover, - label: (0,external_wp_i18n_namespaceObject.__)('Uncategorized') - })), currentlyRenderedCollections.map(([namespace, collection]) => { - const collectionItems = itemsPerCollection[namespace]; - if (!collectionItems || !collectionItems.length) { - return null; + return hasFontStyles ? styleOptions() : weightOptions(); + }, [props.options]); + + // Find current selection by comparing font style & weight against options, + // and fall back to the Default option if there is no matching option. + const currentSelection = selectOptions.find(option => option.style.fontStyle === fontStyle && option.style.fontWeight === fontWeight) || selectOptions[0]; + + // Adjusts screen reader description based on styles or weights. + const getDescribedBy = () => { + if (!currentSelection) { + return (0,external_wp_i18n_namespaceObject.__)('No selected font appearance'); } - return (0,external_wp_element_namespaceObject.createElement)(panel, { - key: namespace, - title: collection.title, - icon: collection.icon - }, (0,external_wp_element_namespaceObject.createElement)(block_types_list, { - items: collectionItems, - onSelect: onSelectItem, - onHover: onHover, - label: collection.title - })); - }))); + if (!hasFontStyles) { + return (0,external_wp_i18n_namespaceObject.sprintf)( + // translators: %s: Currently selected font weight. + (0,external_wp_i18n_namespaceObject.__)('Currently selected font weight: %s'), currentSelection.name); + } + if (!hasFontWeights) { + return (0,external_wp_i18n_namespaceObject.sprintf)( + // translators: %s: Currently selected font style. + (0,external_wp_i18n_namespaceObject.__)('Currently selected font style: %s'), currentSelection.name); + } + return (0,external_wp_i18n_namespaceObject.sprintf)( + // translators: %s: Currently selected font appearance. + (0,external_wp_i18n_namespaceObject.__)('Currently selected font appearance: %s'), currentSelection.name); + }; + return hasStylesOrWeights && (0,external_React_.createElement)(external_wp_components_namespaceObject.CustomSelectControl, { + ...otherProps, + className: "components-font-appearance-control", + label: label, + describedBy: getDescribedBy(), + options: selectOptions, + value: currentSelection, + onChange: ({ + selectedItem + }) => onChange(selectedItem.style), + __nextUnconstrainedWidth: true + }); } -/* harmony default export */ var block_types_tab = (BlockTypesTab); -;// CONCATENATED MODULE: external ["wp","notices"] -var external_wp_notices_namespaceObject = window["wp"]["notices"]; -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/inserter/hooks/use-patterns-state.js +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/line-height-control/utils.js +const BASE_DEFAULT_VALUE = 1.5; +const STEP = 0.01; /** - * WordPress dependencies + * A spin factor of 10 allows the spin controls to increment/decrement by 0.1. + * e.g. A line-height value of 1.55 will increment to 1.65. */ - - - - - - +const SPIN_FACTOR = 10; /** - * Internal dependencies + * There are varying value types within LineHeightControl: + * + * {undefined} Initial value. No changes from the user. + * {string} Input value. Value consumed/outputted by the input. Empty would be ''. + * {number} Block attribute type. Input value needs to be converted for attribute setting. + * + * Note: If the value is undefined, the input requires it to be an empty string ('') + * in order to be considered "controlled" by props (rather than internal state). */ - +const RESET_VALUE = ''; /** - * Retrieves the block patterns inserter state. + * Determines if the lineHeight attribute has been properly defined. * - * @param {Function} onInsert function called when inserter a list of blocks. - * @param {string=} rootClientId Insertion's root client ID. + * @param {any} lineHeight The value to check. * - * @return {Array} Returns the patterns state. (patterns, categories, onSelect handler) + * @return {boolean} Whether the lineHeight attribute is valid. */ -const usePatternsState = (onInsert, rootClientId) => { - const { - patternCategories, - patterns, - userPatternCategories - } = (0,external_wp_data_namespaceObject.useSelect)(select => { - const { - __experimentalGetAllowedPatterns, - getSettings - } = select(store); - const { - __experimentalUserPatternCategories, - __experimentalBlockPatternCategories - } = getSettings(); - return { - patterns: __experimentalGetAllowedPatterns(rootClientId), - userPatternCategories: __experimentalUserPatternCategories, - patternCategories: __experimentalBlockPatternCategories - }; - }, [rootClientId]); - const allCategories = (0,external_wp_element_namespaceObject.useMemo)(() => { - const categories = [...patternCategories]; - userPatternCategories?.forEach(userCategory => { - if (!categories.find(existingCategory => existingCategory.name === userCategory.name)) { - categories.push(userCategory); - } - }); - return categories; - }, [patternCategories, userPatternCategories]); - const { - createSuccessNotice - } = (0,external_wp_data_namespaceObject.useDispatch)(external_wp_notices_namespaceObject.store); - const onClickPattern = (0,external_wp_element_namespaceObject.useCallback)((pattern, blocks) => { - const patternBlocks = pattern.id && pattern.syncStatus !== 'unsynced' ? [(0,external_wp_blocks_namespaceObject.createBlock)('core/block', { - ref: pattern.id - })] : blocks; - onInsert((patternBlocks !== null && patternBlocks !== void 0 ? patternBlocks : []).map(block => (0,external_wp_blocks_namespaceObject.cloneBlock)(block)), pattern.name); - createSuccessNotice((0,external_wp_i18n_namespaceObject.sprintf)( /* translators: %s: block pattern title. */ - (0,external_wp_i18n_namespaceObject.__)('Block pattern "%s" inserted.'), pattern.title), { - type: 'snackbar', - id: 'block-pattern-inserted-notice' - }); - }, [createSuccessNotice, onInsert]); - return [patterns, allCategories, onClickPattern]; -}; -/* harmony default export */ var use_patterns_state = (usePatternsState); +function isLineHeightDefined(lineHeight) { + return lineHeight !== undefined && lineHeight !== RESET_VALUE; +} -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/block-patterns-paging/index.js +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/line-height-control/index.js /** * WordPress dependencies */ -function Pagination({ - currentPage, - numPages, - changePage, - totalItems -}) { - return (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.__experimentalVStack, { - className: "block-editor-patterns__grid-pagination-wrapper" - }, (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.__experimentalText, { - variant: "muted" - }, - // translators: %s: Total number of patterns. - (0,external_wp_i18n_namespaceObject.sprintf)( - // translators: %s: Total number of patterns. - (0,external_wp_i18n_namespaceObject._n)('%s item', '%s items', totalItems), totalItems)), numPages > 1 && (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.__experimentalHStack, { - expanded: false, - spacing: 3, - justify: "flex-start", - className: "block-editor-patterns__grid-pagination" - }, (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.__experimentalHStack, { - expanded: false, - spacing: 1, - className: "block-editor-patterns__grid-pagination-previous" - }, (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.Button, { - variant: "tertiary", - onClick: () => changePage(1), - disabled: currentPage === 1, - "aria-label": (0,external_wp_i18n_namespaceObject.__)('First page') - }, (0,external_wp_element_namespaceObject.createElement)("span", null, "\xAB")), (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.Button, { - variant: "tertiary", - onClick: () => changePage(currentPage - 1), - disabled: currentPage === 1, - "aria-label": (0,external_wp_i18n_namespaceObject.__)('Previous page') - }, (0,external_wp_element_namespaceObject.createElement)("span", null, "\u2039"))), (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.__experimentalText, { - variant: "muted" - }, (0,external_wp_i18n_namespaceObject.sprintf)( - // translators: %1$s: Current page number, %2$s: Total number of pages. - (0,external_wp_i18n_namespaceObject._x)('%1$s of %2$s', 'paging'), currentPage, numPages)), (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.__experimentalHStack, { - expanded: false, - spacing: 1, - className: "block-editor-patterns__grid-pagination-next" - }, (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.Button, { - variant: "tertiary", - onClick: () => changePage(currentPage + 1), - disabled: currentPage === numPages, - "aria-label": (0,external_wp_i18n_namespaceObject.__)('Next page') - }, (0,external_wp_element_namespaceObject.createElement)("span", null, "\u203A")), (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.Button, { - variant: "tertiary", - onClick: () => changePage(numPages), - disabled: currentPage === numPages, - "aria-label": (0,external_wp_i18n_namespaceObject.__)('Last page'), - size: "default" - }, (0,external_wp_element_namespaceObject.createElement)("span", null, "\xBB"))))); -} -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/block-patterns-list/index.js /** - * External dependencies + * Internal dependencies + */ + +const line_height_control_LineHeightControl = ({ + value: lineHeight, + onChange, + /** Start opting into the new margin-free styles that will become the default in a future version. */ + __nextHasNoMarginBottom = false, + __unstableInputWidth = '60px', + ...otherProps +}) => { + const isDefined = isLineHeightDefined(lineHeight); + const adjustNextValue = (nextValue, wasTypedOrPasted) => { + // Set the next value without modification if lineHeight has been defined. + if (isDefined) return nextValue; + + /** + * The following logic handles the initial spin up/down action + * (from an undefined value state) so that the next values are better suited for + * line-height rendering. For example, the first spin up should immediately + * go to 1.6, rather than the normally expected 0.1. + * + * Spin up/down actions can be triggered by keydowns of the up/down arrow keys, + * dragging the input or by clicking the spin buttons. + */ + const spin = STEP * SPIN_FACTOR; + switch (`${nextValue}`) { + case `${spin}`: + // Increment by spin value. + return BASE_DEFAULT_VALUE + spin; + case '0': + { + // This means the user explicitly input '0', rather than using the + // spin down action from an undefined value state. + if (wasTypedOrPasted) return nextValue; + // Decrement by spin value. + return BASE_DEFAULT_VALUE - spin; + } + case '': + return BASE_DEFAULT_VALUE; + default: + return nextValue; + } + }; + const stateReducer = (state, action) => { + // Be careful when changing this — cross-browser behavior of the + // `inputType` field in `input` events are inconsistent. + // For example, Firefox emits an input event with inputType="insertReplacementText" + // on spin button clicks, while other browsers do not even emit an input event. + const wasTypedOrPasted = ['insertText', 'insertFromPaste'].includes(action.payload.event.nativeEvent?.inputType); + const value = adjustNextValue(state.value, wasTypedOrPasted); + return { + ...state, + value + }; + }; + const value = isDefined ? lineHeight : RESET_VALUE; + if (!__nextHasNoMarginBottom) { + external_wp_deprecated_default()('Bottom margin styles for wp.blockEditor.LineHeightControl', { + since: '6.0', + version: '6.4', + hint: 'Set the `__nextHasNoMarginBottom` prop to true to start opting into the new styles, which will become the default in a future version' + }); + } + const deprecatedStyles = __nextHasNoMarginBottom ? undefined : { + marginBottom: 24 + }; + const handleOnChange = (nextValue, { + event + }) => { + if (nextValue === '') { + onChange(); + return; + } + if (event.type === 'click') { + onChange(adjustNextValue(`${nextValue}`, false)); + return; + } + onChange(`${nextValue}`); + }; + return (0,external_React_.createElement)("div", { + className: "block-editor-line-height-control", + style: deprecatedStyles + }, (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalNumberControl, { + ...otherProps, + __unstableInputWidth: __unstableInputWidth, + __unstableStateReducer: stateReducer, + onChange: handleOnChange, + label: (0,external_wp_i18n_namespaceObject.__)('Line height'), + placeholder: BASE_DEFAULT_VALUE, + step: STEP, + spinFactor: SPIN_FACTOR, + value: value, + min: 0, + spinControls: "custom" + })); +}; + +/** + * @see https://github.com/WordPress/gutenberg/blob/HEAD/packages/block-editor/src/components/line-height-control/README.md */ +/* harmony default export */ const line_height_control = (line_height_control_LineHeightControl); +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/letter-spacing-control/index.js /** * WordPress dependencies @@ -23316,543 +27674,342 @@ function Pagination({ - - - /** * Internal dependencies */ - -const WithToolTip = ({ - showTooltip, - title, - children -}) => { - if (showTooltip) { - return (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.Tooltip, { - text: title - }, children); - } - return (0,external_wp_element_namespaceObject.createElement)(external_wp_element_namespaceObject.Fragment, null, children); -}; -function BlockPattern({ - isDraggable, - pattern, - onClick, - onHover, - composite, - showTooltip +/** + * Control for letter-spacing. + * + * @param {Object} props Component props. + * @param {string} props.value Currently selected letter-spacing. + * @param {Function} props.onChange Handles change in letter-spacing selection. + * @param {string|number|undefined} props.__unstableInputWidth Input width to pass through to inner UnitControl. Should be a valid CSS value. + * + * @return {Element} Letter-spacing control. + */ +function LetterSpacingControl({ + value, + onChange, + __unstableInputWidth = '60px', + ...otherProps }) { - const [isDragging, setIsDragging] = (0,external_wp_element_namespaceObject.useState)(false); - const { - blocks, - viewportWidth - } = pattern; - const instanceId = (0,external_wp_compose_namespaceObject.useInstanceId)(BlockPattern); - const descriptionId = `block-editor-block-patterns-list__item-description-${instanceId}`; - return (0,external_wp_element_namespaceObject.createElement)(inserter_draggable_blocks, { - isEnabled: isDraggable, - blocks: blocks, - isPattern: !!pattern - }, ({ - draggable, - onDragStart, - onDragEnd - }) => (0,external_wp_element_namespaceObject.createElement)("div", { - className: "block-editor-block-patterns-list__list-item", - draggable: draggable, - onDragStart: event => { - setIsDragging(true); - if (onDragStart) { - onHover?.(null); - onDragStart(event); - } - }, - onDragEnd: event => { - setIsDragging(false); - if (onDragEnd) { - onDragEnd(event); - } + const [availableUnits] = use_settings_useSettings('spacing.units'); + const units = (0,external_wp_components_namespaceObject.__experimentalUseCustomUnits)({ + availableUnits: availableUnits || ['px', 'em', 'rem'], + defaultValues: { + px: 2, + em: 0.2, + rem: 0.2 } - }, (0,external_wp_element_namespaceObject.createElement)(WithToolTip, { - showTooltip: showTooltip && !pattern.type === 'user', - title: pattern.title - }, (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.__unstableCompositeItem, { - role: "option", - as: "div", - ...composite, - className: classnames_default()('block-editor-block-patterns-list__item', { - 'block-editor-block-patterns-list__list-item-synced': pattern.type === 'user' && !pattern.syncStatus - }), - onClick: () => { - onClick(pattern, blocks); - onHover?.(null); - }, - onMouseEnter: () => { - if (isDragging) { - return; - } - onHover?.(pattern); - }, - onMouseLeave: () => onHover?.(null), - "aria-label": pattern.title, - "aria-describedby": pattern.description ? descriptionId : undefined - }, (0,external_wp_element_namespaceObject.createElement)(block_preview, { - blocks: blocks, - viewportWidth: viewportWidth - }), (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.__experimentalHStack, { - className: "block-editor-patterns__pattern-details" - }, pattern.type === 'user' && !pattern.syncStatus && (0,external_wp_element_namespaceObject.createElement)("div", { - className: "block-editor-patterns__pattern-icon-wrapper" - }, (0,external_wp_element_namespaceObject.createElement)(build_module_icon, { - className: "block-editor-patterns__pattern-icon", - icon: library_symbol - })), (!showTooltip || pattern.type === 'user') && (0,external_wp_element_namespaceObject.createElement)("div", { - className: "block-editor-block-patterns-list__item-title" - }, pattern.title)), !!pattern.description && (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.VisuallyHidden, { - id: descriptionId - }, pattern.description))))); -} -function BlockPatternPlaceholder() { - return (0,external_wp_element_namespaceObject.createElement)("div", { - className: "block-editor-block-patterns-list__item is-placeholder" }); -} -function BlockPatternList({ - isDraggable, - blockPatterns, - shownPatterns, - onHover, - onClickPattern, - orientation, - label = (0,external_wp_i18n_namespaceObject.__)('Block patterns'), - showTitlesAsTooltip, - pagingProps -}, ref) { - const composite = (0,external_wp_components_namespaceObject.__unstableUseCompositeState)({ - orientation + return (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalUnitControl, { + ...otherProps, + label: (0,external_wp_i18n_namespaceObject.__)('Letter spacing'), + value: value, + __unstableInputWidth: __unstableInputWidth, + units: units, + onChange: onChange }); - return (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.__unstableComposite, { - ...composite, - role: "listbox", - className: "block-editor-block-patterns-list", - "aria-label": label, - ref: ref - }, blockPatterns.map(pattern => { - const isShown = shownPatterns.includes(pattern); - return isShown ? (0,external_wp_element_namespaceObject.createElement)(BlockPattern, { - key: pattern.name, - pattern: pattern, - onClick: onClickPattern, - onHover: onHover, - isDraggable: isDraggable, - composite: composite, - showTooltip: showTitlesAsTooltip - }) : (0,external_wp_element_namespaceObject.createElement)(BlockPatternPlaceholder, { - key: pattern.name - }); - }), pagingProps && (0,external_wp_element_namespaceObject.createElement)(Pagination, { - ...pagingProps - })); } -/* harmony default export */ var block_patterns_list = ((0,external_wp_element_namespaceObject.forwardRef)(BlockPatternList)); -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/inserter/block-patterns-explorer/sidebar.js +;// CONCATENATED MODULE: ./node_modules/@wordpress/icons/build-module/library/reset.js /** * WordPress dependencies */ +const reset_reset = (0,external_React_.createElement)(external_wp_primitives_namespaceObject.SVG, { + xmlns: "http://www.w3.org/2000/svg", + viewBox: "0 0 24 24" +}, (0,external_React_.createElement)(external_wp_primitives_namespaceObject.Path, { + d: "M7 11.5h10V13H7z" +})); +/* harmony default export */ const library_reset = (reset_reset); -function PatternCategoriesList({ - selectedCategory, - patternCategories, - onClickCategory -}) { - const baseClassName = 'block-editor-block-patterns-explorer__sidebar'; - return (0,external_wp_element_namespaceObject.createElement)("div", { - className: `${baseClassName}__categories-list` - }, patternCategories.map(({ - name, - label - }) => { - return (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.Button, { - key: name, - label: label, - className: `${baseClassName}__categories-list__item`, - isPressed: selectedCategory === name, - onClick: () => { - onClickCategory(name); - } - }, label); - })); -} -function PatternsExplorerSearch({ - searchValue, - setSearchValue -}) { - const baseClassName = 'block-editor-block-patterns-explorer__search'; - return (0,external_wp_element_namespaceObject.createElement)("div", { - className: baseClassName - }, (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.SearchControl, { - __nextHasNoMarginBottom: true, - onChange: setSearchValue, - value: searchValue, - label: (0,external_wp_i18n_namespaceObject.__)('Search for patterns'), - placeholder: (0,external_wp_i18n_namespaceObject.__)('Search') - })); -} -function PatternExplorerSidebar({ - selectedCategory, - patternCategories, - onClickCategory, - searchValue, - setSearchValue -}) { - const baseClassName = 'block-editor-block-patterns-explorer__sidebar'; - return (0,external_wp_element_namespaceObject.createElement)("div", { - className: baseClassName - }, (0,external_wp_element_namespaceObject.createElement)(PatternsExplorerSearch, { - searchValue: searchValue, - setSearchValue: setSearchValue - }), !searchValue && (0,external_wp_element_namespaceObject.createElement)(PatternCategoriesList, { - selectedCategory: selectedCategory, - patternCategories: patternCategories, - onClickCategory: onClickCategory - })); -} -/* harmony default export */ var sidebar = (PatternExplorerSidebar); +;// CONCATENATED MODULE: ./node_modules/@wordpress/icons/build-module/library/format-uppercase.js -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/inserter/hooks/use-insertion-point.js /** * WordPress dependencies */ +const formatUppercase = (0,external_React_.createElement)(external_wp_primitives_namespaceObject.SVG, { + xmlns: "http://www.w3.org/2000/svg", + viewBox: "0 0 24 24" +}, (0,external_React_.createElement)(external_wp_primitives_namespaceObject.Path, { + d: "M6.1 6.8L2.1 18h1.6l1.1-3h4.3l1.1 3h1.6l-4-11.2H6.1zm-.8 6.8L7 8.9l1.7 4.7H5.3zm15.1-.7c-.4-.5-.9-.8-1.6-1 .4-.2.7-.5.8-.9.2-.4.3-.9.3-1.4 0-.9-.3-1.6-.8-2-.6-.5-1.3-.7-2.4-.7h-3.5V18h4.2c1.1 0 2-.3 2.6-.8.6-.6 1-1.4 1-2.4-.1-.8-.3-1.4-.6-1.9zm-5.7-4.7h1.8c.6 0 1.1.1 1.4.4.3.2.5.7.5 1.3 0 .6-.2 1.1-.5 1.3-.3.2-.8.4-1.4.4h-1.8V8.2zm4 8c-.4.3-.9.5-1.5.5h-2.6v-3.8h2.6c1.4 0 2 .6 2 1.9.1.6-.1 1-.5 1.4z" +})); +/* harmony default export */ const format_uppercase = (formatUppercase); +;// CONCATENATED MODULE: ./node_modules/@wordpress/icons/build-module/library/format-lowercase.js +/** + * WordPress dependencies + */ +const formatLowercase = (0,external_React_.createElement)(external_wp_primitives_namespaceObject.SVG, { + xmlns: "http://www.w3.org/2000/svg", + viewBox: "0 0 24 24" +}, (0,external_React_.createElement)(external_wp_primitives_namespaceObject.Path, { + d: "M11 16.8c-.1-.1-.2-.3-.3-.5v-2.6c0-.9-.1-1.7-.3-2.2-.2-.5-.5-.9-.9-1.2-.4-.2-.9-.3-1.6-.3-.5 0-1 .1-1.5.2s-.9.3-1.2.6l.2 1.2c.4-.3.7-.4 1.1-.5.3-.1.7-.2 1-.2.6 0 1 .1 1.3.4.3.2.4.7.4 1.4-1.2 0-2.3.2-3.3.7s-1.4 1.1-1.4 2.1c0 .7.2 1.2.7 1.6.4.4 1 .6 1.8.6.9 0 1.7-.4 2.4-1.2.1.3.2.5.4.7.1.2.3.3.6.4.3.1.6.1 1.1.1h.1l.2-1.2h-.1c-.4.1-.6 0-.7-.1zM9.2 16c-.2.3-.5.6-.9.8-.3.1-.7.2-1.1.2-.4 0-.7-.1-.9-.3-.2-.2-.3-.5-.3-.9 0-.6.2-1 .7-1.3.5-.3 1.3-.4 2.5-.5v2zm10.6-3.9c-.3-.6-.7-1.1-1.2-1.5-.6-.4-1.2-.6-1.9-.6-.5 0-.9.1-1.4.3-.4.2-.8.5-1.1.8V6h-1.4v12h1.3l.2-1c.2.4.6.6 1 .8.4.2.9.3 1.4.3.7 0 1.2-.2 1.8-.5.5-.4 1-.9 1.3-1.5.3-.6.5-1.3.5-2.1-.1-.6-.2-1.3-.5-1.9zm-1.7 4c-.4.5-.9.8-1.6.8s-1.2-.2-1.7-.7c-.4-.5-.7-1.2-.7-2.1 0-.9.2-1.6.7-2.1.4-.5 1-.7 1.7-.7s1.2.3 1.6.8c.4.5.6 1.2.6 2s-.2 1.4-.6 2z" +})); +/* harmony default export */ const format_lowercase = (formatLowercase); +;// CONCATENATED MODULE: ./node_modules/@wordpress/icons/build-module/library/format-capitalize.js /** - * Internal dependencies + * WordPress dependencies */ +const formatCapitalize = (0,external_React_.createElement)(external_wp_primitives_namespaceObject.SVG, { + xmlns: "http://www.w3.org/2000/svg", + viewBox: "0 0 24 24" +}, (0,external_React_.createElement)(external_wp_primitives_namespaceObject.Path, { + d: "M7.1 6.8L3.1 18h1.6l1.1-3h4.3l1.1 3h1.6l-4-11.2H7.1zm-.8 6.8L8 8.9l1.7 4.7H6.3zm14.5-1.5c-.3-.6-.7-1.1-1.2-1.5-.6-.4-1.2-.6-1.9-.6-.5 0-.9.1-1.4.3-.4.2-.8.5-1.1.8V6h-1.4v12h1.3l.2-1c.2.4.6.6 1 .8.4.2.9.3 1.4.3.7 0 1.2-.2 1.8-.5.5-.4 1-.9 1.3-1.5.3-.6.5-1.3.5-2.1-.1-.6-.2-1.3-.5-1.9zm-1.7 4c-.4.5-.9.8-1.6.8s-1.2-.2-1.7-.7c-.4-.5-.7-1.2-.7-2.1 0-.9.2-1.6.7-2.1.4-.5 1-.7 1.7-.7s1.2.3 1.6.8c.4.5.6 1.2.6 2 .1.8-.2 1.4-.6 2z" +})); +/* harmony default export */ const format_capitalize = (formatCapitalize); + +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/text-transform-control/index.js /** - * @typedef WPInserterConfig - * - * @property {string=} rootClientId If set, insertion will be into the - * block with this ID. - * @property {number=} insertionIndex If set, insertion will be into this - * explicit position. - * @property {string=} clientId If set, insertion will be after the - * block with this ID. - * @property {boolean=} isAppender Whether the inserter is an appender - * or not. - * @property {Function=} onSelect Called after insertion. + * External dependencies */ + /** - * Returns the insertion point state given the inserter config. + * WordPress dependencies + */ + + + +const TEXT_TRANSFORMS = [{ + name: (0,external_wp_i18n_namespaceObject.__)('None'), + value: 'none', + icon: library_reset +}, { + name: (0,external_wp_i18n_namespaceObject.__)('Uppercase'), + value: 'uppercase', + icon: format_uppercase +}, { + name: (0,external_wp_i18n_namespaceObject.__)('Lowercase'), + value: 'lowercase', + icon: format_lowercase +}, { + name: (0,external_wp_i18n_namespaceObject.__)('Capitalize'), + value: 'capitalize', + icon: format_capitalize +}]; + +/** + * Control to facilitate text transform selections. * - * @param {WPInserterConfig} config Inserter Config. - * @return {Array} Insertion Point State (rootClientID, onInsertBlocks and onToggle). + * @param {Object} props Component props. + * @param {string} props.className Class name to add to the control. + * @param {string} props.value Currently selected text transform. + * @param {Function} props.onChange Handles change in text transform selection. + * + * @return {Element} Text transform control. */ -function useInsertionPoint({ - rootClientId = '', - insertionIndex, - clientId, - isAppender, - onSelect, - shouldFocusBlock = true, - selectBlockOnInsert = true +function TextTransformControl({ + className, + value, + onChange }) { - const { - getSelectedBlock - } = (0,external_wp_data_namespaceObject.useSelect)(store); - const { - destinationRootClientId, - destinationIndex - } = (0,external_wp_data_namespaceObject.useSelect)(select => { - const { - getSelectedBlockClientId, - getBlockRootClientId, - getBlockIndex, - getBlockOrder - } = select(store); - const selectedBlockClientId = getSelectedBlockClientId(); - let _destinationRootClientId = rootClientId; - let _destinationIndex; - if (insertionIndex !== undefined) { - // Insert into a specific index. - _destinationIndex = insertionIndex; - } else if (clientId) { - // Insert after a specific client ID. - _destinationIndex = getBlockIndex(clientId); - } else if (!isAppender && selectedBlockClientId) { - _destinationRootClientId = getBlockRootClientId(selectedBlockClientId); - _destinationIndex = getBlockIndex(selectedBlockClientId) + 1; - } else { - // Insert at the end of the list. - _destinationIndex = getBlockOrder(_destinationRootClientId).length; - } - return { - destinationRootClientId: _destinationRootClientId, - destinationIndex: _destinationIndex - }; - }, [rootClientId, insertionIndex, clientId, isAppender]); - const { - replaceBlocks, - insertBlocks, - showInsertionPoint, - hideInsertionPoint - } = (0,external_wp_data_namespaceObject.useDispatch)(store); - const onInsertBlocks = (0,external_wp_element_namespaceObject.useCallback)((blocks, meta, shouldForceFocusBlock = false) => { - const selectedBlock = getSelectedBlock(); - if (!isAppender && selectedBlock && (0,external_wp_blocks_namespaceObject.isUnmodifiedDefaultBlock)(selectedBlock)) { - replaceBlocks(selectedBlock.clientId, blocks, null, shouldFocusBlock || shouldForceFocusBlock ? 0 : null, meta); - } else { - insertBlocks(blocks, destinationIndex, destinationRootClientId, selectBlockOnInsert, shouldFocusBlock || shouldForceFocusBlock ? 0 : null, meta); - } - const blockLength = Array.isArray(blocks) ? blocks.length : 1; - const message = (0,external_wp_i18n_namespaceObject.sprintf)( - // translators: %d: the name of the block that has been added - (0,external_wp_i18n_namespaceObject._n)('%d block added.', '%d blocks added.', blockLength), blockLength); - (0,external_wp_a11y_namespaceObject.speak)(message); - if (onSelect) { - onSelect(blocks); - } - }, [isAppender, getSelectedBlock, replaceBlocks, insertBlocks, destinationRootClientId, destinationIndex, onSelect, shouldFocusBlock, selectBlockOnInsert]); - const onToggleInsertionPoint = (0,external_wp_element_namespaceObject.useCallback)(show => { - if (show) { - showInsertionPoint(destinationRootClientId, destinationIndex); - } else { - hideInsertionPoint(); - } - }, [showInsertionPoint, hideInsertionPoint, destinationRootClientId, destinationIndex]); - return [destinationRootClientId, onInsertBlocks, onToggleInsertionPoint]; + return (0,external_React_.createElement)("fieldset", { + className: classnames_default()('block-editor-text-transform-control', className) + }, (0,external_React_.createElement)(external_wp_components_namespaceObject.BaseControl.VisualLabel, { + as: "legend" + }, (0,external_wp_i18n_namespaceObject.__)('Letter case')), (0,external_React_.createElement)("div", { + className: "block-editor-text-transform-control__buttons" + }, TEXT_TRANSFORMS.map(textTransform => { + return (0,external_React_.createElement)(external_wp_components_namespaceObject.Button, { + key: textTransform.value, + icon: textTransform.icon, + label: textTransform.name, + isPressed: textTransform.value === value, + onClick: () => { + onChange(textTransform.value === value ? undefined : textTransform.value); + } + }); + }))); } -/* harmony default export */ var use_insertion_point = (useInsertionPoint); -// EXTERNAL MODULE: ./node_modules/remove-accents/index.js -var remove_accents = __webpack_require__(4793); -var remove_accents_default = /*#__PURE__*/__webpack_require__.n(remove_accents); -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/inserter/search-items.js +;// CONCATENATED MODULE: ./node_modules/@wordpress/icons/build-module/library/format-underline.js + /** - * External dependencies + * WordPress dependencies */ +const formatUnderline = (0,external_React_.createElement)(external_wp_primitives_namespaceObject.SVG, { + xmlns: "http://www.w3.org/2000/svg", + viewBox: "0 0 24 24" +}, (0,external_React_.createElement)(external_wp_primitives_namespaceObject.Path, { + d: "M7 18v1h10v-1H7zm5-2c1.5 0 2.6-.4 3.4-1.2.8-.8 1.1-2 1.1-3.5V5H15v5.8c0 1.2-.2 2.1-.6 2.8-.4.7-1.2 1-2.4 1s-2-.3-2.4-1c-.4-.7-.6-1.6-.6-2.8V5H7.5v6.2c0 1.5.4 2.7 1.1 3.5.8.9 1.9 1.3 3.4 1.3z" +})); +/* harmony default export */ const format_underline = (formatUnderline); +;// CONCATENATED MODULE: ./node_modules/@wordpress/icons/build-module/library/format-strikethrough.js -// Default search helpers. -const defaultGetName = item => item.name || ''; -const defaultGetTitle = item => item.title; -const defaultGetDescription = item => item.description || ''; -const defaultGetKeywords = item => item.keywords || []; -const defaultGetCategory = item => item.category; -const defaultGetCollection = () => null; +/** + * WordPress dependencies + */ + +const formatStrikethrough = (0,external_React_.createElement)(external_wp_primitives_namespaceObject.SVG, { + xmlns: "http://www.w3.org/2000/svg", + viewBox: "0 0 24 24" +}, (0,external_React_.createElement)(external_wp_primitives_namespaceObject.Path, { + d: "M9.1 9v-.5c0-.6.2-1.1.7-1.4.5-.3 1.2-.5 2-.5.7 0 1.4.1 2.1.3.7.2 1.4.5 2.1.9l.2-1.9c-.6-.3-1.2-.5-1.9-.7-.8-.1-1.6-.2-2.4-.2-1.5 0-2.7.3-3.6 1-.8.7-1.2 1.5-1.2 2.6V9h2zM20 12H4v1h8.3c.3.1.6.2.8.3.5.2.9.5 1.1.8.3.3.4.7.4 1.2 0 .7-.2 1.1-.8 1.5-.5.3-1.2.5-2.1.5-.8 0-1.6-.1-2.4-.3-.8-.2-1.5-.5-2.2-.8L7 18.1c.5.2 1.2.4 2 .6.8.2 1.6.3 2.4.3 1.7 0 3-.3 3.9-1 .9-.7 1.3-1.6 1.3-2.8 0-.9-.2-1.7-.7-2.2H20v-1z" +})); +/* harmony default export */ const format_strikethrough = (formatStrikethrough); + +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/text-decoration-control/index.js /** - * Extracts words from an input string. - * - * @param {string} input The input string. - * - * @return {Array} Words, extracted from the input string. + * External dependencies */ -function extractWords(input = '') { - return noCase(input, { - splitRegexp: [/([\p{Ll}\p{Lo}\p{N}])([\p{Lu}\p{Lt}])/gu, - // One lowercase or digit, followed by one uppercase. - /([\p{Lu}\p{Lt}])([\p{Lu}\p{Lt}][\p{Ll}\p{Lo}])/gu // One uppercase followed by one uppercase and one lowercase. - ], - stripRegexp: /(\p{C}|\p{P}|\p{S})+/giu // Anything that's not a punctuation, symbol or control/format character. - }).split(' ').filter(Boolean); -} /** - * Sanitizes the search input string. - * - * @param {string} input The search input to normalize. - * - * @return {string} The normalized search input. + * WordPress dependencies */ -function normalizeSearchInput(input = '') { - // Disregard diacritics. - // Input: "média" - input = remove_accents_default()(input); - // Accommodate leading slash, matching autocomplete expectations. - // Input: "/media" - input = input.replace(/^\//, ''); - // Lowercase. - // Input: "MEDIA" - input = input.toLowerCase(); - return input; -} + +const TEXT_DECORATIONS = [{ + name: (0,external_wp_i18n_namespaceObject.__)('None'), + value: 'none', + icon: library_reset +}, { + name: (0,external_wp_i18n_namespaceObject.__)('Underline'), + value: 'underline', + icon: format_underline +}, { + name: (0,external_wp_i18n_namespaceObject.__)('Strikethrough'), + value: 'line-through', + icon: format_strikethrough +}]; /** - * Converts the search term into a list of normalized terms. + * Control to facilitate text decoration selections. * - * @param {string} input The search term to normalize. + * @param {Object} props Component props. + * @param {string} props.value Currently selected text decoration. + * @param {Function} props.onChange Handles change in text decoration selection. + * @param {string} [props.className] Additional class name to apply. * - * @return {string[]} The normalized list of search terms. + * @return {Element} Text decoration control. */ -const getNormalizedSearchTerms = (input = '') => { - return extractWords(normalizeSearchInput(input)); -}; -const removeMatchingTerms = (unmatchedTerms, unprocessedTerms) => { - return unmatchedTerms.filter(term => !getNormalizedSearchTerms(unprocessedTerms).some(unprocessedTerm => unprocessedTerm.includes(term))); -}; -const searchBlockItems = (items, categories, collections, searchInput) => { - const normalizedSearchTerms = getNormalizedSearchTerms(searchInput); - if (normalizedSearchTerms.length === 0) { - return items; - } - const config = { - getCategory: item => categories.find(({ - slug - }) => slug === item.category)?.title, - getCollection: item => collections[item.name.split('/')[0]]?.title - }; - return searchItems(items, searchInput, config); -}; +function TextDecorationControl({ + value, + onChange, + className +}) { + return (0,external_React_.createElement)("fieldset", { + className: classnames_default()('block-editor-text-decoration-control', className) + }, (0,external_React_.createElement)(external_wp_components_namespaceObject.BaseControl.VisualLabel, { + as: "legend" + }, (0,external_wp_i18n_namespaceObject.__)('Decoration')), (0,external_React_.createElement)("div", { + className: "block-editor-text-decoration-control__buttons" + }, TEXT_DECORATIONS.map(textDecoration => { + return (0,external_React_.createElement)(external_wp_components_namespaceObject.Button, { + key: textDecoration.value, + icon: textDecoration.icon, + label: textDecoration.name, + isPressed: textDecoration.value === value, + onClick: () => { + onChange(textDecoration.value === value ? undefined : textDecoration.value); + } + }); + }))); +} + +;// CONCATENATED MODULE: ./node_modules/@wordpress/icons/build-module/library/text-horizontal.js /** - * Filters an item list given a search term. - * - * @param {Array} items Item list - * @param {string} searchInput Search input. - * @param {Object} config Search Config. - * - * @return {Array} Filtered item list. + * WordPress dependencies */ -const searchItems = (items = [], searchInput = '', config = {}) => { - const normalizedSearchTerms = getNormalizedSearchTerms(searchInput); - if (normalizedSearchTerms.length === 0) { - return items; - } - const rankedItems = items.map(item => { - return [item, getItemSearchRank(item, searchInput, config)]; - }).filter(([, rank]) => rank > 0); - rankedItems.sort(([, rank1], [, rank2]) => rank2 - rank1); - return rankedItems.map(([item]) => item); -}; + +const textHorizontal = (0,external_React_.createElement)(external_wp_primitives_namespaceObject.SVG, { + xmlns: "http://www.w3.org/2000/svg", + viewBox: "0 0 24 24" +}, (0,external_React_.createElement)(external_wp_primitives_namespaceObject.Path, { + d: "M8.2 14.4h3.9L13 17h1.7L11 6.5H9.3L5.6 17h1.7l.9-2.6zm2-5.5 1.4 4H8.8l1.4-4zm7.4 7.5-1.3.8.8 1.4H5.5V20h14.3l-2.2-3.6z" +})); +/* harmony default export */ const text_horizontal = (textHorizontal); + +;// CONCATENATED MODULE: ./node_modules/@wordpress/icons/build-module/library/text-vertical.js /** - * Get the search rank for a given item and a specific search term. - * The better the match, the higher the rank. - * If the rank equals 0, it should be excluded from the results. - * - * @param {Object} item Item to filter. - * @param {string} searchTerm Search term. - * @param {Object} config Search Config. - * - * @return {number} Search Rank. + * WordPress dependencies */ -function getItemSearchRank(item, searchTerm, config = {}) { - const { - getName = defaultGetName, - getTitle = defaultGetTitle, - getDescription = defaultGetDescription, - getKeywords = defaultGetKeywords, - getCategory = defaultGetCategory, - getCollection = defaultGetCollection - } = config; - const name = getName(item); - const title = getTitle(item); - const description = getDescription(item); - const keywords = getKeywords(item); - const category = getCategory(item); - const collection = getCollection(item); - const normalizedSearchInput = normalizeSearchInput(searchTerm); - const normalizedTitle = normalizeSearchInput(title); - let rank = 0; - // Prefers exact matches - // Then prefers if the beginning of the title matches the search term - // name, keywords, categories, collection, variations match come later. - if (normalizedSearchInput === normalizedTitle) { - rank += 30; - } else if (normalizedTitle.startsWith(normalizedSearchInput)) { - rank += 20; - } else { - const terms = [name, title, description, ...keywords, category, collection].join(' '); - const normalizedSearchTerms = extractWords(normalizedSearchInput); - const unmatchedTerms = removeMatchingTerms(normalizedSearchTerms, terms); - if (unmatchedTerms.length === 0) { - rank += 10; - } - } +const textVertical = (0,external_React_.createElement)(external_wp_primitives_namespaceObject.SVG, { + xmlns: "http://www.w3.org/2000/svg", + viewBox: "0 0 24 24" +}, (0,external_React_.createElement)(external_wp_primitives_namespaceObject.Path, { + d: "M7 5.6v1.7l2.6.9v3.9L7 13v1.7L17.5 11V9.3L7 5.6zm4.2 6V8.8l4 1.4-4 1.4zm-5.7 5.6V5.5H4v14.3l3.6-2.2-.8-1.3-1.3.9z" +})); +/* harmony default export */ const text_vertical = (textVertical); + +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/writing-mode-control/index.js + +/** + * External dependencies + */ - // Give a better rank to "core" namespaced items. - if (rank !== 0 && name.startsWith('core/')) { - const isCoreBlockVariation = name !== item.id; - // Give a bit better rank to "core" blocks over "core" block variations. - rank += isCoreBlockVariation ? 1 : 2; - } - return rank; -} -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/inserter/hooks/use-patterns-paging.js /** * WordPress dependencies */ -const PAGE_SIZE = 20; -const INITIAL_INSERTER_RESULTS = 5; +const WRITING_MODES = [{ + name: (0,external_wp_i18n_namespaceObject.__)('Horizontal'), + value: 'horizontal-tb', + icon: text_horizontal +}, { + name: (0,external_wp_i18n_namespaceObject.__)('Vertical'), + value: (0,external_wp_i18n_namespaceObject.isRTL)() ? 'vertical-lr' : 'vertical-rl', + icon: text_vertical +}]; /** - * Supplies values needed to page the patterns list client side. + * Control to facilitate writing mode selections. * - * @param {Array} currentCategoryPatterns An array of the current patterns to display. - * @param {string} currentCategory The currently selected category. - * @param {Object} scrollContainerRef Ref of container to to find scroll container for when moving between pages. - * @param {string} currentFilter The currently search filter. + * @param {Object} props Component props. + * @param {string} props.className Class name to add to the control. + * @param {string} props.value Currently selected writing mode. + * @param {Function} props.onChange Handles change in the writing mode selection. * - * @return {Object} Returns the relevant paging values. (totalItems, categoryPatternsList, numPages, changePage, currentPage) + * @return {Element} Writing Mode control. */ -function usePatternsPaging(currentCategoryPatterns, currentCategory, scrollContainerRef, currentFilter = '') { - const [currentPage, setCurrentPage] = (0,external_wp_element_namespaceObject.useState)(1); - const previousCategory = (0,external_wp_compose_namespaceObject.usePrevious)(currentCategory); - const previousFilter = (0,external_wp_compose_namespaceObject.usePrevious)(currentFilter); - if ((previousCategory !== currentCategory || previousFilter !== currentFilter) && currentPage !== 1) { - setCurrentPage(1); - } - const totalItems = currentCategoryPatterns.length; - const pageIndex = currentPage - 1; - const categoryPatterns = (0,external_wp_element_namespaceObject.useMemo)(() => { - return currentCategoryPatterns.slice(pageIndex * PAGE_SIZE, pageIndex * PAGE_SIZE + PAGE_SIZE); - }, [pageIndex, currentCategoryPatterns]); - const categoryPatternsAsyncList = (0,external_wp_compose_namespaceObject.useAsyncList)(categoryPatterns, { - step: INITIAL_INSERTER_RESULTS - }); - const numPages = Math.ceil(currentCategoryPatterns.length / PAGE_SIZE); - const changePage = page => { - const scrollContainer = (0,external_wp_dom_namespaceObject.getScrollContainer)(scrollContainerRef?.current); - scrollContainer?.scrollTo(0, 0); - setCurrentPage(page); - }; - (0,external_wp_element_namespaceObject.useEffect)(function scrollToTopOnCategoryChange() { - const scrollContainer = (0,external_wp_dom_namespaceObject.getScrollContainer)(scrollContainerRef?.current); - scrollContainer?.scrollTo(0, 0); - }, [currentCategory, scrollContainerRef]); - return { - totalItems, - categoryPatterns, - categoryPatternsAsyncList, - numPages, - changePage, - currentPage - }; +function WritingModeControl({ + className, + value, + onChange +}) { + return (0,external_React_.createElement)("fieldset", { + className: classnames_default()('block-editor-writing-mode-control', className) + }, (0,external_React_.createElement)(external_wp_components_namespaceObject.BaseControl.VisualLabel, { + as: "legend" + }, (0,external_wp_i18n_namespaceObject.__)('Orientation')), (0,external_React_.createElement)("div", { + className: "block-editor-writing-mode-control__buttons" + }, WRITING_MODES.map(writingMode => { + return (0,external_React_.createElement)(external_wp_components_namespaceObject.Button, { + key: writingMode.value, + icon: writingMode.icon, + label: writingMode.name, + isPressed: writingMode.value === value, + onClick: () => { + onChange(writingMode.value === value ? undefined : writingMode.value); + } + }); + }))); } -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/inserter/block-patterns-explorer/patterns-list.js +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/global-styles/typography-panel.js /** * WordPress dependencies @@ -23861,8 +28018,6 @@ function usePatternsPaging(currentCategoryPatterns, currentCategory, scrollConta - - /** * Internal dependencies */ @@ -23874,90 +28029,421 @@ function usePatternsPaging(currentCategoryPatterns, currentCategory, scrollConta -function PatternsListHeader({ - filterValue, - filteredBlockPatternsLength -}) { - if (!filterValue) { - return null; + + +const MIN_TEXT_COLUMNS = 1; +const MAX_TEXT_COLUMNS = 6; +function useHasTypographyPanel(settings) { + const hasFontFamily = useHasFontFamilyControl(settings); + const hasLineHeight = useHasLineHeightControl(settings); + const hasFontAppearance = useHasAppearanceControl(settings); + const hasLetterSpacing = useHasLetterSpacingControl(settings); + const hasTextTransform = useHasTextTransformControl(settings); + const hasTextDecoration = useHasTextDecorationControl(settings); + const hasWritingMode = useHasWritingModeControl(settings); + const hasTextColumns = useHasTextColumnsControl(settings); + const hasFontSize = useHasFontSizeControl(settings); + return hasFontFamily || hasLineHeight || hasFontAppearance || hasLetterSpacing || hasTextTransform || hasFontSize || hasTextDecoration || hasWritingMode || hasTextColumns; +} +function useHasFontSizeControl(settings) { + return hasOriginValue(settings?.typography?.fontSizes) || settings?.typography?.customFontSize; +} +function useHasFontFamilyControl(settings) { + return hasOriginValue(settings?.typography?.fontFamilies); +} +function useHasLineHeightControl(settings) { + return settings?.typography?.lineHeight; +} +function useHasAppearanceControl(settings) { + return settings?.typography?.fontStyle || settings?.typography?.fontWeight; +} +function useAppearanceControlLabel(settings) { + if (!settings?.typography?.fontStyle) { + return (0,external_wp_i18n_namespaceObject.__)('Font weight'); } - return (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.__experimentalHeading, { - level: 2, - lineHeight: '48px', - className: "block-editor-block-patterns-explorer__search-results-count" - }, (0,external_wp_i18n_namespaceObject.sprintf)( /* translators: %d: number of patterns. */ - (0,external_wp_i18n_namespaceObject._n)('%d pattern found', '%d patterns found', filteredBlockPatternsLength), filteredBlockPatternsLength)); + if (!settings?.typography?.fontWeight) { + return (0,external_wp_i18n_namespaceObject.__)('Font style'); + } + return (0,external_wp_i18n_namespaceObject.__)('Appearance'); } -function PatternList({ - searchValue, - selectedCategory, - patternCategories +function useHasLetterSpacingControl(settings) { + return settings?.typography?.letterSpacing; +} +function useHasTextTransformControl(settings) { + return settings?.typography?.textTransform; +} +function useHasTextDecorationControl(settings) { + return settings?.typography?.textDecoration; +} +function useHasWritingModeControl(settings) { + return settings?.typography?.writingMode; +} +function useHasTextColumnsControl(settings) { + return settings?.typography?.textColumns; +} +function getUniqueFontSizesBySlug(settings) { + var _settings$typography$, _overrideOrigins; + const fontSizes = (_settings$typography$ = settings?.typography?.fontSizes) !== null && _settings$typography$ !== void 0 ? _settings$typography$ : {}; + const overriddenFontSizes = (_overrideOrigins = overrideOrigins(fontSizes)) !== null && _overrideOrigins !== void 0 ? _overrideOrigins : []; + const uniqueSizes = []; + for (const currentSize of overriddenFontSizes) { + if (!uniqueSizes.some(({ + slug + }) => slug === currentSize.slug)) { + uniqueSizes.push(currentSize); + } + } + return uniqueSizes; +} +function TypographyToolsPanel({ + resetAllFilter, + onChange, + value, + panelId, + children }) { - const container = (0,external_wp_element_namespaceObject.useRef)(); - const debouncedSpeak = (0,external_wp_compose_namespaceObject.useDebounce)(external_wp_a11y_namespaceObject.speak, 500); - const [destinationRootClientId, onInsertBlocks] = use_insertion_point({ - shouldFocusBlock: true - }); - const [patterns,, onClickPattern] = use_patterns_state(onInsertBlocks, destinationRootClientId); - const registeredPatternCategories = (0,external_wp_element_namespaceObject.useMemo)(() => patternCategories.map(patternCategory => patternCategory.name), [patternCategories]); - const filteredBlockPatterns = (0,external_wp_element_namespaceObject.useMemo)(() => { - const filteredPatterns = patterns.filter(pattern => { - if (selectedCategory === allPatternsCategory.name) { - return true; - } - if (selectedCategory === myPatternsCategory.name && pattern.id) { - return true; + const resetAll = () => { + const updatedValue = resetAllFilter(value); + onChange(updatedValue); + }; + return (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalToolsPanel, { + label: (0,external_wp_i18n_namespaceObject.__)('Typography'), + resetAll: resetAll, + panelId: panelId, + dropdownMenuProps: TOOLSPANEL_DROPDOWNMENU_PROPS + }, children); +} +const typography_panel_DEFAULT_CONTROLS = { + fontFamily: true, + fontSize: true, + fontAppearance: true, + lineHeight: true, + letterSpacing: true, + textTransform: true, + textDecoration: true, + writingMode: true, + textColumns: true +}; +function TypographyPanel({ + as: Wrapper = TypographyToolsPanel, + value, + onChange, + inheritedValue = value, + settings, + panelId, + defaultControls = typography_panel_DEFAULT_CONTROLS +}) { + var _settings$typography$2; + const decodeValue = rawValue => getValueFromVariable({ + settings + }, '', rawValue); + + // Font Family + const hasFontFamilyEnabled = useHasFontFamilyControl(settings); + const fontFamilies = (_settings$typography$2 = settings?.typography?.fontFamilies) !== null && _settings$typography$2 !== void 0 ? _settings$typography$2 : {}; + const mergedFontFamilies = fontFamilies ? mergeOrigins(fontFamilies) : []; + const fontFamily = decodeValue(inheritedValue?.typography?.fontFamily); + const setFontFamily = newValue => { + const slug = mergedFontFamilies?.find(({ + fontFamily: f + }) => f === newValue)?.slug; + onChange(setImmutably(value, ['typography', 'fontFamily'], slug ? `var:preset|font-family|${slug}` : newValue || undefined)); + }; + const hasFontFamily = () => !!value?.typography?.fontFamily; + const resetFontFamily = () => setFontFamily(undefined); + + // Font Size + const hasFontSizeEnabled = useHasFontSizeControl(settings); + const disableCustomFontSizes = !settings?.typography?.customFontSize; + const mergedFontSizes = getUniqueFontSizesBySlug(settings); + const fontSize = decodeValue(inheritedValue?.typography?.fontSize); + const setFontSize = (newValue, metadata) => { + const actualValue = !!metadata?.slug ? `var:preset|font-size|${metadata?.slug}` : newValue; + onChange(setImmutably(value, ['typography', 'fontSize'], actualValue || undefined)); + }; + const hasFontSize = () => !!value?.typography?.fontSize; + const resetFontSize = () => setFontSize(undefined); + + // Appearance + const hasAppearanceControl = useHasAppearanceControl(settings); + const appearanceControlLabel = useAppearanceControlLabel(settings); + const hasFontStyles = settings?.typography?.fontStyle; + const hasFontWeights = settings?.typography?.fontWeight; + const fontStyle = decodeValue(inheritedValue?.typography?.fontStyle); + const fontWeight = decodeValue(inheritedValue?.typography?.fontWeight); + const setFontAppearance = ({ + fontStyle: newFontStyle, + fontWeight: newFontWeight + }) => { + onChange({ + ...value, + typography: { + ...value?.typography, + fontStyle: newFontStyle || undefined, + fontWeight: newFontWeight || undefined } - if (selectedCategory === 'uncategorized') { - const hasKnownCategory = pattern.categories.some(category => registeredPatternCategories.includes(category)); - return !pattern.categories?.length || !hasKnownCategory; + }); + }; + const hasFontAppearance = () => !!value?.typography?.fontStyle || !!value?.typography?.fontWeight; + const resetFontAppearance = () => { + setFontAppearance({}); + }; + + // Line Height + const hasLineHeightEnabled = useHasLineHeightControl(settings); + const lineHeight = decodeValue(inheritedValue?.typography?.lineHeight); + const setLineHeight = newValue => { + onChange(setImmutably(value, ['typography', 'lineHeight'], newValue || undefined)); + }; + const hasLineHeight = () => value?.typography?.lineHeight !== undefined; + const resetLineHeight = () => setLineHeight(undefined); + + // Letter Spacing + const hasLetterSpacingControl = useHasLetterSpacingControl(settings); + const letterSpacing = decodeValue(inheritedValue?.typography?.letterSpacing); + const setLetterSpacing = newValue => { + onChange(setImmutably(value, ['typography', 'letterSpacing'], newValue || undefined)); + }; + const hasLetterSpacing = () => !!value?.typography?.letterSpacing; + const resetLetterSpacing = () => setLetterSpacing(undefined); + + // Text Columns + const hasTextColumnsControl = useHasTextColumnsControl(settings); + const textColumns = decodeValue(inheritedValue?.typography?.textColumns); + const setTextColumns = newValue => { + onChange(setImmutably(value, ['typography', 'textColumns'], newValue || undefined)); + }; + const hasTextColumns = () => !!value?.typography?.textColumns; + const resetTextColumns = () => setTextColumns(undefined); + + // Text Transform + const hasTextTransformControl = useHasTextTransformControl(settings); + const textTransform = decodeValue(inheritedValue?.typography?.textTransform); + const setTextTransform = newValue => { + onChange(setImmutably(value, ['typography', 'textTransform'], newValue || undefined)); + }; + const hasTextTransform = () => !!value?.typography?.textTransform; + const resetTextTransform = () => setTextTransform(undefined); + + // Text Decoration + const hasTextDecorationControl = useHasTextDecorationControl(settings); + const textDecoration = decodeValue(inheritedValue?.typography?.textDecoration); + const setTextDecoration = newValue => { + onChange(setImmutably(value, ['typography', 'textDecoration'], newValue || undefined)); + }; + const hasTextDecoration = () => !!value?.typography?.textDecoration; + const resetTextDecoration = () => setTextDecoration(undefined); + + // Text Orientation + const hasWritingModeControl = useHasWritingModeControl(settings); + const writingMode = decodeValue(inheritedValue?.typography?.writingMode); + const setWritingMode = newValue => { + onChange(setImmutably(value, ['typography', 'writingMode'], newValue || undefined)); + }; + const hasWritingMode = () => !!value?.typography?.writingMode; + const resetWritingMode = () => setWritingMode(undefined); + const resetAllFilter = (0,external_wp_element_namespaceObject.useCallback)(previousValue => { + return { + ...previousValue, + typography: {} + }; + }, []); + return (0,external_React_.createElement)(Wrapper, { + resetAllFilter: resetAllFilter, + value: value, + onChange: onChange, + panelId: panelId + }, hasFontFamilyEnabled && (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalToolsPanelItem, { + label: (0,external_wp_i18n_namespaceObject.__)('Font family'), + hasValue: hasFontFamily, + onDeselect: resetFontFamily, + isShownByDefault: defaultControls.fontFamily, + panelId: panelId + }, (0,external_React_.createElement)(FontFamilyControl, { + fontFamilies: mergedFontFamilies, + value: fontFamily, + onChange: setFontFamily, + size: "__unstable-large", + __nextHasNoMarginBottom: true + })), hasFontSizeEnabled && (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalToolsPanelItem, { + label: (0,external_wp_i18n_namespaceObject.__)('Font size'), + hasValue: hasFontSize, + onDeselect: resetFontSize, + isShownByDefault: defaultControls.fontSize, + panelId: panelId + }, (0,external_React_.createElement)(external_wp_components_namespaceObject.FontSizePicker, { + value: fontSize, + onChange: setFontSize, + fontSizes: mergedFontSizes, + disableCustomFontSizes: disableCustomFontSizes, + withReset: false, + withSlider: true, + size: "__unstable-large" + })), hasAppearanceControl && (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalToolsPanelItem, { + className: "single-column", + label: appearanceControlLabel, + hasValue: hasFontAppearance, + onDeselect: resetFontAppearance, + isShownByDefault: defaultControls.fontAppearance, + panelId: panelId + }, (0,external_React_.createElement)(FontAppearanceControl, { + value: { + fontStyle, + fontWeight + }, + onChange: setFontAppearance, + hasFontStyles: hasFontStyles, + hasFontWeights: hasFontWeights, + size: "__unstable-large", + __nextHasNoMarginBottom: true + })), hasLineHeightEnabled && (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalToolsPanelItem, { + className: "single-column", + label: (0,external_wp_i18n_namespaceObject.__)('Line height'), + hasValue: hasLineHeight, + onDeselect: resetLineHeight, + isShownByDefault: defaultControls.lineHeight, + panelId: panelId + }, (0,external_React_.createElement)(line_height_control, { + __nextHasNoMarginBottom: true, + __unstableInputWidth: "auto", + value: lineHeight, + onChange: setLineHeight, + size: "__unstable-large" + })), hasLetterSpacingControl && (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalToolsPanelItem, { + className: "single-column", + label: (0,external_wp_i18n_namespaceObject.__)('Letter spacing'), + hasValue: hasLetterSpacing, + onDeselect: resetLetterSpacing, + isShownByDefault: defaultControls.letterSpacing, + panelId: panelId + }, (0,external_React_.createElement)(LetterSpacingControl, { + value: letterSpacing, + onChange: setLetterSpacing, + size: "__unstable-large", + __unstableInputWidth: "auto" + })), hasTextColumnsControl && (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalToolsPanelItem, { + className: "single-column", + label: (0,external_wp_i18n_namespaceObject.__)('Text columns'), + hasValue: hasTextColumns, + onDeselect: resetTextColumns, + isShownByDefault: defaultControls.textColumns, + panelId: panelId + }, (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalNumberControl, { + label: (0,external_wp_i18n_namespaceObject.__)('Text columns'), + max: MAX_TEXT_COLUMNS, + min: MIN_TEXT_COLUMNS, + onChange: setTextColumns, + size: "__unstable-large", + spinControls: "custom", + value: textColumns, + initialPosition: 1 + })), hasTextDecorationControl && (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalToolsPanelItem, { + className: "single-column", + label: (0,external_wp_i18n_namespaceObject.__)('Text decoration'), + hasValue: hasTextDecoration, + onDeselect: resetTextDecoration, + isShownByDefault: defaultControls.textDecoration, + panelId: panelId + }, (0,external_React_.createElement)(TextDecorationControl, { + value: textDecoration, + onChange: setTextDecoration, + size: "__unstable-large", + __unstableInputWidth: "auto" + })), hasWritingModeControl && (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalToolsPanelItem, { + className: "single-column", + label: (0,external_wp_i18n_namespaceObject.__)('Text orientation'), + hasValue: hasWritingMode, + onDeselect: resetWritingMode, + isShownByDefault: defaultControls.writingMode, + panelId: panelId + }, (0,external_React_.createElement)(WritingModeControl, { + value: writingMode, + onChange: setWritingMode, + size: "__unstable-large", + __nextHasNoMarginBottom: true + })), hasTextTransformControl && (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalToolsPanelItem, { + label: (0,external_wp_i18n_namespaceObject.__)('Letter case'), + hasValue: hasTextTransform, + onDeselect: resetTextTransform, + isShownByDefault: defaultControls.textTransform, + panelId: panelId + }, (0,external_React_.createElement)(TextTransformControl, { + value: textTransform, + onChange: setTextTransform, + showNone: true, + isBlock: true, + size: "__unstable-large", + __nextHasNoMarginBottom: true + }))); +} + +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/hooks/line-height.js + +/** + * WordPress dependencies + */ + + +/** + * Internal dependencies + */ + + + +const LINE_HEIGHT_SUPPORT_KEY = 'typography.lineHeight'; + +/** + * Inspector control panel containing the line height related configuration + * + * @param {Object} props + * + * @return {Element} Line height edit element. + */ +function LineHeightEdit(props) { + const { + attributes: { + style + }, + setAttributes + } = props; + const onChange = newLineHeightValue => { + const newStyle = { + ...style, + typography: { + ...style?.typography, + lineHeight: newLineHeightValue } - return pattern.categories?.includes(selectedCategory); + }; + setAttributes({ + style: cleanEmptyObject(newStyle) }); - if (!searchValue) { - return filteredPatterns; - } - return searchItems(filteredPatterns, searchValue); - }, [searchValue, patterns, selectedCategory, registeredPatternCategories]); - - // Announce search results on change. - (0,external_wp_element_namespaceObject.useEffect)(() => { - if (!searchValue) { - return; - } - const count = filteredBlockPatterns.length; - const resultsFoundMessage = (0,external_wp_i18n_namespaceObject.sprintf)( /* translators: %d: number of results. */ - (0,external_wp_i18n_namespaceObject._n)('%d result found.', '%d results found.', count), count); - debouncedSpeak(resultsFoundMessage); - }, [searchValue, debouncedSpeak, filteredBlockPatterns.length]); - const pagingProps = usePatternsPaging(filteredBlockPatterns, selectedCategory, container); - - // Reset page when search value changes. - const [previousSearchValue, setPreviousSearchValue] = (0,external_wp_element_namespaceObject.useState)(searchValue); - if (searchValue !== previousSearchValue) { - setPreviousSearchValue(searchValue); - pagingProps.changePage(1); - } - const hasItems = !!filteredBlockPatterns?.length; - return (0,external_wp_element_namespaceObject.createElement)("div", { - className: "block-editor-block-patterns-explorer__list", - ref: container - }, (0,external_wp_element_namespaceObject.createElement)(PatternsListHeader, { - filterValue: searchValue, - filteredBlockPatternsLength: filteredBlockPatterns.length - }), (0,external_wp_element_namespaceObject.createElement)(inserter_listbox, null, hasItems && (0,external_wp_element_namespaceObject.createElement)(external_wp_element_namespaceObject.Fragment, null, (0,external_wp_element_namespaceObject.createElement)(block_patterns_list, { - shownPatterns: pagingProps.categoryPatternsAsyncList, - blockPatterns: pagingProps.categoryPatterns, - onClickPattern: onClickPattern, - isDraggable: false - }), (0,external_wp_element_namespaceObject.createElement)(Pagination, { - ...pagingProps - })))); + }; + return createElement(LineHeightControl, { + __unstableInputWidth: "100%", + __nextHasNoMarginBottom: true, + value: style?.typography?.lineHeight, + onChange: onChange, + size: "__unstable-large" + }); } -/* harmony default export */ var patterns_list = (PatternList); -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/inserter/block-patterns-explorer/explorer.js +/** + * Custom hook that checks if line-height settings have been disabled. + * + * @param {string} name The name of the block. + * @return {boolean} Whether setting is disabled. + */ +function useIsLineHeightDisabled({ + name: blockName +} = {}) { + const [isEnabled] = useSettings('typography.lineHeight'); + return !isEnabled || !hasBlockSupport(blockName, LINE_HEIGHT_SUPPORT_KEY); +} +;// CONCATENATED MODULE: external ["wp","tokenList"] +const external_wp_tokenList_namespaceObject = window["wp"]["tokenList"]; +var external_wp_tokenList_default = /*#__PURE__*/__webpack_require__.n(external_wp_tokenList_namespaceObject); +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/hooks/font-family.js /** * WordPress dependencies */ @@ -23965,240 +28451,176 @@ function PatternList({ + /** * Internal dependencies */ -function PatternsExplorer({ - initialCategory, - rootClientId -}) { - const [searchValue, setSearchValue] = (0,external_wp_element_namespaceObject.useState)(''); - const [patternSourceFilter, setPatternSourceFilter] = (0,external_wp_element_namespaceObject.useState)('all'); - const [selectedCategory, setSelectedCategory] = (0,external_wp_element_namespaceObject.useState)(initialCategory?.name); - const patternCategories = usePatternsCategories(rootClientId, patternSourceFilter); - return (0,external_wp_element_namespaceObject.createElement)("div", { - className: "block-editor-block-patterns-explorer" - }, (0,external_wp_element_namespaceObject.createElement)(sidebar, { - selectedCategory: selectedCategory, - patternCategories: patternCategories, - onClickCategory: setSelectedCategory, - searchValue: searchValue, - setSearchValue: setSearchValue, - patternSourceFilter: patternSourceFilter, - setPatternSourceFilter: setPatternSourceFilter - }), (0,external_wp_element_namespaceObject.createElement)(patterns_list, { - searchValue: searchValue, - selectedCategory: selectedCategory, - patternCategories: patternCategories, - patternSourceFilter: patternSourceFilter - })); -} -function PatternsExplorerModal({ - onModalClose, - ...restProps -}) { - return (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.Modal, { - title: (0,external_wp_i18n_namespaceObject.__)('Patterns'), - onRequestClose: onModalClose, - isFullScreen: true - }, (0,external_wp_element_namespaceObject.createElement)(PatternsExplorer, { - ...restProps - })); -} -/* harmony default export */ var explorer = (PatternsExplorerModal); - -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/inserter/mobile-tab-navigation.js +const FONT_FAMILY_SUPPORT_KEY = 'typography.__experimentalFontFamily'; /** - * WordPress dependencies + * Filters registered block settings, extending attributes to include + * the `fontFamily` attribute. + * + * @param {Object} settings Original block settings + * @return {Object} Filtered block settings */ +function font_family_addAttributes(settings) { + if (!(0,external_wp_blocks_namespaceObject.hasBlockSupport)(settings, FONT_FAMILY_SUPPORT_KEY)) { + return settings; + } + // Allow blocks to specify a default value if needed. + if (!settings.attributes.fontFamily) { + Object.assign(settings.attributes, { + fontFamily: { + type: 'string' + } + }); + } + return settings; +} +/** + * Override props assigned to save component to inject font family. + * + * @param {Object} props Additional props applied to save element + * @param {Object} blockType Block type + * @param {Object} attributes Block attributes + * @return {Object} Filtered props applied to save element + */ +function font_family_addSaveProps(props, blockType, attributes) { + if (!(0,external_wp_blocks_namespaceObject.hasBlockSupport)(blockType, FONT_FAMILY_SUPPORT_KEY)) { + return props; + } + if (shouldSkipSerialization(blockType, TYPOGRAPHY_SUPPORT_KEY, 'fontFamily')) { + return props; + } + if (!attributes?.fontFamily) { + return props; + } -function ScreenHeader({ - title -}) { - return (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.__experimentalVStack, { - spacing: 0 - }, (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.__experimentalView, null, (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.__experimentalSpacer, { - marginBottom: 0, - paddingX: 4, - paddingY: 3 - }, (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.__experimentalHStack, { - spacing: 2 - }, (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.__experimentalNavigatorBackButton, { - style: - // TODO: This style override is also used in ToolsPanelHeader. - // It should be supported out-of-the-box by Button. - { - minWidth: 24, - padding: 0 - }, - icon: (0,external_wp_i18n_namespaceObject.isRTL)() ? chevron_right : chevron_left, - isSmall: true, - "aria-label": (0,external_wp_i18n_namespaceObject.__)('Navigate to the previous view') - }), (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.__experimentalSpacer, null, (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.__experimentalHeading, { - level: 5 - }, title)))))); + // Use TokenList to dedupe classes. + const classes = new (external_wp_tokenList_default())(props.className); + const { + kebabCase + } = unlock(external_wp_components_namespaceObject.privateApis); + classes.add(`has-${kebabCase(attributes?.fontFamily)}-font-family`); + const newClassName = classes.value; + props.className = newClassName ? newClassName : undefined; + return props; } -function MobileTabNavigation({ - categories, - children +function font_family_useBlockProps({ + name, + fontFamily }) { - return (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.__experimentalNavigatorProvider, { - initialPath: "/", - className: "block-editor-inserter__mobile-tab-navigation" - }, (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.__experimentalNavigatorScreen, { - path: "/" - }, (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.__experimentalItemGroup, null, categories.map(category => (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.__experimentalNavigatorButton, { - key: category.name, - path: `/category/${category.name}`, - as: external_wp_components_namespaceObject.__experimentalItem, - isAction: true - }, (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.__experimentalHStack, null, (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.FlexBlock, null, category.label), (0,external_wp_element_namespaceObject.createElement)(build_module_icon, { - icon: (0,external_wp_i18n_namespaceObject.isRTL)() ? chevron_left : chevron_right - })))))), categories.map(category => (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.__experimentalNavigatorScreen, { - key: category.name, - path: `/category/${category.name}` - }, (0,external_wp_element_namespaceObject.createElement)(ScreenHeader, { - title: (0,external_wp_i18n_namespaceObject.__)('Back') - }), children(category)))); + return font_family_addSaveProps({}, name, { + fontFamily + }); } +/* harmony default export */ const font_family = ({ + useBlockProps: font_family_useBlockProps, + addSaveProps: font_family_addSaveProps, + attributeKeys: ['fontFamily'], + hasSupport(name) { + return (0,external_wp_blocks_namespaceObject.hasBlockSupport)(name, FONT_FAMILY_SUPPORT_KEY); + } +}); -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/inserter/block-patterns-filter.js +/** + * Resets the font family block support attribute. This can be used when + * disabling the font family support controls for a block via a progressive + * discovery panel. + * + * @param {Object} props Block props. + * @param {Object} props.setAttributes Function to set block's attributes. + */ +function resetFontFamily({ + setAttributes +}) { + setAttributes({ + fontFamily: undefined + }); +} +(0,external_wp_hooks_namespaceObject.addFilter)('blocks.registerBlockType', 'core/fontFamily/addAttribute', font_family_addAttributes); +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/font-sizes/utils.js /** * WordPress dependencies */ - - - /** * Internal dependencies */ -const PATTERN_TYPES = { - all: 'all', - synced: 'synced', - unsynced: 'unsynced', - user: 'user', - theme: 'theme', - directory: 'directory' -}; -const SYNC_TYPES = { - all: 'all', - full: 'fully', - unsynced: 'unsynced' -}; -const getShouldDisableSyncFilter = sourceFilter => sourceFilter !== PATTERN_TYPES.all && sourceFilter !== PATTERN_TYPES.user; -const getShouldDisableNonUserSources = category => { - return category.name === myPatternsCategory.name; + +/** + * Returns the font size object based on an array of named font sizes and the namedFontSize and customFontSize values. + * If namedFontSize is undefined or not found in fontSizes an object with just the size value based on customFontSize is returned. + * + * @param {Array} fontSizes Array of font size objects containing at least the "name" and "size" values as properties. + * @param {?string} fontSizeAttribute Content of the font size attribute (slug). + * @param {?number} customFontSizeAttribute Contents of the custom font size attribute (value). + * + * @return {?Object} If fontSizeAttribute is set and an equal slug is found in fontSizes it returns the font size object for that slug. + * Otherwise, an object with just the size value based on customFontSize is returned. + */ +const utils_getFontSize = (fontSizes, fontSizeAttribute, customFontSizeAttribute) => { + if (fontSizeAttribute) { + const fontSizeObject = fontSizes?.find(({ + slug + }) => slug === fontSizeAttribute); + if (fontSizeObject) { + return fontSizeObject; + } + } + return { + size: customFontSizeAttribute + }; }; -function BlockPatternsSyncFilter({ - setPatternSyncFilter, - setPatternSourceFilter, - patternSyncFilter, - patternSourceFilter, - scrollContainerRef, - category -}) { - // If the category is `myPatterns` then we need to set the source filter to `user`, but - // we do this by deriving from props rather than calling setPatternSourceFilter otherwise - // the user may be confused when switching to another category if the haven't explicity set - // this filter themselves. - const currentPatternSourceFilter = category.name === myPatternsCategory.name ? PATTERN_TYPES.user : patternSourceFilter; - // We need to disable the sync filter option if the source filter is not 'all' or 'user' - // otherwise applying them will just result in no patterns being shown. - const shouldDisableSyncFilter = getShouldDisableSyncFilter(currentPatternSourceFilter); +/** + * Returns the corresponding font size object for a given value. + * + * @param {Array} fontSizes Array of font size objects. + * @param {number} value Font size value. + * + * @return {Object} Font size object. + */ +function utils_getFontSizeObjectByValue(fontSizes, value) { + const fontSizeObject = fontSizes?.find(({ + size + }) => size === value); + if (fontSizeObject) { + return fontSizeObject; + } + return { + size: value + }; +} - // We also need to disable the directory and theme source filter options if the category - // is `myPatterns` otherwise applying them will also just result in no patterns being shown. - const shouldDisableNonUserSources = getShouldDisableNonUserSources(category); - const patternSyncMenuOptions = (0,external_wp_element_namespaceObject.useMemo)(() => [{ - value: SYNC_TYPES.all, - label: (0,external_wp_i18n_namespaceObject._x)('All', 'Option that shows all patterns') - }, { - value: SYNC_TYPES.full, - label: (0,external_wp_i18n_namespaceObject._x)('Synced', 'Option that shows all synchronized patterns'), - disabled: shouldDisableSyncFilter - }, { - value: SYNC_TYPES.unsynced, - label: (0,external_wp_i18n_namespaceObject._x)('Not synced', 'Option that shows all patterns that are not synchronized'), - disabled: shouldDisableSyncFilter - }], [shouldDisableSyncFilter]); - const patternSourceMenuOptions = (0,external_wp_element_namespaceObject.useMemo)(() => [{ - value: PATTERN_TYPES.all, - label: (0,external_wp_i18n_namespaceObject.__)('All'), - disabled: shouldDisableNonUserSources - }, { - value: PATTERN_TYPES.directory, - label: (0,external_wp_i18n_namespaceObject.__)('Pattern Directory'), - disabled: shouldDisableNonUserSources - }, { - value: PATTERN_TYPES.theme, - label: (0,external_wp_i18n_namespaceObject.__)('Theme & Plugins'), - disabled: shouldDisableNonUserSources - }, { - value: PATTERN_TYPES.user, - label: (0,external_wp_i18n_namespaceObject.__)('User') - }], [shouldDisableNonUserSources]); - function handleSetSourceFilterChange(newSourceFilter) { - setPatternSourceFilter(newSourceFilter); - if (getShouldDisableSyncFilter(newSourceFilter)) { - setPatternSyncFilter(SYNC_TYPES.all); - } +/** + * Returns a class based on fontSizeName. + * + * @param {string} fontSizeSlug Slug of the fontSize. + * + * @return {string | undefined} String with the class corresponding to the fontSize passed. + * The class is generated by appending 'has-' followed by fontSizeSlug in kebabCase and ending with '-font-size'. + */ +function getFontSizeClass(fontSizeSlug) { + if (!fontSizeSlug) { + return; } - return (0,external_wp_element_namespaceObject.createElement)(external_wp_element_namespaceObject.Fragment, null, (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.DropdownMenu, { - popoverProps: { - placement: 'right-end' - }, - label: "Filter patterns", - icon: (0,external_wp_element_namespaceObject.createElement)(build_module_icon, { - icon: (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.SVG, { - width: "24", - height: "24", - viewBox: "0 0 24 24", - fill: "none", - xmlns: "http://www.w3.org/2000/svg" - }, (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.Path, { - d: "M10 17.5H14V16H10V17.5ZM6 6V7.5H18V6H6ZM8 12.5H16V11H8V12.5Z", - fill: "#1E1E1E" - })) - }) - }, () => (0,external_wp_element_namespaceObject.createElement)(external_wp_element_namespaceObject.Fragment, null, (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.MenuGroup, { - label: (0,external_wp_i18n_namespaceObject.__)('Source') - }, (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.MenuItemsChoice, { - choices: patternSourceMenuOptions, - onSelect: value => { - handleSetSourceFilterChange(value); - scrollContainerRef.current?.scrollTo(0, 0); - }, - value: currentPatternSourceFilter - })), (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.MenuGroup, { - label: (0,external_wp_i18n_namespaceObject.__)('Type') - }, (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.MenuItemsChoice, { - choices: patternSyncMenuOptions, - onSelect: value => { - setPatternSyncFilter(value); - scrollContainerRef.current?.scrollTo(0, 0); - }, - value: patternSyncFilter - })), (0,external_wp_element_namespaceObject.createElement)("div", { - className: "block-editor-tool-selector__help" - }, (0,external_wp_element_namespaceObject.createInterpolateElement)((0,external_wp_i18n_namespaceObject.__)('Patterns are available from the WordPress.org Pattern Directory, bundled in the active theme, or created by users on this site. Only patterns created on this site can be synced.'), { - Link: (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.ExternalLink, { - href: (0,external_wp_i18n_namespaceObject.__)('https://wordpress.org/patterns/') - }) - }))))); + const { + kebabCase + } = unlock(external_wp_components_namespaceObject.privateApis); + return `has-${kebabCase(fontSizeSlug)}-font-size`; } -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/inserter/block-patterns-tab.js +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/hooks/font-size.js /** * WordPress dependencies @@ -24207,10 +28629,6 @@ function BlockPatternsSyncFilter({ - - - - /** * Internal dependencies */ @@ -24219,694 +28637,781 @@ function BlockPatternsSyncFilter({ +const FONT_SIZE_SUPPORT_KEY = 'typography.fontSize'; -const block_patterns_tab_noop = () => {}; -const allPatternsCategory = { - name: 'allPatterns', - label: (0,external_wp_i18n_namespaceObject.__)('All patterns') -}; -const myPatternsCategory = { - name: 'myPatterns', - label: (0,external_wp_i18n_namespaceObject.__)('My patterns') -}; -function isPatternFiltered(pattern, sourceFilter, syncFilter) { - const isUserPattern = pattern.name.startsWith('core/block'); - const isDirectoryPattern = pattern.source === 'core' || pattern.source?.startsWith('pattern-directory'); - - // If theme source selected, filter out user created patterns and those from - // the core patterns directory. - if (sourceFilter === PATTERN_TYPES.theme && (isUserPattern || isDirectoryPattern)) { - return true; - } - - // If the directory source is selected, filter out user created patterns - // and those bundled with the theme. - if (sourceFilter === PATTERN_TYPES.directory && (isUserPattern || !isDirectoryPattern)) { - return true; +/** + * Filters registered block settings, extending attributes to include + * `fontSize` and `fontWeight` attributes. + * + * @param {Object} settings Original block settings. + * + * @return {Object} Filtered block settings. + */ +function font_size_addAttributes(settings) { + if (!(0,external_wp_blocks_namespaceObject.hasBlockSupport)(settings, FONT_SIZE_SUPPORT_KEY)) { + return settings; } - // If user source selected, filter out theme patterns. Any pattern without - // an id wasn't created by a user. - if (sourceFilter === PATTERN_TYPES.user && !pattern.id) { - return true; + // Allow blocks to specify a default value if needed. + if (!settings.attributes.fontSize) { + Object.assign(settings.attributes, { + fontSize: { + type: 'string' + } + }); } + return settings; +} - // Filter by sync status. - if (syncFilter === SYNC_TYPES.full && pattern.syncStatus !== '') { - return true; +/** + * Override props assigned to save component to inject font size. + * + * @param {Object} props Additional props applied to save element. + * @param {Object} blockNameOrType Block type. + * @param {Object} attributes Block attributes. + * + * @return {Object} Filtered props applied to save element. + */ +function font_size_addSaveProps(props, blockNameOrType, attributes) { + if (!(0,external_wp_blocks_namespaceObject.hasBlockSupport)(blockNameOrType, FONT_SIZE_SUPPORT_KEY)) { + return props; } - if (syncFilter === SYNC_TYPES.unsynced && pattern.syncStatus !== 'unsynced' && isUserPattern) { - return true; + if (shouldSkipSerialization(blockNameOrType, TYPOGRAPHY_SUPPORT_KEY, 'fontSize')) { + return props; } - return false; -} -function usePatternsCategories(rootClientId, sourceFilter = 'all') { - const [patterns, allCategories] = use_patterns_state(undefined, rootClientId); - const filteredPatterns = (0,external_wp_element_namespaceObject.useMemo)(() => sourceFilter === 'all' ? patterns : patterns.filter(pattern => !isPatternFiltered(pattern, sourceFilter)), [sourceFilter, patterns]); - const hasRegisteredCategory = (0,external_wp_element_namespaceObject.useCallback)(pattern => { - if (!pattern.categories || !pattern.categories.length) { - return false; - } - return pattern.categories.some(cat => allCategories.some(category => category.name === cat)); - }, [allCategories]); - // Remove any empty categories. - const populatedCategories = (0,external_wp_element_namespaceObject.useMemo)(() => { - const categories = allCategories.filter(category => filteredPatterns.some(pattern => pattern.categories?.includes(category.name))).sort((a, b) => a.label.localeCompare(b.label)); - if (filteredPatterns.some(pattern => !hasRegisteredCategory(pattern)) && !categories.find(category => category.name === 'uncategorized')) { - categories.push({ - name: 'uncategorized', - label: (0,external_wp_i18n_namespaceObject._x)('Uncategorized') - }); - } - if (filteredPatterns.some(pattern => pattern.id)) { - categories.unshift(myPatternsCategory); - } - if (filteredPatterns.length > 0) { - categories.unshift({ - name: allPatternsCategory.name, - label: allPatternsCategory.label - }); - } - (0,external_wp_a11y_namespaceObject.speak)((0,external_wp_i18n_namespaceObject.sprintf)( /* translators: %d: number of categories . */ - (0,external_wp_i18n_namespaceObject._n)('%d category button displayed.', '%d category buttons displayed.', categories.length), categories.length)); - return categories; - }, [allCategories, filteredPatterns, hasRegisteredCategory]); - return populatedCategories; -} -function BlockPatternsCategoryDialog({ - rootClientId, - onInsert, - onHover, - category, - showTitlesAsTooltip, - patternFilter -}) { - const container = (0,external_wp_element_namespaceObject.useRef)(); - (0,external_wp_element_namespaceObject.useEffect)(() => { - const timeout = setTimeout(() => { - const [firstTabbable] = external_wp_dom_namespaceObject.focus.tabbable.find(container.current); - firstTabbable?.focus(); - }); - return () => clearTimeout(timeout); - }, [category]); - return (0,external_wp_element_namespaceObject.createElement)("div", { - ref: container, - className: "block-editor-inserter__patterns-category-dialog" - }, (0,external_wp_element_namespaceObject.createElement)(BlockPatternsCategoryPanel, { - key: category.name, - rootClientId: rootClientId, - onInsert: onInsert, - onHover: onHover, - category: category, - showTitlesAsTooltip: showTitlesAsTooltip, - patternFilter: patternFilter - })); + // Use TokenList to dedupe classes. + const classes = new (external_wp_tokenList_default())(props.className); + classes.add(getFontSizeClass(attributes.fontSize)); + const newClassName = classes.value; + props.className = newClassName ? newClassName : undefined; + return props; } -function BlockPatternsCategoryPanel({ - rootClientId, - onInsert, - onHover = block_patterns_tab_noop, - category, - showTitlesAsTooltip -}) { - const [allPatterns,, onClickPattern] = use_patterns_state(onInsert, rootClientId); - const [patternSyncFilter, setPatternSyncFilter] = (0,external_wp_element_namespaceObject.useState)('all'); - const [patternSourceFilter, setPatternSourceFilter] = (0,external_wp_element_namespaceObject.useState)('all'); - const availableCategories = usePatternsCategories(rootClientId, patternSourceFilter); - const scrollContainerRef = (0,external_wp_element_namespaceObject.useRef)(); - const currentCategoryPatterns = (0,external_wp_element_namespaceObject.useMemo)(() => allPatterns.filter(pattern => { - var _pattern$categories$f; - if (isPatternFiltered(pattern, patternSourceFilter, patternSyncFilter)) { - return false; - } - if (category.name === allPatternsCategory.name) { - return true; - } - if (category.name === myPatternsCategory.name && pattern.id) { - return true; - } - if (category.name !== 'uncategorized') { - return pattern.categories?.includes(category.name); - } - - // The uncategorized category should show all the patterns without any category - // or with no available category. - const availablePatternCategories = (_pattern$categories$f = pattern.categories?.filter(cat => availableCategories.find(availableCategory => availableCategory.name === cat))) !== null && _pattern$categories$f !== void 0 ? _pattern$categories$f : []; - return availablePatternCategories.length === 0; - }), [allPatterns, availableCategories, category.name, patternSourceFilter, patternSyncFilter]); - const pagingProps = usePatternsPaging(currentCategoryPatterns, category, scrollContainerRef); - const { - changePage - } = pagingProps; - // Hide block pattern preview on unmount. - // eslint-disable-next-line react-hooks/exhaustive-deps - (0,external_wp_element_namespaceObject.useEffect)(() => () => onHover(null), []); - const onSetPatternSyncFilter = (0,external_wp_element_namespaceObject.useCallback)(value => { - setPatternSyncFilter(value); - changePage(1); - }, [setPatternSyncFilter, changePage]); - const onSetPatternSourceFilter = (0,external_wp_element_namespaceObject.useCallback)(value => { - setPatternSourceFilter(value); - changePage(1); - }, [setPatternSourceFilter, changePage]); - return (0,external_wp_element_namespaceObject.createElement)("div", { - className: "block-editor-inserter__patterns-category-panel" - }, (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.__experimentalVStack, { - spacing: 2, - className: "block-editor-inserter__patterns-category-panel-header" - }, (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.__experimentalHStack, null, (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.FlexBlock, null, (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.__experimentalHeading, { - level: 4, - as: "div" - }, category.label)), (0,external_wp_element_namespaceObject.createElement)(BlockPatternsSyncFilter, { - patternSyncFilter: patternSyncFilter, - patternSourceFilter: patternSourceFilter, - setPatternSyncFilter: onSetPatternSyncFilter, - setPatternSourceFilter: onSetPatternSourceFilter, - scrollContainerRef: scrollContainerRef, - category: category - })), !currentCategoryPatterns.length && (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.__experimentalText, { - variant: "muted", - className: "block-editor-inserter__patterns-category-no-results" - }, (0,external_wp_i18n_namespaceObject.__)('No results found'))), currentCategoryPatterns.length > 0 && (0,external_wp_element_namespaceObject.createElement)(block_patterns_list, { - ref: scrollContainerRef, - shownPatterns: pagingProps.categoryPatternsAsyncList, - blockPatterns: pagingProps.categoryPatterns, - onClickPattern: onClickPattern, - onHover: onHover, - label: category.label, - orientation: "vertical", - category: category.name, - isDraggable: true, - showTitlesAsTooltip: showTitlesAsTooltip, - patternFilter: patternSourceFilter, - pagingProps: pagingProps - })); +/** + * Inspector control panel containing the font size related configuration + * + * @param {Object} props + * + * @return {Element} Font size edit element. + */ +function FontSizeEdit(props) { + const { + attributes: { + fontSize, + style + }, + setAttributes + } = props; + const [fontSizes] = useSettings('typography.fontSizes'); + const onChange = value => { + const fontSizeSlug = getFontSizeObjectByValue(fontSizes, value).slug; + setAttributes({ + style: cleanEmptyObject({ + ...style, + typography: { + ...style?.typography, + fontSize: fontSizeSlug ? undefined : value + } + }), + fontSize: fontSizeSlug + }); + }; + const fontSizeObject = getFontSize(fontSizes, fontSize, style?.typography?.fontSize); + const fontSizeValue = fontSizeObject?.size || style?.typography?.fontSize || fontSize; + return createElement(FontSizePicker, { + onChange: onChange, + value: fontSizeValue, + withReset: false, + withSlider: true, + size: "__unstable-large" + }); } -function BlockPatternsTabs({ - onSelectCategory, - selectedCategory, - onInsert, - rootClientId + +/** + * Custom hook that checks if font-size settings have been disabled. + * + * @param {string} name The name of the block. + * @return {boolean} Whether setting is disabled. + */ +function useIsFontSizeDisabled({ + name: blockName +} = {}) { + const [fontSizes] = useSettings('typography.fontSizes'); + const hasFontSizes = !!fontSizes?.length; + return !hasBlockSupport(blockName, FONT_SIZE_SUPPORT_KEY) || !hasFontSizes; +} +function font_size_useBlockProps({ + name, + fontSize, + style }) { - const [showPatternsExplorer, setShowPatternsExplorer] = (0,external_wp_element_namespaceObject.useState)(false); - const categories = usePatternsCategories(rootClientId); - const initialCategory = selectedCategory || categories[0]; - const isMobile = (0,external_wp_compose_namespaceObject.useViewportMatch)('medium', '<'); - return (0,external_wp_element_namespaceObject.createElement)(external_wp_element_namespaceObject.Fragment, null, !isMobile && (0,external_wp_element_namespaceObject.createElement)("div", { - className: "block-editor-inserter__block-patterns-tabs-container" - }, (0,external_wp_element_namespaceObject.createElement)("nav", { - "aria-label": (0,external_wp_i18n_namespaceObject.__)('Block pattern categories'), - className: "block-editor-inserter__block-patterns-tabs" - }, (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.__experimentalItemGroup, { - role: "list" - }, categories.map(category => (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.__experimentalItem, { - role: "listitem", - key: category.name, - onClick: () => onSelectCategory(category), - className: category === selectedCategory ? 'block-editor-inserter__patterns-category block-editor-inserter__patterns-selected-category' : 'block-editor-inserter__patterns-category', - "aria-label": category.label, - "aria-current": category === selectedCategory ? 'true' : undefined - }, (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.__experimentalHStack, null, (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.FlexBlock, null, category.label), (0,external_wp_element_namespaceObject.createElement)(build_module_icon, { - icon: (0,external_wp_i18n_namespaceObject.isRTL)() ? chevron_left : chevron_right - })))), (0,external_wp_element_namespaceObject.createElement)("div", { - role: "listitem" - }, (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.Button, { - className: "block-editor-inserter__patterns-explore-button", - onClick: () => setShowPatternsExplorer(true), - variant: "secondary" - }, (0,external_wp_i18n_namespaceObject.__)('Explore all patterns')))))), isMobile && (0,external_wp_element_namespaceObject.createElement)(MobileTabNavigation, { - categories: categories - }, category => (0,external_wp_element_namespaceObject.createElement)(BlockPatternsCategoryPanel, { - key: category.name, - onInsert: onInsert, - rootClientId: rootClientId, - category: category, - showTitlesAsTooltip: false - })), showPatternsExplorer && (0,external_wp_element_namespaceObject.createElement)(explorer, { - initialCategory: initialCategory, - patternCategories: categories, - onModalClose: () => setShowPatternsExplorer(false), - rootClientId: rootClientId - })); + const [fontSizes, fluidTypographySettings, layoutSettings] = use_settings_useSettings('typography.fontSizes', 'typography.fluid', 'layout'); + + /* + * Only add inline styles if the block supports font sizes, + * doesn't skip serialization of font sizes, + * and has either a custom font size or a preset font size. + */ + if (!(0,external_wp_blocks_namespaceObject.hasBlockSupport)(name, FONT_SIZE_SUPPORT_KEY) || shouldSkipSerialization(name, TYPOGRAPHY_SUPPORT_KEY, 'fontSize') || !fontSize && !style?.typography?.fontSize) { + return; + } + let props; + if (style?.typography?.fontSize) { + const fluidSettings = getFluidTypographyOptionsFromSettings({ + typography: { + fluid: fluidTypographySettings + }, + layout: layoutSettings + }); + props = { + style: { + fontSize: getTypographyFontSizeValue({ + size: style.typography.fontSize + }, fluidSettings) + } + }; + } + if (fontSize) { + props = { + style: { + fontSize: utils_getFontSize(fontSizes, fontSize, style?.typography?.fontSize).size + } + }; + } + if (!props) { + return; + } + return font_size_addSaveProps(props, name, { + fontSize + }); +} +/* harmony default export */ const font_size = ({ + useBlockProps: font_size_useBlockProps, + addSaveProps: font_size_addSaveProps, + attributeKeys: ['fontSize', 'style'], + hasSupport(name) { + return (0,external_wp_blocks_namespaceObject.hasBlockSupport)(name, FONT_SIZE_SUPPORT_KEY); + } +}); +const font_size_MIGRATION_PATHS = { + fontSize: [['fontSize'], ['style', 'typography', 'fontSize']] +}; +function font_size_addTransforms(result, source, index, results) { + const destinationBlockType = result.name; + const activeSupports = { + fontSize: (0,external_wp_blocks_namespaceObject.hasBlockSupport)(destinationBlockType, FONT_SIZE_SUPPORT_KEY) + }; + return transformStyles(activeSupports, font_size_MIGRATION_PATHS, result, source, index, results); } -/* harmony default export */ var block_patterns_tab = (BlockPatternsTabs); +(0,external_wp_hooks_namespaceObject.addFilter)('blocks.registerBlockType', 'core/font/addAttribute', font_size_addAttributes); +(0,external_wp_hooks_namespaceObject.addFilter)('blocks.switchToBlockType.transformedBlock', 'core/font-size/addTransforms', font_size_addTransforms); + +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/hooks/typography.js -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/inserter/media-tab/hooks.js /** * WordPress dependencies */ + /** * Internal dependencies */ -/** @typedef {import('../../../store/actions').InserterMediaRequest} InserterMediaRequest */ -/** @typedef {import('../../../store/actions').InserterMediaItem} InserterMediaItem */ -/** - * Fetches media items based on the provided category. - * Each media category is responsible for providing a `fetch` function. - * - * @param {Object} category The media category to fetch results for. - * @param {InserterMediaRequest} query The query args to use for the request. - * @return {InserterMediaItem[]} The media results. - */ -function useMediaResults(category, query = {}) { - const [mediaList, setMediaList] = (0,external_wp_element_namespaceObject.useState)(); - const [isLoading, setIsLoading] = (0,external_wp_element_namespaceObject.useState)(false); - // We need to keep track of the last request made because - // multiple request can be fired without knowing the order - // of resolution, and we need to ensure we are showing - // the results of the last request. - // In the future we could use AbortController to cancel previous - // requests, but we don't for now as it involves adding support - // for this to `core-data` package. - const lastRequest = (0,external_wp_element_namespaceObject.useRef)(); - (0,external_wp_element_namespaceObject.useEffect)(() => { - (async () => { - const key = JSON.stringify({ - category: category.name, - ...query - }); - lastRequest.current = key; - setIsLoading(true); - setMediaList([]); // Empty the previous results. - const _media = await category.fetch?.(query); - if (key === lastRequest.current) { - setMediaList(_media); - setIsLoading(false); - } - })(); - }, [category.name, ...Object.values(query)]); + + + +function omit(object, keys) { + return Object.fromEntries(Object.entries(object).filter(([key]) => !keys.includes(key))); +} +const LETTER_SPACING_SUPPORT_KEY = 'typography.__experimentalLetterSpacing'; +const TEXT_TRANSFORM_SUPPORT_KEY = 'typography.__experimentalTextTransform'; +const TEXT_DECORATION_SUPPORT_KEY = 'typography.__experimentalTextDecoration'; +const TEXT_COLUMNS_SUPPORT_KEY = 'typography.textColumns'; +const FONT_STYLE_SUPPORT_KEY = 'typography.__experimentalFontStyle'; +const FONT_WEIGHT_SUPPORT_KEY = 'typography.__experimentalFontWeight'; +const WRITING_MODE_SUPPORT_KEY = 'typography.__experimentalWritingMode'; +const TYPOGRAPHY_SUPPORT_KEY = 'typography'; +const TYPOGRAPHY_SUPPORT_KEYS = [LINE_HEIGHT_SUPPORT_KEY, FONT_SIZE_SUPPORT_KEY, FONT_STYLE_SUPPORT_KEY, FONT_WEIGHT_SUPPORT_KEY, FONT_FAMILY_SUPPORT_KEY, TEXT_COLUMNS_SUPPORT_KEY, TEXT_DECORATION_SUPPORT_KEY, WRITING_MODE_SUPPORT_KEY, TEXT_TRANSFORM_SUPPORT_KEY, LETTER_SPACING_SUPPORT_KEY]; +function typography_styleToAttributes(style) { + const updatedStyle = { + ...omit(style, ['fontFamily']) + }; + const fontSizeValue = style?.typography?.fontSize; + const fontFamilyValue = style?.typography?.fontFamily; + const fontSizeSlug = fontSizeValue?.startsWith('var:preset|font-size|') ? fontSizeValue.substring('var:preset|font-size|'.length) : undefined; + const fontFamilySlug = fontFamilyValue?.startsWith('var:preset|font-family|') ? fontFamilyValue.substring('var:preset|font-family|'.length) : undefined; + updatedStyle.typography = { + ...omit(updatedStyle.typography, ['fontFamily']), + fontSize: fontSizeSlug ? undefined : fontSizeValue + }; return { - mediaList, - isLoading + style: utils_cleanEmptyObject(updatedStyle), + fontFamily: fontFamilySlug, + fontSize: fontSizeSlug }; } -function useMediaCategories(rootClientId) { - const [categories, setCategories] = (0,external_wp_element_namespaceObject.useState)([]); - const inserterMediaCategories = (0,external_wp_data_namespaceObject.useSelect)(select => unlock(select(store)).getInserterMediaCategories(), []); - const { - canInsertImage, - canInsertVideo, - canInsertAudio - } = (0,external_wp_data_namespaceObject.useSelect)(select => { +function typography_attributesToStyle(attributes) { + return { + ...attributes.style, + typography: { + ...attributes.style?.typography, + fontFamily: attributes.fontFamily ? 'var:preset|font-family|' + attributes.fontFamily : undefined, + fontSize: attributes.fontSize ? 'var:preset|font-size|' + attributes.fontSize : attributes.style?.typography?.fontSize + } + }; +} +function TypographyInspectorControl({ + children, + resetAllFilter +}) { + const attributesResetAllFilter = (0,external_wp_element_namespaceObject.useCallback)(attributes => { + const existingStyle = typography_attributesToStyle(attributes); + const updatedStyle = resetAllFilter(existingStyle); + return { + ...attributes, + ...typography_styleToAttributes(updatedStyle) + }; + }, [resetAllFilter]); + return (0,external_React_.createElement)(inspector_controls, { + group: "typography", + resetAllFilter: attributesResetAllFilter + }, children); +} +function typography_TypographyPanel({ + clientId, + name, + setAttributes, + settings +}) { + function selector(select) { const { - canInsertBlockType - } = select(store); + style, + fontFamily, + fontSize + } = select(store).getBlockAttributes(clientId) || {}; return { - canInsertImage: canInsertBlockType('core/image', rootClientId), - canInsertVideo: canInsertBlockType('core/video', rootClientId), - canInsertAudio: canInsertBlockType('core/audio', rootClientId) + style, + fontFamily, + fontSize }; - }, [rootClientId]); - (0,external_wp_element_namespaceObject.useEffect)(() => { - (async () => { - const _categories = []; - // If `inserterMediaCategories` is not defined in - // block editor settings, do not show any media categories. - if (!inserterMediaCategories) { - return; - } - // Loop through categories to check if they have at least one media item. - const categoriesHaveMedia = new Map(await Promise.all(inserterMediaCategories.map(async category => { - // Some sources are external and we don't need to make a request. - if (category.isExternalResource) { - return [category.name, true]; - } - let results = []; - try { - results = await category.fetch({ - per_page: 1 - }); - } catch (e) { - // If the request fails, we shallow the error and just don't show - // the category, in order to not break the media tab. - } - return [category.name, !!results.length]; - }))); - // We need to filter out categories that don't have any media items or - // whose corresponding block type is not allowed to be inserted, based - // on the category's `mediaType`. - const canInsertMediaType = { - image: canInsertImage, - video: canInsertVideo, - audio: canInsertAudio - }; - inserterMediaCategories.forEach(category => { - if (canInsertMediaType[category.mediaType] && categoriesHaveMedia.get(category.name)) { - _categories.push(category); - } - }); - if (!!_categories.length) { - setCategories(_categories); - } - })(); - }, [canInsertImage, canInsertVideo, canInsertAudio, inserterMediaCategories]); - return categories; + } + const { + style, + fontFamily, + fontSize + } = (0,external_wp_data_namespaceObject.useSelect)(selector, [clientId]); + const isEnabled = useHasTypographyPanel(settings); + const value = (0,external_wp_element_namespaceObject.useMemo)(() => typography_attributesToStyle({ + style, + fontFamily, + fontSize + }), [style, fontSize, fontFamily]); + const onChange = newStyle => { + setAttributes(typography_styleToAttributes(newStyle)); + }; + if (!isEnabled) { + return null; + } + const defaultControls = (0,external_wp_blocks_namespaceObject.getBlockSupport)(name, [TYPOGRAPHY_SUPPORT_KEY, '__experimentalDefaultControls']); + return (0,external_React_.createElement)(TypographyPanel, { + as: TypographyInspectorControl, + panelId: clientId, + settings: settings, + value: value, + onChange: onChange, + defaultControls: defaultControls + }); } +const hasTypographySupport = blockName => { + return TYPOGRAPHY_SUPPORT_KEYS.some(key => hasBlockSupport(blockName, key)); +}; -;// CONCATENATED MODULE: ./node_modules/@wordpress/icons/build-module/library/external.js +;// CONCATENATED MODULE: ./node_modules/@wordpress/icons/build-module/library/settings.js /** * WordPress dependencies */ -const external = (0,external_wp_element_namespaceObject.createElement)(external_wp_primitives_namespaceObject.SVG, { +const settings_settings = (0,external_React_.createElement)(external_wp_primitives_namespaceObject.SVG, { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24" -}, (0,external_wp_element_namespaceObject.createElement)(external_wp_primitives_namespaceObject.Path, { - d: "M19.5 4.5h-7V6h4.44l-5.97 5.97 1.06 1.06L18 7.06v4.44h1.5v-7Zm-13 1a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2v-3H17v3a.5.5 0 0 1-.5.5h-10a.5.5 0 0 1-.5-.5v-10a.5.5 0 0 1 .5-.5h3V5.5h-3Z" +}, (0,external_React_.createElement)(external_wp_primitives_namespaceObject.Path, { + d: "m19 7.5h-7.628c-.3089-.87389-1.1423-1.5-2.122-1.5-.97966 0-1.81309.62611-2.12197 1.5h-2.12803v1.5h2.12803c.30888.87389 1.14231 1.5 2.12197 1.5.9797 0 1.8131-.62611 2.122-1.5h7.628z" +}), (0,external_React_.createElement)(external_wp_primitives_namespaceObject.Path, { + d: "m19 15h-2.128c-.3089-.8739-1.1423-1.5-2.122-1.5s-1.8131.6261-2.122 1.5h-7.628v1.5h7.628c.3089.8739 1.1423 1.5 2.122 1.5s1.8131-.6261 2.122-1.5h2.128z" })); -/* harmony default export */ var library_external = (external); +/* harmony default export */ const library_settings = (settings_settings); -;// CONCATENATED MODULE: external ["wp","blob"] -var external_wp_blob_namespaceObject = window["wp"]["blob"]; -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/inserter/media-tab/utils.js +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/spacing-sizes-control/input-controls/spacing-input-control.js /** * WordPress dependencies */ -const mediaTypeTag = { - image: 'img', - video: 'video', - audio: 'audio' -}; -/** @typedef {import('./hooks').InserterMediaItem} InserterMediaItem */ + + + + /** - * Creates a block and a preview element from a media object. - * - * @param {InserterMediaItem} media The media object to create the block from. - * @param {('image'|'audio'|'video')} mediaType The media type to create the block for. - * @return {[WPBlock, JSX.Element]} An array containing the block and the preview element. + * Internal dependencies */ -function getBlockAndPreviewFromMedia(media, mediaType) { - // Add the common attributes between the different media types. - const attributes = { - id: media.id || undefined, - caption: media.caption || undefined - }; - const mediaSrc = media.url; - const alt = media.alt || undefined; - if (mediaType === 'image') { - attributes.url = mediaSrc; - attributes.alt = alt; - } else if (['video', 'audio'].includes(mediaType)) { - attributes.src = mediaSrc; + + + +const CUSTOM_VALUE_SETTINGS = { + px: { + max: 300, + steps: 1 + }, + '%': { + max: 100, + steps: 1 + }, + vw: { + max: 100, + steps: 1 + }, + vh: { + max: 100, + steps: 1 + }, + em: { + max: 10, + steps: 0.1 + }, + rm: { + max: 10, + steps: 0.1 + }, + svw: { + max: 100, + steps: 1 + }, + lvw: { + max: 100, + steps: 1 + }, + dvw: { + max: 100, + steps: 1 + }, + svh: { + max: 100, + steps: 1 + }, + lvh: { + max: 100, + steps: 1 + }, + dvh: { + max: 100, + steps: 1 + }, + vi: { + max: 100, + steps: 1 + }, + svi: { + max: 100, + steps: 1 + }, + lvi: { + max: 100, + steps: 1 + }, + dvi: { + max: 100, + steps: 1 + }, + vb: { + max: 100, + steps: 1 + }, + svb: { + max: 100, + steps: 1 + }, + lvb: { + max: 100, + steps: 1 + }, + dvb: { + max: 100, + steps: 1 + }, + vmin: { + max: 100, + steps: 1 + }, + svmin: { + max: 100, + steps: 1 + }, + lvmin: { + max: 100, + steps: 1 + }, + dvmin: { + max: 100, + steps: 1 + }, + vmax: { + max: 100, + steps: 1 + }, + svmax: { + max: 100, + steps: 1 + }, + lvmax: { + max: 100, + steps: 1 + }, + dvmax: { + max: 100, + steps: 1 } - const PreviewTag = mediaTypeTag[mediaType]; - const preview = (0,external_wp_element_namespaceObject.createElement)(PreviewTag, { - src: media.previewUrl || mediaSrc, - alt: alt, - controls: mediaType === 'audio' ? true : undefined, - inert: "true", - onError: ({ - currentTarget - }) => { - // Fall back to the media source if the preview cannot be loaded. - if (currentTarget.src === media.previewUrl) { - currentTarget.src = mediaSrc; +}; +function SpacingInputControl({ + icon, + isMixed = false, + minimumCustomValue, + onChange, + onMouseOut, + onMouseOver, + showSideInLabel = true, + side, + spacingSizes, + type, + value +}) { + var _CUSTOM_VALUE_SETTING, _CUSTOM_VALUE_SETTING2; + // Treat value as a preset value if the passed in value matches the value of one of the spacingSizes. + value = getPresetValueFromCustomValue(value, spacingSizes); + let selectListSizes = spacingSizes; + const showRangeControl = spacingSizes.length <= 8; + const disableCustomSpacingSizes = (0,external_wp_data_namespaceObject.useSelect)(select => { + const editorSettings = select(store).getSettings(); + return editorSettings?.disableCustomSpacingSizes; + }); + const [showCustomValueControl, setShowCustomValueControl] = (0,external_wp_element_namespaceObject.useState)(!disableCustomSpacingSizes && value !== undefined && !isValueSpacingPreset(value)); + const previousValue = (0,external_wp_compose_namespaceObject.usePrevious)(value); + if (!!value && previousValue !== value && !isValueSpacingPreset(value) && showCustomValueControl !== true) { + setShowCustomValueControl(true); + } + const [availableUnits] = use_settings_useSettings('spacing.units'); + const units = (0,external_wp_components_namespaceObject.__experimentalUseCustomUnits)({ + availableUnits: availableUnits || ['px', 'em', 'rem'] + }); + let currentValue = null; + const showCustomValueInSelectList = !showRangeControl && !showCustomValueControl && value !== undefined && (!isValueSpacingPreset(value) || isValueSpacingPreset(value) && isMixed); + if (showCustomValueInSelectList) { + selectListSizes = [...spacingSizes, { + name: !isMixed ? + // translators: A custom measurement, eg. a number followed by a unit like 12px. + (0,external_wp_i18n_namespaceObject.sprintf)((0,external_wp_i18n_namespaceObject.__)('Custom (%s)'), value) : (0,external_wp_i18n_namespaceObject.__)('Mixed'), + slug: 'custom', + size: value + }]; + currentValue = selectListSizes.length - 1; + } else if (!isMixed) { + currentValue = !showCustomValueControl ? getSliderValueFromPreset(value, spacingSizes) : getCustomValueFromPreset(value, spacingSizes); + } + const selectedUnit = (0,external_wp_element_namespaceObject.useMemo)(() => (0,external_wp_components_namespaceObject.__experimentalParseQuantityAndUnitFromRawValue)(currentValue), [currentValue])[1] || units[0]?.value; + const setInitialValue = () => { + if (value === undefined) { + onChange('0'); + } + }; + const customTooltipContent = newValue => value === undefined ? undefined : spacingSizes[newValue]?.name; + const customRangeValue = parseFloat(currentValue, 10); + const getNewCustomValue = newSize => { + const isNumeric = !isNaN(parseFloat(newSize)); + const nextValue = isNumeric ? newSize : undefined; + return nextValue; + }; + const getNewPresetValue = (newSize, controlType) => { + const size = parseInt(newSize, 10); + if (controlType === 'selectList') { + if (size === 0) { + return undefined; + } + if (size === 1) { + return '0'; } + } else if (size === 0) { + return '0'; } - }); - return [(0,external_wp_blocks_namespaceObject.createBlock)(`core/${mediaType}`, attributes), preview]; + return `var:preset|spacing|${spacingSizes[newSize]?.slug}`; + }; + const handleCustomValueSliderChange = next => { + onChange([next, selectedUnit].join('')); + }; + const allPlaceholder = isMixed ? (0,external_wp_i18n_namespaceObject.__)('Mixed') : null; + const options = selectListSizes.map((size, index) => ({ + key: index, + name: size.name + })); + const marks = spacingSizes.map((_newValue, index) => ({ + value: index, + label: undefined + })); + const sideLabel = ALL_SIDES.includes(side) && showSideInLabel ? LABELS[side] : ''; + const typeLabel = showSideInLabel ? type?.toLowerCase() : type; + const ariaLabel = (0,external_wp_i18n_namespaceObject.sprintf)( + // translators: 1: The side of the block being modified (top, bottom, left, All sides etc.). 2. Type of spacing being modified (Padding, margin, etc) + (0,external_wp_i18n_namespaceObject.__)('%1$s %2$s'), sideLabel, typeLabel).trim(); + return (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalHStack, { + className: "spacing-sizes-control__wrapper" + }, icon && (0,external_React_.createElement)(external_wp_components_namespaceObject.Icon, { + className: "spacing-sizes-control__icon", + icon: icon, + size: 24 + }), showCustomValueControl && (0,external_React_.createElement)(external_React_.Fragment, null, (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalUnitControl, { + onMouseOver: onMouseOver, + onMouseOut: onMouseOut, + onFocus: onMouseOver, + onBlur: onMouseOut, + onChange: newSize => onChange(getNewCustomValue(newSize)), + value: currentValue, + units: units, + min: minimumCustomValue, + placeholder: allPlaceholder, + disableUnits: isMixed, + label: ariaLabel, + hideLabelFromVision: true, + className: "spacing-sizes-control__custom-value-input", + size: '__unstable-large' + }), (0,external_React_.createElement)(external_wp_components_namespaceObject.RangeControl, { + onMouseOver: onMouseOver, + onMouseOut: onMouseOut, + onFocus: onMouseOver, + onBlur: onMouseOut, + value: customRangeValue, + min: 0, + max: (_CUSTOM_VALUE_SETTING = CUSTOM_VALUE_SETTINGS[selectedUnit]?.max) !== null && _CUSTOM_VALUE_SETTING !== void 0 ? _CUSTOM_VALUE_SETTING : 10, + step: (_CUSTOM_VALUE_SETTING2 = CUSTOM_VALUE_SETTINGS[selectedUnit]?.steps) !== null && _CUSTOM_VALUE_SETTING2 !== void 0 ? _CUSTOM_VALUE_SETTING2 : 0.1, + withInputField: false, + onChange: handleCustomValueSliderChange, + className: "spacing-sizes-control__custom-value-range", + __nextHasNoMarginBottom: true + })), showRangeControl && !showCustomValueControl && (0,external_React_.createElement)(external_wp_components_namespaceObject.RangeControl, { + onMouseOver: onMouseOver, + onMouseOut: onMouseOut, + className: "spacing-sizes-control__range-control", + value: currentValue, + onChange: newSize => onChange(getNewPresetValue(newSize)), + onMouseDown: event => { + // If mouse down is near start of range set initial value to 0, which + // prevents the user have to drag right then left to get 0 setting. + if (event?.nativeEvent?.offsetX < 35) { + setInitialValue(); + } + }, + withInputField: false, + "aria-valuenow": currentValue, + "aria-valuetext": spacingSizes[currentValue]?.name, + renderTooltipContent: customTooltipContent, + min: 0, + max: spacingSizes.length - 1, + marks: marks, + label: ariaLabel, + hideLabelFromVision: true, + __nextHasNoMarginBottom: true, + onFocus: onMouseOver, + onBlur: onMouseOut + }), !showRangeControl && !showCustomValueControl && (0,external_React_.createElement)(external_wp_components_namespaceObject.CustomSelectControl, { + className: "spacing-sizes-control__custom-select-control", + value: options.find(option => option.key === currentValue) || '' // passing undefined here causes a downshift controlled/uncontrolled warning + , + onChange: selection => { + onChange(getNewPresetValue(selection.selectedItem.key, 'selectList')); + }, + options: options, + label: ariaLabel, + hideLabelFromVision: true, + __nextUnconstrainedWidth: true, + size: '__unstable-large', + onMouseOver: onMouseOver, + onMouseOut: onMouseOut, + onFocus: onMouseOver, + onBlur: onMouseOut + }), !disableCustomSpacingSizes && (0,external_React_.createElement)(external_wp_components_namespaceObject.Button, { + label: showCustomValueControl ? (0,external_wp_i18n_namespaceObject.__)('Use size preset') : (0,external_wp_i18n_namespaceObject.__)('Set custom size'), + icon: library_settings, + onClick: () => { + setShowCustomValueControl(!showCustomValueControl); + }, + isPressed: showCustomValueControl, + size: "small", + className: "spacing-sizes-control__custom-toggle", + iconSize: 24 + })); } -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/inserter/media-tab/media-preview.js - -/** - * External dependencies - */ - - -/** - * WordPress dependencies - */ - - - - - - - - +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/spacing-sizes-control/input-controls/axial.js /** * Internal dependencies */ - -const ALLOWED_MEDIA_TYPES = ['image']; -const MAXIMUM_TITLE_LENGTH = 25; -const MEDIA_OPTIONS_POPOVER_PROPS = { - position: 'bottom left', - className: 'block-editor-inserter__media-list__item-preview-options__popover' -}; -function MediaPreviewOptions({ - category, - media -}) { - if (!category.getReportUrl) { - return null; - } - const reportUrl = category.getReportUrl(media); - return (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.DropdownMenu, { - className: "block-editor-inserter__media-list__item-preview-options", - label: (0,external_wp_i18n_namespaceObject.__)('Options'), - popoverProps: MEDIA_OPTIONS_POPOVER_PROPS, - icon: more_vertical - }, () => (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.MenuGroup, null, (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.MenuItem, { - onClick: () => window.open(reportUrl, '_blank').focus(), - icon: library_external - }, (0,external_wp_i18n_namespaceObject.sprintf)( /* translators: %s: The media type to report e.g: "image", "video", "audio" */ - (0,external_wp_i18n_namespaceObject.__)('Report %s'), category.mediaType)))); -} -function InsertExternalImageModal({ - onClose, - onSubmit -}) { - return (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.Modal, { - title: (0,external_wp_i18n_namespaceObject.__)('Insert external image'), - onRequestClose: onClose, - className: "block-editor-inserter-media-tab-media-preview-inserter-external-image-modal" - }, (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.__experimentalVStack, { - spacing: 3 - }, (0,external_wp_element_namespaceObject.createElement)("p", null, (0,external_wp_i18n_namespaceObject.__)('This image cannot be uploaded to your Media Library, but it can still be inserted as an external image.')), (0,external_wp_element_namespaceObject.createElement)("p", null, (0,external_wp_i18n_namespaceObject.__)('External images can be removed by the external provider without warning and could even have legal compliance issues related to privacy legislation.'))), (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.Flex, { - className: "block-editor-block-lock-modal__actions", - justify: "flex-end", - expanded: false - }, (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.FlexItem, null, (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.Button, { - variant: "tertiary", - onClick: onClose - }, (0,external_wp_i18n_namespaceObject.__)('Cancel'))), (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.FlexItem, null, (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.Button, { - variant: "primary", - onClick: onSubmit - }, (0,external_wp_i18n_namespaceObject.__)('Insert'))))); -} -function MediaPreview({ - media, - onClick, - composite, - category +const groupedSides = ['vertical', 'horizontal']; +function AxialInputControls({ + minimumCustomValue, + onChange, + onMouseOut, + onMouseOver, + sides, + spacingSizes, + type, + values }) { - const [showExternalUploadModal, setShowExternalUploadModal] = (0,external_wp_element_namespaceObject.useState)(false); - const [isHovered, setIsHovered] = (0,external_wp_element_namespaceObject.useState)(false); - const [isInserting, setIsInserting] = (0,external_wp_element_namespaceObject.useState)(false); - const [block, preview] = (0,external_wp_element_namespaceObject.useMemo)(() => getBlockAndPreviewFromMedia(media, category.mediaType), [media, category.mediaType]); - const { - createErrorNotice, - createSuccessNotice - } = (0,external_wp_data_namespaceObject.useDispatch)(external_wp_notices_namespaceObject.store); - const mediaUpload = (0,external_wp_data_namespaceObject.useSelect)(select => select(store).getSettings().mediaUpload, []); - const onMediaInsert = (0,external_wp_element_namespaceObject.useCallback)(previewBlock => { - // Prevent multiple uploads when we're in the process of inserting. - if (isInserting) { + const createHandleOnChange = side => next => { + if (!onChange) { return; } - const clonedBlock = (0,external_wp_blocks_namespaceObject.cloneBlock)(previewBlock); - const { - id, - url, - caption - } = clonedBlock.attributes; - // Media item already exists in library, so just insert it. - if (!!id) { - onClick(clonedBlock); - return; + + // Encode the existing value into the preset value if the passed in value matches the value of one of the spacingSizes. + const nextValues = { + ...Object.keys(values).reduce((acc, key) => { + acc[key] = getPresetValueFromCustomValue(values[key], spacingSizes); + return acc; + }, {}) + }; + if (side === 'vertical') { + nextValues.top = next; + nextValues.bottom = next; } - setIsInserting(true); - // Media item does not exist in library, so try to upload it. - // Fist fetch the image data. This may fail if the image host - // doesn't allow CORS with the domain. - // If this happens, we insert the image block using the external - // URL and let the user know about the possible implications. - window.fetch(url).then(response => response.blob()).then(blob => { - mediaUpload({ - filesList: [blob], - additionalData: { - caption - }, - onFileChange([img]) { - if ((0,external_wp_blob_namespaceObject.isBlobURL)(img.url)) { - return; - } - onClick({ - ...clonedBlock, - attributes: { - ...clonedBlock.attributes, - id: img.id, - url: img.url - } - }); - createSuccessNotice((0,external_wp_i18n_namespaceObject.__)('Image uploaded and inserted.'), { - type: 'snackbar' - }); - setIsInserting(false); - }, - allowedTypes: ALLOWED_MEDIA_TYPES, - onError(message) { - createErrorNotice(message, { - type: 'snackbar' - }); - setIsInserting(false); - } - }); - }).catch(() => { - setShowExternalUploadModal(true); - setIsInserting(false); - }); - }, [isInserting, onClick, mediaUpload, createErrorNotice, createSuccessNotice]); - const title = media.title?.rendered || media.title; - let truncatedTitle; - if (title.length > MAXIMUM_TITLE_LENGTH) { - const omission = '...'; - truncatedTitle = title.slice(0, MAXIMUM_TITLE_LENGTH - omission.length) + omission; - } - const onMouseEnter = (0,external_wp_element_namespaceObject.useCallback)(() => setIsHovered(true), []); - const onMouseLeave = (0,external_wp_element_namespaceObject.useCallback)(() => setIsHovered(false), []); - return (0,external_wp_element_namespaceObject.createElement)(external_wp_element_namespaceObject.Fragment, null, (0,external_wp_element_namespaceObject.createElement)(inserter_draggable_blocks, { - isEnabled: true, - blocks: [block] - }, ({ - draggable, - onDragStart, - onDragEnd - }) => (0,external_wp_element_namespaceObject.createElement)("div", { - className: classnames_default()('block-editor-inserter__media-list__list-item', { - 'is-hovered': isHovered - }), - draggable: draggable, - onDragStart: onDragStart, - onDragEnd: onDragEnd - }, (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.Tooltip, { - text: truncatedTitle || title - }, (0,external_wp_element_namespaceObject.createElement)("div", { - onMouseEnter: onMouseEnter, - onMouseLeave: onMouseLeave - }, (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.__unstableCompositeItem, { - role: "option", - as: "div", - ...composite, - className: "block-editor-inserter__media-list__item", - onClick: () => onMediaInsert(block), - "aria-label": title - }, (0,external_wp_element_namespaceObject.createElement)("div", { - className: "block-editor-inserter__media-list__item-preview" - }, preview, isInserting && (0,external_wp_element_namespaceObject.createElement)("div", { - className: "block-editor-inserter__media-list__item-preview-spinner" - }, (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.Spinner, null)))), !isInserting && (0,external_wp_element_namespaceObject.createElement)(MediaPreviewOptions, { - category: category, - media: media - }))))), showExternalUploadModal && (0,external_wp_element_namespaceObject.createElement)(InsertExternalImageModal, { - onClose: () => setShowExternalUploadModal(false), - onSubmit: () => { - onClick((0,external_wp_blocks_namespaceObject.cloneBlock)(block)); - createSuccessNotice((0,external_wp_i18n_namespaceObject.__)('Image inserted.'), { - type: 'snackbar' - }); - setShowExternalUploadModal(false); + if (side === 'horizontal') { + nextValues.left = next; + nextValues.right = next; } + onChange(nextValues); + }; + + // Filter sides if custom configuration provided, maintaining default order. + const filteredSides = sides?.length ? groupedSides.filter(side => hasAxisSupport(sides, side)) : groupedSides; + return (0,external_React_.createElement)(external_React_.Fragment, null, filteredSides.map(side => { + const axisValue = side === 'vertical' ? values.top : values.left; + return (0,external_React_.createElement)(SpacingInputControl, { + key: `spacing-sizes-control-${side}`, + icon: ICONS[side], + label: LABELS[side], + minimumCustomValue: minimumCustomValue, + onChange: createHandleOnChange(side), + onMouseOut: onMouseOut, + onMouseOver: onMouseOver, + side: side, + spacingSizes: spacingSizes, + type: type, + value: axisValue, + withInputField: false + }); })); } -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/inserter/media-tab/media-list.js - -/** - * WordPress dependencies - */ - - +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/spacing-sizes-control/input-controls/separated.js /** * Internal dependencies */ -function MediaList({ - mediaList, - category, - onClick, - label = (0,external_wp_i18n_namespaceObject.__)('Media List') -}) { - const composite = (0,external_wp_components_namespaceObject.__unstableUseCompositeState)(); - return (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.__unstableComposite, { - ...composite, - role: "listbox", - className: "block-editor-inserter__media-list", - "aria-label": label - }, mediaList.map((media, index) => (0,external_wp_element_namespaceObject.createElement)(MediaPreview, { - key: media.id || media.sourceId || index, - media: media, - category: category, - onClick: onClick, - composite: composite - }))); -} -/* harmony default export */ var media_list = (MediaList); - -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/inserter/hooks/use-debounced-input.js -/** - * WordPress dependencies - */ - -function useDebouncedInput(defaultValue = '') { - const [input, setInput] = (0,external_wp_element_namespaceObject.useState)(defaultValue); - const [debouncedInput, setDebouncedState] = (0,external_wp_element_namespaceObject.useState)(defaultValue); - const setDebouncedInput = (0,external_wp_compose_namespaceObject.useDebounce)(setDebouncedState, 250); - (0,external_wp_element_namespaceObject.useEffect)(() => { - setDebouncedInput(input); - }, [input]); - return [input, setInput, debouncedInput]; +function SeparatedInputControls({ + minimumCustomValue, + onChange, + onMouseOut, + onMouseOver, + sides, + spacingSizes, + type, + values +}) { + // Filter sides if custom configuration provided, maintaining default order. + const filteredSides = sides?.length ? ALL_SIDES.filter(side => sides.includes(side)) : ALL_SIDES; + const createHandleOnChange = side => next => { + // Encode the existing value into the preset value if the passed in value matches the value of one of the spacingSizes. + const nextValues = { + ...Object.keys(values).reduce((acc, key) => { + acc[key] = getPresetValueFromCustomValue(values[key], spacingSizes); + return acc; + }, {}) + }; + nextValues[side] = next; + onChange(nextValues); + }; + return (0,external_React_.createElement)(external_React_.Fragment, null, filteredSides.map(side => { + return (0,external_React_.createElement)(SpacingInputControl, { + key: `spacing-sizes-control-${side}`, + icon: ICONS[side], + label: LABELS[side], + minimumCustomValue: minimumCustomValue, + onChange: createHandleOnChange(side), + onMouseOut: onMouseOut, + onMouseOver: onMouseOver, + side: side, + spacingSizes: spacingSizes, + type: type, + value: values[side], + withInputField: false + }); + })); } -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/inserter/no-results.js +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/spacing-sizes-control/input-controls/single.js /** - * WordPress dependencies + * Internal dependencies */ -function InserterNoResults() { - return (0,external_wp_element_namespaceObject.createElement)("div", { - className: "block-editor-inserter__no-results" - }, (0,external_wp_element_namespaceObject.createElement)(build_module_icon, { - className: "block-editor-inserter__no-results-icon", - icon: block_default - }), (0,external_wp_element_namespaceObject.createElement)("p", null, (0,external_wp_i18n_namespaceObject.__)('No results found.'))); +function SingleInputControl({ + minimumCustomValue, + onChange, + onMouseOut, + onMouseOver, + showSideInLabel, + side, + spacingSizes, + type, + values +}) { + const createHandleOnChange = currentSide => next => { + // Encode the existing value into the preset value if the passed in value matches the value of one of the spacingSizes. + const nextValues = { + ...Object.keys(values).reduce((acc, key) => { + acc[key] = getPresetValueFromCustomValue(values[key], spacingSizes); + return acc; + }, {}) + }; + nextValues[currentSide] = next; + onChange(nextValues); + }; + return (0,external_React_.createElement)(SpacingInputControl, { + label: LABELS[side], + minimumCustomValue: minimumCustomValue, + onChange: createHandleOnChange(side), + onMouseOut: onMouseOut, + onMouseOver: onMouseOver, + showSideInLabel: showSideInLabel, + side: side, + spacingSizes: spacingSizes, + type: type, + value: values[side], + withInputField: false + }); } -/* harmony default export */ var no_results = (InserterNoResults); -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/inserter/media-tab/media-panel.js +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/spacing-sizes-control/sides-dropdown/index.js /** * WordPress dependencies @@ -24914,72 +29419,71 @@ function InserterNoResults() { - - /** * Internal dependencies */ - - - -const INITIAL_MEDIA_ITEMS_PER_PAGE = 10; -function MediaCategoryDialog({ - rootClientId, - onInsert, - category -}) { - const container = (0,external_wp_element_namespaceObject.useRef)(); - (0,external_wp_element_namespaceObject.useEffect)(() => { - const timeout = setTimeout(() => { - const [firstTabbable] = external_wp_dom_namespaceObject.focus.tabbable.find(container.current); - firstTabbable?.focus(); - }); - return () => clearTimeout(timeout); - }, [category]); - return (0,external_wp_element_namespaceObject.createElement)("div", { - ref: container, - className: "block-editor-inserter__media-dialog" - }, (0,external_wp_element_namespaceObject.createElement)(MediaCategoryPanel, { - rootClientId: rootClientId, - onInsert: onInsert, - category: category - })); -} -function MediaCategoryPanel({ - rootClientId, - onInsert, - category +const checkIcon = (0,external_React_.createElement)(external_wp_components_namespaceObject.Icon, { + icon: library_check, + size: 24 +}); +function SidesDropdown({ + label: labelProp, + onChange, + sides, + value }) { - const [search, setSearch, debouncedSearch] = useDebouncedInput(); + if (!sides || !sides.length) { + return; + } + const supportedItems = getSupportedMenuItems(sides); + const sideIcon = supportedItems[value].icon; const { - mediaList, - isLoading - } = useMediaResults(category, { - per_page: !!debouncedSearch ? 20 : INITIAL_MEDIA_ITEMS_PER_PAGE, - search: debouncedSearch - }); - const baseCssClass = 'block-editor-inserter__media-panel'; - const searchLabel = category.labels.search_items || (0,external_wp_i18n_namespaceObject.__)('Search'); - return (0,external_wp_element_namespaceObject.createElement)("div", { - className: baseCssClass - }, (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.SearchControl, { - className: `${baseCssClass}-search`, - onChange: setSearch, - value: search, - label: searchLabel, - placeholder: searchLabel - }), isLoading && (0,external_wp_element_namespaceObject.createElement)("div", { - className: `${baseCssClass}-spinner` - }, (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.Spinner, null)), !isLoading && !mediaList?.length && (0,external_wp_element_namespaceObject.createElement)(no_results, null), !isLoading && !!mediaList?.length && (0,external_wp_element_namespaceObject.createElement)(media_list, { - rootClientId: rootClientId, - onClick: onInsert, - mediaList: mediaList, - category: category - })); + custom: customItem, + ...menuItems + } = supportedItems; + return (0,external_React_.createElement)(external_wp_components_namespaceObject.DropdownMenu, { + icon: sideIcon, + label: labelProp, + className: "spacing-sizes-control__dropdown", + toggleProps: { + isSmall: true + } + }, ({ + onClose + }) => { + return (0,external_React_.createElement)(external_React_.Fragment, null, (0,external_React_.createElement)(external_wp_components_namespaceObject.MenuGroup, null, Object.entries(menuItems).map(([slug, { + label, + icon + }]) => { + const isSelected = value === slug; + return (0,external_React_.createElement)(external_wp_components_namespaceObject.MenuItem, { + key: slug, + icon: icon, + iconPosition: "left", + isSelected: isSelected, + role: "menuitemradio", + onClick: () => { + onChange(slug); + onClose(); + }, + suffix: isSelected ? checkIcon : undefined + }, label); + })), !!customItem && (0,external_React_.createElement)(external_wp_components_namespaceObject.MenuGroup, null, (0,external_React_.createElement)(external_wp_components_namespaceObject.MenuItem, { + icon: customItem.icon, + iconPosition: "left", + isSelected: value === VIEWS.custom, + role: "menuitemradio", + onClick: () => { + onChange(VIEWS.custom); + onClose(); + }, + suffix: value === VIEWS.custom ? checkIcon : undefined + }, customItem.label))); + }); } -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/media-upload/check.js +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/spacing-sizes-control/hooks/use-spacing-sizes.js /** * WordPress dependencies */ @@ -24989,51 +29493,123 @@ function MediaCategoryPanel({ * Internal dependencies */ -function MediaUploadCheck({ - fallback = null, - children -}) { - const hasUploadPermissions = (0,external_wp_data_namespaceObject.useSelect)(select => { - const { - getSettings - } = select(store); - return !!getSettings().mediaUpload; - }, []); - return hasUploadPermissions ? children : fallback; +function useSpacingSizes() { + const spacingSizes = [{ + name: 0, + slug: '0', + size: 0 + }]; + const [settingsSizes] = use_settings_useSettings('spacing.spacingSizes'); + if (settingsSizes) { + spacingSizes.push(...settingsSizes); + } + if (spacingSizes.length > 8) { + spacingSizes.unshift({ + name: (0,external_wp_i18n_namespaceObject.__)('Default'), + slug: 'default', + size: undefined + }); + } + return spacingSizes; } -/** - * @see https://github.com/WordPress/gutenberg/blob/HEAD/packages/block-editor/src/components/media-upload/README.md - */ -/* harmony default export */ var check = (MediaUploadCheck); +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/spacing-sizes-control/index.js -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/media-upload/index.js /** * WordPress dependencies */ -/** - * This is a placeholder for the media upload component necessary to make it possible to provide - * an integration with the core blocks that handle media files. By default it renders nothing but - * it provides a way to have it overridden with the `editor.MediaUpload` filter. - * - * @return {WPComponent} The component to be rendered. - */ -const MediaUpload = () => null; + /** - * @see https://github.com/WordPress/gutenberg/blob/HEAD/packages/block-editor/src/components/media-upload/README.md + * Internal dependencies */ -/* harmony default export */ var media_upload = ((0,external_wp_components_namespaceObject.withFilters)('editor.MediaUpload')(MediaUpload)); -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/inserter/media-tab/media-tab.js -/** - * External dependencies - */ + + +function SpacingSizesControl({ + inputProps, + label: labelProp, + minimumCustomValue = 0, + onChange, + onMouseOut, + onMouseOver, + showSideInLabel = true, + sides = ALL_SIDES, + useSelect, + values +}) { + const spacingSizes = useSpacingSizes(); + const inputValues = values || DEFAULT_VALUES; + const hasOneSide = sides?.length === 1; + const hasOnlyAxialSides = sides?.includes('horizontal') && sides?.includes('vertical') && sides?.length === 2; + const [view, setView] = (0,external_wp_element_namespaceObject.useState)(getInitialView(inputValues, sides)); + const handleOnChange = nextValue => { + const newValues = { + ...values, + ...nextValue + }; + onChange(newValues); + }; + const inputControlProps = { + ...inputProps, + minimumCustomValue, + onChange: handleOnChange, + onMouseOut, + onMouseOver, + sides, + spacingSizes, + type: labelProp, + useSelect, + values: inputValues + }; + const renderControls = () => { + if (view === VIEWS.axial) { + return (0,external_React_.createElement)(AxialInputControls, { + ...inputControlProps + }); + } + if (view === VIEWS.custom) { + return (0,external_React_.createElement)(SeparatedInputControls, { + ...inputControlProps + }); + } + return (0,external_React_.createElement)(SingleInputControl, { + side: view, + ...inputControlProps, + showSideInLabel: showSideInLabel + }); + }; + const sideLabel = ALL_SIDES.includes(view) && showSideInLabel ? LABELS[view] : ''; + const label = (0,external_wp_i18n_namespaceObject.sprintf)( + // translators: 2. Type of spacing being modified (Padding, margin, etc). 1: The side of the block being modified (top, bottom, left etc.). + (0,external_wp_i18n_namespaceObject.__)('%1$s %2$s'), labelProp, sideLabel).trim(); + const dropdownLabelText = (0,external_wp_i18n_namespaceObject.sprintf)( + // translators: %s: The current spacing property e.g. "Padding", "Margin". + (0,external_wp_i18n_namespaceObject._x)('%s options', 'Button label to reveal side configuration options'), labelProp); + return (0,external_React_.createElement)("fieldset", { + className: "spacing-sizes-control" + }, (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalHStack, { + className: "spacing-sizes-control__header" + }, (0,external_React_.createElement)(external_wp_components_namespaceObject.BaseControl.VisualLabel, { + as: "legend", + className: "spacing-sizes-control__label" + }, label), !hasOneSide && !hasOnlyAxialSides && (0,external_React_.createElement)(SidesDropdown, { + label: dropdownLabelText, + onChange: setView, + sides: sides, + value: view + })), (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalVStack, { + spacing: 0.5 + }, renderControls())); +} + +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/height-control/index.js + /** * WordPress dependencies */ @@ -25041,138 +29617,394 @@ const MediaUpload = () => null; +/** + * Internal dependencies + */ +const RANGE_CONTROL_CUSTOM_SETTINGS = { + px: { + max: 1000, + step: 1 + }, + '%': { + max: 100, + step: 1 + }, + vw: { + max: 100, + step: 1 + }, + vh: { + max: 100, + step: 1 + }, + em: { + max: 50, + step: 0.1 + }, + rem: { + max: 50, + step: 0.1 + }, + svw: { + max: 100, + step: 1 + }, + lvw: { + max: 100, + step: 1 + }, + dvw: { + max: 100, + step: 1 + }, + svh: { + max: 100, + step: 1 + }, + lvh: { + max: 100, + step: 1 + }, + dvh: { + max: 100, + step: 1 + }, + vi: { + max: 100, + step: 1 + }, + svi: { + max: 100, + step: 1 + }, + lvi: { + max: 100, + step: 1 + }, + dvi: { + max: 100, + step: 1 + }, + vb: { + max: 100, + step: 1 + }, + svb: { + max: 100, + step: 1 + }, + lvb: { + max: 100, + step: 1 + }, + dvb: { + max: 100, + step: 1 + }, + vmin: { + max: 100, + step: 1 + }, + svmin: { + max: 100, + step: 1 + }, + lvmin: { + max: 100, + step: 1 + }, + dvmin: { + max: 100, + step: 1 + }, + vmax: { + max: 100, + step: 1 + }, + svmax: { + max: 100, + step: 1 + }, + lvmax: { + max: 100, + step: 1 + }, + dvmax: { + max: 100, + step: 1 + } +}; /** - * Internal dependencies + * HeightControl renders a linked unit control and range control for adjusting the height of a block. + * + * @see https://github.com/WordPress/gutenberg/blob/HEAD/packages/block-editor/src/components/height-control/README.md + * + * @param {Object} props + * @param {?string} props.label A label for the control. + * @param {( value: string ) => void } props.onChange Called when the height changes. + * @param {string} props.value The current height value. + * + * @return {Component} The component to be rendered. */ +function HeightControl({ + label = (0,external_wp_i18n_namespaceObject.__)('Height'), + onChange, + value +}) { + var _RANGE_CONTROL_CUSTOM, _RANGE_CONTROL_CUSTOM2; + const customRangeValue = parseFloat(value); + const [availableUnits] = use_settings_useSettings('spacing.units'); + const units = (0,external_wp_components_namespaceObject.__experimentalUseCustomUnits)({ + availableUnits: availableUnits || ['%', 'px', 'em', 'rem', 'vh', 'vw'] + }); + const selectedUnit = (0,external_wp_element_namespaceObject.useMemo)(() => (0,external_wp_components_namespaceObject.__experimentalParseQuantityAndUnitFromRawValue)(value), [value])[1] || units[0]?.value || 'px'; + const handleSliderChange = next => { + onChange([next, selectedUnit].join('')); + }; + const handleUnitChange = newUnit => { + // Attempt to smooth over differences between currentUnit and newUnit. + // This should slightly improve the experience of switching between unit types. + const [currentValue, currentUnit] = (0,external_wp_components_namespaceObject.__experimentalParseQuantityAndUnitFromRawValue)(value); + if (['em', 'rem'].includes(newUnit) && currentUnit === 'px') { + // Convert pixel value to an approximate of the new unit, assuming a root size of 16px. + onChange((currentValue / 16).toFixed(2) + newUnit); + } else if (['em', 'rem'].includes(currentUnit) && newUnit === 'px') { + // Convert to pixel value assuming a root size of 16px. + onChange(Math.round(currentValue * 16) + newUnit); + } else if (['%', 'vw', 'svw', 'lvw', 'dvw', 'vh', 'svh', 'lvh', 'dvh', 'vi', 'svi', 'lvi', 'dvi', 'vb', 'svb', 'lvb', 'dvb', 'vmin', 'svmin', 'lvmin', 'dvmin', 'vmax', 'svmax', 'lvmax', 'dvmax'].includes(newUnit) && currentValue > 100) { + // When converting to `%` or viewport-relative units, cap the new value at 100. + onChange(100 + newUnit); + } + }; + return (0,external_React_.createElement)("fieldset", { + className: "block-editor-height-control" + }, (0,external_React_.createElement)(external_wp_components_namespaceObject.BaseControl.VisualLabel, { + as: "legend" + }, label), (0,external_React_.createElement)(external_wp_components_namespaceObject.Flex, null, (0,external_React_.createElement)(external_wp_components_namespaceObject.FlexItem, { + isBlock: true + }, (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalUnitControl, { + value: value, + units: units, + onChange: onChange, + onUnitChange: handleUnitChange, + min: 0, + size: '__unstable-large', + label: label, + hideLabelFromVision: true + })), (0,external_React_.createElement)(external_wp_components_namespaceObject.FlexItem, { + isBlock: true + }, (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalSpacer, { + marginX: 2, + marginBottom: 0 + }, (0,external_React_.createElement)(external_wp_components_namespaceObject.RangeControl, { + value: customRangeValue, + min: 0, + max: (_RANGE_CONTROL_CUSTOM = RANGE_CONTROL_CUSTOM_SETTINGS[selectedUnit]?.max) !== null && _RANGE_CONTROL_CUSTOM !== void 0 ? _RANGE_CONTROL_CUSTOM : 100, + step: (_RANGE_CONTROL_CUSTOM2 = RANGE_CONTROL_CUSTOM_SETTINGS[selectedUnit]?.step) !== null && _RANGE_CONTROL_CUSTOM2 !== void 0 ? _RANGE_CONTROL_CUSTOM2 : 0.1, + withInputField: false, + onChange: handleSliderChange, + __nextHasNoMarginBottom: true, + label: label, + hideLabelFromVision: true + }))))); +} +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/child-layout-control/index.js +/** + * WordPress dependencies + */ +function helpText(selfStretch, parentLayout) { + const { + orientation = 'horizontal' + } = parentLayout; + if (selfStretch === 'fill') { + return (0,external_wp_i18n_namespaceObject.__)('Stretch to fill available space.'); + } + if (selfStretch === 'fixed' && orientation === 'horizontal') { + return (0,external_wp_i18n_namespaceObject.__)('Specify a fixed width.'); + } else if (selfStretch === 'fixed') { + return (0,external_wp_i18n_namespaceObject.__)('Specify a fixed height.'); + } + return (0,external_wp_i18n_namespaceObject.__)('Fit contents.'); +} -const media_tab_ALLOWED_MEDIA_TYPES = ['image', 'video', 'audio']; -function MediaTab({ - rootClientId, - selectedCategory, - onSelectCategory, - onInsert +/** + * Form to edit the child layout value. + * + * @param {Object} props Props. + * @param {Object} props.value The child layout value. + * @param {Function} props.onChange Function to update the child layout value. + * @param {Object} props.parentLayout The parent layout value. + * + * @return {Element} child layout edit element. + */ +function ChildLayoutControl({ + value: childLayout = {}, + onChange, + parentLayout }) { - const mediaCategories = useMediaCategories(rootClientId); - const isMobile = (0,external_wp_compose_namespaceObject.useViewportMatch)('medium', '<'); - const baseCssClass = 'block-editor-inserter__media-tabs'; - const onSelectMedia = (0,external_wp_element_namespaceObject.useCallback)(media => { - if (!media?.url) { - return; + const { + selfStretch, + flexSize + } = childLayout; + (0,external_wp_element_namespaceObject.useEffect)(() => { + if (selfStretch === 'fixed' && !flexSize) { + onChange({ + ...childLayout, + selfStretch: 'fit' + }); } - const [block] = getBlockAndPreviewFromMedia(media, media.type); - onInsert(block); - }, [onInsert]); - const mobileMediaCategories = (0,external_wp_element_namespaceObject.useMemo)(() => mediaCategories.map(mediaCategory => ({ - ...mediaCategory, - label: mediaCategory.labels.name - })), [mediaCategories]); - return (0,external_wp_element_namespaceObject.createElement)(external_wp_element_namespaceObject.Fragment, null, !isMobile && (0,external_wp_element_namespaceObject.createElement)("div", { - className: `${baseCssClass}-container` - }, (0,external_wp_element_namespaceObject.createElement)("nav", { - "aria-label": (0,external_wp_i18n_namespaceObject.__)('Media categories') - }, (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.__experimentalItemGroup, { - role: "list", - className: baseCssClass - }, mediaCategories.map(mediaCategory => (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.__experimentalItem, { - role: "listitem", - key: mediaCategory.name, - onClick: () => onSelectCategory(mediaCategory), - className: classnames_default()(`${baseCssClass}__media-category`, { - 'is-selected': selectedCategory === mediaCategory - }), - "aria-label": mediaCategory.labels.name, - "aria-current": mediaCategory === selectedCategory ? 'true' : undefined - }, (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.__experimentalHStack, null, (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.FlexBlock, null, mediaCategory.labels.name), (0,external_wp_element_namespaceObject.createElement)(build_module_icon, { - icon: (0,external_wp_i18n_namespaceObject.isRTL)() ? chevron_left : chevron_right - })))), (0,external_wp_element_namespaceObject.createElement)("div", { - role: "listitem" - }, (0,external_wp_element_namespaceObject.createElement)(check, null, (0,external_wp_element_namespaceObject.createElement)(media_upload, { - multiple: false, - onSelect: onSelectMedia, - allowedTypes: media_tab_ALLOWED_MEDIA_TYPES, - render: ({ - open - }) => (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.Button, { - onClick: event => { - // Safari doesn't emit a focus event on button elements when - // clicked and we need to manually focus the button here. - // The reason is that core's Media Library modal explicitly triggers a - // focus event and therefore a `blur` event is triggered on a different - // element, which doesn't contain the `data-unstable-ignore-focus-outside-for-relatedtarget` - // attribute making the Inserter dialog to close. - event.target.focus(); - open(); - }, - className: "block-editor-inserter__media-library-button", - variant: "secondary", - "data-unstable-ignore-focus-outside-for-relatedtarget": ".media-modal" - }, (0,external_wp_i18n_namespaceObject.__)('Open Media Library')) - })))))), isMobile && (0,external_wp_element_namespaceObject.createElement)(MobileTabNavigation, { - categories: mobileMediaCategories - }, category => (0,external_wp_element_namespaceObject.createElement)(MediaCategoryPanel, { - onInsert: onInsert, - rootClientId: rootClientId, - category: category - }))); + }, []); + return (0,external_React_.createElement)(external_React_.Fragment, null, (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalToggleGroupControl, { + __nextHasNoMarginBottom: true, + size: '__unstable-large', + label: childLayoutOrientation(parentLayout), + value: selfStretch || 'fit', + help: helpText(selfStretch, parentLayout), + onChange: value => { + const newFlexSize = value !== 'fixed' ? null : flexSize; + onChange({ + ...childLayout, + selfStretch: value, + flexSize: newFlexSize + }); + }, + isBlock: true + }, (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalToggleGroupControlOption, { + key: 'fit', + value: 'fit', + label: (0,external_wp_i18n_namespaceObject.__)('Fit') + }), (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalToggleGroupControlOption, { + key: 'fill', + value: 'fill', + label: (0,external_wp_i18n_namespaceObject.__)('Fill') + }), (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalToggleGroupControlOption, { + key: 'fixed', + value: 'fixed', + label: (0,external_wp_i18n_namespaceObject.__)('Fixed') + })), selfStretch === 'fixed' && (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalUnitControl, { + size: '__unstable-large', + onChange: value => { + onChange({ + ...childLayout, + flexSize: value + }); + }, + value: flexSize + })); +} +function childLayoutOrientation(parentLayout) { + const { + orientation = 'horizontal' + } = parentLayout; + return orientation === 'horizontal' ? (0,external_wp_i18n_namespaceObject.__)('Width') : (0,external_wp_i18n_namespaceObject.__)('Height'); } -/* harmony default export */ var media_tab = (MediaTab); -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/inserter-menu-extension/index.js +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/dimensions-tool/aspect-ratio-tool.js + /** * WordPress dependencies */ -const { - Fill: __unstableInserterMenuExtension, - Slot -} = (0,external_wp_components_namespaceObject.createSlotFill)('__unstableInserterMenuExtension'); -__unstableInserterMenuExtension.Slot = Slot; -/* harmony default export */ var inserter_menu_extension = (__unstableInserterMenuExtension); -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/utils/order-inserter-block-items.js -/** @typedef {import('../store/selectors').WPEditorInserterItem} WPEditorInserterItem */ /** - * Helper function to order inserter block items according to a provided array of prioritized blocks. - * - * @param {WPEditorInserterItem[]} items The array of editor inserter block items to be sorted. - * @param {string[]} priority The array of block names to be prioritized. - * @return {WPEditorInserterItem[]} The sorted array of editor inserter block items. + * @typedef {import('@wordpress/components/build-types/select-control/types').SelectControlProps} SelectControlProps */ -const orderInserterBlockItems = (items, priority) => { - if (!priority) { - return items; - } - items.sort(({ - id: aName - }, { - id: bName - }) => { - // Sort block items according to `priority`. - let aIndex = priority.indexOf(aName); - let bIndex = priority.indexOf(bName); - // All other block items should come after that. - if (aIndex < 0) aIndex = priority.length; - if (bIndex < 0) bIndex = priority.length; - return aIndex - bIndex; - }); - return items; -}; -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/inserter/search-results.js +/** + * @type {SelectControlProps[]} + */ +const DEFAULT_ASPECT_RATIO_OPTIONS = [{ + label: (0,external_wp_i18n_namespaceObject._x)('Original', 'Aspect ratio option for dimensions control'), + value: 'auto' +}, { + label: (0,external_wp_i18n_namespaceObject._x)('Square - 1:1', 'Aspect ratio option for dimensions control'), + value: '1' +}, { + label: (0,external_wp_i18n_namespaceObject._x)('Standard - 4:3', 'Aspect ratio option for dimensions control'), + value: '4/3' +}, { + label: (0,external_wp_i18n_namespaceObject._x)('Portrait - 3:4', 'Aspect ratio option for dimensions control'), + value: '3/4' +}, { + label: (0,external_wp_i18n_namespaceObject._x)('Classic - 3:2', 'Aspect ratio option for dimensions control'), + value: '3/2' +}, { + label: (0,external_wp_i18n_namespaceObject._x)('Classic Portrait - 2:3', 'Aspect ratio option for dimensions control'), + value: '2/3' +}, { + label: (0,external_wp_i18n_namespaceObject._x)('Wide - 16:9', 'Aspect ratio option for dimensions control'), + value: '16/9' +}, { + label: (0,external_wp_i18n_namespaceObject._x)('Tall - 9:16', 'Aspect ratio option for dimensions control'), + value: '9/16' +}, { + label: (0,external_wp_i18n_namespaceObject._x)('Custom', 'Aspect ratio option for dimensions control'), + value: 'custom', + disabled: true, + hidden: true +}]; + +/** + * @callback AspectRatioToolPropsOnChange + * @param {string} [value] New aspect ratio value. + * @return {void} No return. + */ + +/** + * @typedef {Object} AspectRatioToolProps + * @property {string} [panelId] ID of the panel this tool is associated with. + * @property {string} [value] Current aspect ratio value. + * @property {AspectRatioToolPropsOnChange} [onChange] Callback to update the aspect ratio value. + * @property {SelectControlProps[]} [options] Aspect ratio options. + * @property {string} [defaultValue] Default aspect ratio value. + * @property {boolean} [isShownByDefault] Whether the tool is shown by default. + */ + +function AspectRatioTool({ + panelId, + value, + onChange = () => {}, + options = DEFAULT_ASPECT_RATIO_OPTIONS, + defaultValue = DEFAULT_ASPECT_RATIO_OPTIONS[0].value, + hasValue, + isShownByDefault = true +}) { + // Match the CSS default so if the value is used directly in CSS it will look correct in the control. + const displayValue = value !== null && value !== void 0 ? value : 'auto'; + return (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalToolsPanelItem, { + hasValue: hasValue ? hasValue : () => displayValue !== defaultValue, + label: (0,external_wp_i18n_namespaceObject.__)('Aspect ratio'), + onDeselect: () => onChange(undefined), + isShownByDefault: isShownByDefault, + panelId: panelId + }, (0,external_React_.createElement)(external_wp_components_namespaceObject.SelectControl, { + label: (0,external_wp_i18n_namespaceObject.__)('Aspect ratio'), + value: displayValue, + options: options, + onChange: onChange, + size: '__unstable-large', + __nextHasNoMarginBottom: true + })); +} + +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/global-styles/dimensions-panel.js /** - * WordPress dependencies + * External dependencies */ +/** + * WordPress dependencies + */ @@ -25188,191 +30020,534 @@ const orderInserterBlockItems = (items, priority) => { +const AXIAL_SIDES = ['horizontal', 'vertical']; +function useHasDimensionsPanel(settings) { + const hasContentSize = useHasContentSize(settings); + const hasWideSize = useHasWideSize(settings); + const hasPadding = useHasPadding(settings); + const hasMargin = useHasMargin(settings); + const hasGap = useHasGap(settings); + const hasMinHeight = useHasMinHeight(settings); + const hasAspectRatio = useHasAspectRatio(settings); + const hasChildLayout = useHasChildLayout(settings); + return external_wp_element_namespaceObject.Platform.OS === 'web' && (hasContentSize || hasWideSize || hasPadding || hasMargin || hasGap || hasMinHeight || hasAspectRatio || hasChildLayout); +} +function useHasContentSize(settings) { + return settings?.layout?.contentSize; +} +function useHasWideSize(settings) { + return settings?.layout?.wideSize; +} +function useHasPadding(settings) { + return settings?.spacing?.padding; +} +function useHasMargin(settings) { + return settings?.spacing?.margin; +} +function useHasGap(settings) { + return settings?.spacing?.blockGap; +} +function useHasMinHeight(settings) { + return settings?.dimensions?.minHeight; +} +function useHasAspectRatio(settings) { + return settings?.dimensions?.aspectRatio; +} +function useHasChildLayout(settings) { + var _settings$parentLayou; + const { + type: parentLayoutType = 'default', + default: { + type: defaultParentLayoutType = 'default' + } = {}, + allowSizingOnChildren = false + } = (_settings$parentLayou = settings?.parentLayout) !== null && _settings$parentLayou !== void 0 ? _settings$parentLayou : {}; + const support = (defaultParentLayoutType === 'flex' || parentLayoutType === 'flex') && allowSizingOnChildren; + return !!settings?.layout && support; +} +function useHasSpacingPresets(settings) { + var _ref, _ref2; + const { + custom, + theme, + default: defaultPresets + } = settings?.spacing?.spacingSizes || {}; + const presets = (_ref = (_ref2 = custom !== null && custom !== void 0 ? custom : theme) !== null && _ref2 !== void 0 ? _ref2 : defaultPresets) !== null && _ref !== void 0 ? _ref : []; + return presets.length > 0; +} +function filterValuesBySides(values, sides) { + // If no custom side configuration, all sides are opted into by default. + // Without any values, we have nothing to filter either. + if (!sides || !values) { + return values; + } + // Only include sides opted into within filtered values. + const filteredValues = {}; + sides.forEach(side => { + if (side === 'vertical') { + filteredValues.top = values.top; + filteredValues.bottom = values.bottom; + } + if (side === 'horizontal') { + filteredValues.left = values.left; + filteredValues.right = values.right; + } + filteredValues[side] = values?.[side]; + }); + return filteredValues; +} +function splitStyleValue(value) { + // Check for shorthand value (a string value). + if (value && typeof value === 'string') { + // Convert to value for individual sides for BoxControl. + return { + top: value, + right: value, + bottom: value, + left: value + }; + } + return value; +} +function splitGapValue(value) { + // Check for shorthand value (a string value). + if (value && typeof value === 'string') { + // If the value is a string, treat it as a single side (top) for the spacing controls. + return { + top: value + }; + } + if (value) { + return { + ...value, + right: value?.left, + bottom: value?.top + }; + } + return value; +} +function DimensionsToolsPanel({ + resetAllFilter, + onChange, + value, + panelId, + children +}) { + const resetAll = () => { + const updatedValue = resetAllFilter(value); + onChange(updatedValue); + }; + return (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalToolsPanel, { + label: (0,external_wp_i18n_namespaceObject.__)('Dimensions'), + resetAll: resetAll, + panelId: panelId, + dropdownMenuProps: TOOLSPANEL_DROPDOWNMENU_PROPS + }, children); +} +const dimensions_panel_DEFAULT_CONTROLS = { + contentSize: true, + wideSize: true, + padding: true, + margin: true, + blockGap: true, + minHeight: true, + aspectRatio: true, + childLayout: true +}; +function DimensionsPanel({ + as: Wrapper = DimensionsToolsPanel, + value, + onChange, + inheritedValue = value, + settings, + panelId, + defaultControls = dimensions_panel_DEFAULT_CONTROLS, + onVisualize = () => {}, + // Special case because the layout controls are not part of the dimensions panel + // in global styles but not in block inspector. + includeLayoutControls = false +}) { + var _settings$parentLayou2, _defaultControls$cont, _defaultControls$wide, _defaultControls$padd, _defaultControls$marg, _defaultControls$bloc, _defaultControls$minH, _defaultControls$aspe, _defaultControls$chil; + const { + dimensions, + spacing + } = settings; + const decodeValue = rawValue => { + if (rawValue && typeof rawValue === 'object') { + return Object.keys(rawValue).reduce((acc, key) => { + acc[key] = getValueFromVariable({ + settings: { + dimensions, + spacing + } + }, '', rawValue[key]); + return acc; + }, {}); + } + return getValueFromVariable({ + settings: { + dimensions, + spacing + } + }, '', rawValue); + }; + const showSpacingPresetsControl = useHasSpacingPresets(settings); + const units = (0,external_wp_components_namespaceObject.__experimentalUseCustomUnits)({ + availableUnits: settings?.spacing?.units || ['%', 'px', 'em', 'rem', 'vw'] + }); + // Content Size + const showContentSizeControl = useHasContentSize(settings) && includeLayoutControls; + const contentSizeValue = decodeValue(inheritedValue?.layout?.contentSize); + const setContentSizeValue = newValue => { + onChange(setImmutably(value, ['layout', 'contentSize'], newValue || undefined)); + }; + const hasUserSetContentSizeValue = () => !!value?.layout?.contentSize; + const resetContentSizeValue = () => setContentSizeValue(undefined); + // Wide Size + const showWideSizeControl = useHasWideSize(settings) && includeLayoutControls; + const wideSizeValue = decodeValue(inheritedValue?.layout?.wideSize); + const setWideSizeValue = newValue => { + onChange(setImmutably(value, ['layout', 'wideSize'], newValue || undefined)); + }; + const hasUserSetWideSizeValue = () => !!value?.layout?.wideSize; + const resetWideSizeValue = () => setWideSizeValue(undefined); + // Padding + const showPaddingControl = useHasPadding(settings); + const rawPadding = decodeValue(inheritedValue?.spacing?.padding); + const paddingValues = splitStyleValue(rawPadding); + const paddingSides = Array.isArray(settings?.spacing?.padding) ? settings?.spacing?.padding : settings?.spacing?.padding?.sides; + const isAxialPadding = paddingSides && paddingSides.some(side => AXIAL_SIDES.includes(side)); + const setPaddingValues = newPaddingValues => { + const padding = filterValuesBySides(newPaddingValues, paddingSides); + onChange(setImmutably(value, ['spacing', 'padding'], padding)); + }; + const hasPaddingValue = () => !!value?.spacing?.padding && Object.keys(value?.spacing?.padding).length; + const resetPaddingValue = () => setPaddingValues(undefined); + const onMouseOverPadding = () => onVisualize('padding'); + // Margin + const showMarginControl = useHasMargin(settings); + const rawMargin = decodeValue(inheritedValue?.spacing?.margin); + const marginValues = splitStyleValue(rawMargin); + const marginSides = Array.isArray(settings?.spacing?.margin) ? settings?.spacing?.margin : settings?.spacing?.margin?.sides; + const isAxialMargin = marginSides && marginSides.some(side => AXIAL_SIDES.includes(side)); + const setMarginValues = newMarginValues => { + const margin = filterValuesBySides(newMarginValues, marginSides); + onChange(setImmutably(value, ['spacing', 'margin'], margin)); + }; + const hasMarginValue = () => !!value?.spacing?.margin && Object.keys(value?.spacing?.margin).length; + const resetMarginValue = () => setMarginValues(undefined); + const onMouseOverMargin = () => onVisualize('margin'); -const search_results_INITIAL_INSERTER_RESULTS = 9; -/** - * Shared reference to an empty array for cases where it is important to avoid - * returning a new array reference on every invocation and rerendering the component. - * - * @type {Array} - */ -const search_results_EMPTY_ARRAY = []; -function InserterSearchResults({ - filterValue, - onSelect, - onHover, - onHoverPattern, - rootClientId, - clientId, - isAppender, - __experimentalInsertionIndex, - maxBlockPatterns, - maxBlockTypes, - showBlockDirectory = false, - isDraggable = true, - shouldFocusBlock = true, - prioritizePatterns, - selectBlockOnInsert -}) { - const debouncedSpeak = (0,external_wp_compose_namespaceObject.useDebounce)(external_wp_a11y_namespaceObject.speak, 500); - const { - prioritizedBlocks - } = (0,external_wp_data_namespaceObject.useSelect)(select => { - const blockListSettings = select(store).getBlockListSettings(rootClientId); - return { - prioritizedBlocks: blockListSettings?.prioritizedInserterBlocks || search_results_EMPTY_ARRAY - }; - }, [rootClientId]); - const [destinationRootClientId, onInsertBlocks] = use_insertion_point({ - onSelect, - rootClientId, - clientId, - isAppender, - insertionIndex: __experimentalInsertionIndex, - shouldFocusBlock, - selectBlockOnInsert - }); - const [blockTypes, blockTypeCategories, blockTypeCollections, onSelectBlockType] = use_block_types_state(destinationRootClientId, onInsertBlocks); - const [patterns,, onClickPattern] = use_patterns_state(onInsertBlocks, destinationRootClientId); - const filteredBlockPatterns = (0,external_wp_element_namespaceObject.useMemo)(() => { - if (maxBlockPatterns === 0) { - return []; - } - const results = searchItems(patterns, filterValue); - return maxBlockPatterns !== undefined ? results.slice(0, maxBlockPatterns) : results; - }, [filterValue, patterns, maxBlockPatterns]); - let maxBlockTypesToShow = maxBlockTypes; - if (prioritizePatterns && filteredBlockPatterns.length > 2) { - maxBlockTypesToShow = 0; - } - const filteredBlockTypes = (0,external_wp_element_namespaceObject.useMemo)(() => { - if (maxBlockTypesToShow === 0) { - return []; + // Block Gap + const showGapControl = useHasGap(settings); + const gapValue = decodeValue(inheritedValue?.spacing?.blockGap); + const gapValues = splitGapValue(gapValue); + const gapSides = Array.isArray(settings?.spacing?.blockGap) ? settings?.spacing?.blockGap : settings?.spacing?.blockGap?.sides; + const isAxialGap = gapSides && gapSides.some(side => AXIAL_SIDES.includes(side)); + const setGapValue = newGapValue => { + onChange(setImmutably(value, ['spacing', 'blockGap'], newGapValue)); + }; + const setGapValues = nextBoxGapValue => { + if (!nextBoxGapValue) { + setGapValue(null); } - const nonPatternBlockTypes = blockTypes.filter(blockType => blockType.name !== 'core/block'); - let orderedItems = orderBy(nonPatternBlockTypes, 'frecency', 'desc'); - if (!filterValue && prioritizedBlocks.length) { - orderedItems = orderInserterBlockItems(orderedItems, prioritizedBlocks); + // If axial gap is not enabled, treat the 'top' value as the shorthand gap value. + if (!isAxialGap && nextBoxGapValue?.hasOwnProperty('top')) { + setGapValue(nextBoxGapValue.top); + } else { + setGapValue({ + top: nextBoxGapValue?.top, + left: nextBoxGapValue?.left + }); } - const results = searchBlockItems(orderedItems, blockTypeCategories, blockTypeCollections, filterValue); - return maxBlockTypesToShow !== undefined ? results.slice(0, maxBlockTypesToShow) : results; - }, [filterValue, blockTypes, blockTypeCategories, blockTypeCollections, maxBlockTypesToShow, prioritizedBlocks]); + }; + const resetGapValue = () => setGapValue(undefined); + const hasGapValue = () => !!value?.spacing?.blockGap; - // Announce search results on change. - (0,external_wp_element_namespaceObject.useEffect)(() => { - if (!filterValue) { - return; - } - const count = filteredBlockTypes.length + filteredBlockPatterns.length; - const resultsFoundMessage = (0,external_wp_i18n_namespaceObject.sprintf)( /* translators: %d: number of results. */ - (0,external_wp_i18n_namespaceObject._n)('%d result found.', '%d results found.', count), count); - debouncedSpeak(resultsFoundMessage); - }, [filterValue, debouncedSpeak, filteredBlockTypes, filteredBlockPatterns]); - const currentShownBlockTypes = (0,external_wp_compose_namespaceObject.useAsyncList)(filteredBlockTypes, { - step: search_results_INITIAL_INSERTER_RESULTS - }); - const currentShownPatterns = (0,external_wp_compose_namespaceObject.useAsyncList)(currentShownBlockTypes.length === filteredBlockTypes.length ? filteredBlockPatterns : search_results_EMPTY_ARRAY); - const hasItems = filteredBlockTypes.length > 0 || filteredBlockPatterns.length > 0; - const blocksUI = !!filteredBlockTypes.length && (0,external_wp_element_namespaceObject.createElement)(panel, { - title: (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.VisuallyHidden, null, (0,external_wp_i18n_namespaceObject.__)('Blocks')) - }, (0,external_wp_element_namespaceObject.createElement)(block_types_list, { - items: currentShownBlockTypes, - onSelect: onSelectBlockType, - onHover: onHover, - label: (0,external_wp_i18n_namespaceObject.__)('Blocks'), - isDraggable: isDraggable - })); - const patternsUI = !!filteredBlockPatterns.length && (0,external_wp_element_namespaceObject.createElement)(panel, { - title: (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.VisuallyHidden, null, (0,external_wp_i18n_namespaceObject.__)('Block patterns')) - }, (0,external_wp_element_namespaceObject.createElement)("div", { - className: "block-editor-inserter__quick-inserter-patterns" - }, (0,external_wp_element_namespaceObject.createElement)(block_patterns_list, { - shownPatterns: currentShownPatterns, - blockPatterns: filteredBlockPatterns, - onClickPattern: onClickPattern, - onHover: onHoverPattern, - isDraggable: isDraggable + // Min Height + const showMinHeightControl = useHasMinHeight(settings); + const minHeightValue = decodeValue(inheritedValue?.dimensions?.minHeight); + const setMinHeightValue = newValue => { + const tempValue = setImmutably(value, ['dimensions', 'minHeight'], newValue); + // Apply min-height, while removing any applied aspect ratio. + onChange(setImmutably(tempValue, ['dimensions', 'aspectRatio'], undefined)); + }; + const resetMinHeightValue = () => { + setMinHeightValue(undefined); + }; + const hasMinHeightValue = () => !!value?.dimensions?.minHeight; + + // Aspect Ratio + const showAspectRatioControl = useHasAspectRatio(settings); + const aspectRatioValue = decodeValue(inheritedValue?.dimensions?.aspectRatio); + const setAspectRatioValue = newValue => { + const tempValue = setImmutably(value, ['dimensions', 'aspectRatio'], newValue); + // Apply aspect-ratio, while removing any applied min-height. + onChange(setImmutably(tempValue, ['dimensions', 'minHeight'], undefined)); + }; + const hasAspectRatioValue = () => !!value?.dimensions?.aspectRatio; + + // Child Layout + const showChildLayoutControl = useHasChildLayout(settings); + const childLayout = inheritedValue?.layout; + const { + orientation = 'horizontal' + } = (_settings$parentLayou2 = settings?.parentLayout) !== null && _settings$parentLayou2 !== void 0 ? _settings$parentLayou2 : {}; + const childLayoutOrientationLabel = orientation === 'horizontal' ? (0,external_wp_i18n_namespaceObject.__)('Width') : (0,external_wp_i18n_namespaceObject.__)('Height'); + const setChildLayout = newChildLayout => { + onChange({ + ...value, + layout: { + ...value?.layout, + ...newChildLayout + } + }); + }; + const resetChildLayoutValue = () => { + setChildLayout({ + selfStretch: undefined, + flexSize: undefined + }); + }; + const hasChildLayoutValue = () => !!value?.layout; + const resetAllFilter = (0,external_wp_element_namespaceObject.useCallback)(previousValue => { + return { + ...previousValue, + layout: utils_cleanEmptyObject({ + ...previousValue?.layout, + contentSize: undefined, + wideSize: undefined, + selfStretch: undefined, + flexSize: undefined + }), + spacing: { + ...previousValue?.spacing, + padding: undefined, + margin: undefined, + blockGap: undefined + }, + dimensions: { + ...previousValue?.dimensions, + minHeight: undefined, + aspectRatio: undefined + } + }; + }, []); + const onMouseLeaveControls = () => onVisualize(false); + return (0,external_React_.createElement)(Wrapper, { + resetAllFilter: resetAllFilter, + value: value, + onChange: onChange, + panelId: panelId + }, (showContentSizeControl || showWideSizeControl) && (0,external_React_.createElement)("span", { + className: "span-columns" + }, (0,external_wp_i18n_namespaceObject.__)('Set the width of the main content area.')), showContentSizeControl && (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalToolsPanelItem, { + className: "single-column", + label: (0,external_wp_i18n_namespaceObject.__)('Content size'), + hasValue: hasUserSetContentSizeValue, + onDeselect: resetContentSizeValue, + isShownByDefault: (_defaultControls$cont = defaultControls.contentSize) !== null && _defaultControls$cont !== void 0 ? _defaultControls$cont : dimensions_panel_DEFAULT_CONTROLS.contentSize, + panelId: panelId + }, (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalHStack, { + alignment: "flex-end", + justify: "flex-start" + }, (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalUnitControl, { + label: (0,external_wp_i18n_namespaceObject.__)('Content'), + labelPosition: "top", + __unstableInputWidth: "80px", + value: contentSizeValue || '', + onChange: nextContentSize => { + setContentSizeValue(nextContentSize); + }, + units: units + }), (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalView, null, (0,external_React_.createElement)(build_module_icon, { + icon: position_center + })))), showWideSizeControl && (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalToolsPanelItem, { + className: "single-column", + label: (0,external_wp_i18n_namespaceObject.__)('Wide size'), + hasValue: hasUserSetWideSizeValue, + onDeselect: resetWideSizeValue, + isShownByDefault: (_defaultControls$wide = defaultControls.wideSize) !== null && _defaultControls$wide !== void 0 ? _defaultControls$wide : dimensions_panel_DEFAULT_CONTROLS.wideSize, + panelId: panelId + }, (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalHStack, { + alignment: "flex-end", + justify: "flex-start" + }, (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalUnitControl, { + label: (0,external_wp_i18n_namespaceObject.__)('Wide'), + labelPosition: "top", + __unstableInputWidth: "80px", + value: wideSizeValue || '', + onChange: nextWideSize => { + setWideSizeValue(nextWideSize); + }, + units: units + }), (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalView, null, (0,external_React_.createElement)(build_module_icon, { + icon: stretch_wide + })))), showPaddingControl && (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalToolsPanelItem, { + hasValue: hasPaddingValue, + label: (0,external_wp_i18n_namespaceObject.__)('Padding'), + onDeselect: resetPaddingValue, + isShownByDefault: (_defaultControls$padd = defaultControls.padding) !== null && _defaultControls$padd !== void 0 ? _defaultControls$padd : dimensions_panel_DEFAULT_CONTROLS.padding, + className: classnames_default()({ + 'tools-panel-item-spacing': showSpacingPresetsControl + }), + panelId: panelId + }, !showSpacingPresetsControl && (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalBoxControl, { + values: paddingValues, + onChange: setPaddingValues, + label: (0,external_wp_i18n_namespaceObject.__)('Padding'), + sides: paddingSides, + units: units, + allowReset: false, + splitOnAxis: isAxialPadding, + onMouseOver: onMouseOverPadding, + onMouseOut: onMouseLeaveControls + }), showSpacingPresetsControl && (0,external_React_.createElement)(SpacingSizesControl, { + values: paddingValues, + onChange: setPaddingValues, + label: (0,external_wp_i18n_namespaceObject.__)('Padding'), + sides: paddingSides, + units: units, + allowReset: false, + onMouseOver: onMouseOverPadding, + onMouseOut: onMouseLeaveControls + })), showMarginControl && (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalToolsPanelItem, { + hasValue: hasMarginValue, + label: (0,external_wp_i18n_namespaceObject.__)('Margin'), + onDeselect: resetMarginValue, + isShownByDefault: (_defaultControls$marg = defaultControls.margin) !== null && _defaultControls$marg !== void 0 ? _defaultControls$marg : dimensions_panel_DEFAULT_CONTROLS.margin, + className: classnames_default()({ + 'tools-panel-item-spacing': showSpacingPresetsControl + }), + panelId: panelId + }, !showSpacingPresetsControl && (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalBoxControl, { + values: marginValues, + onChange: setMarginValues, + label: (0,external_wp_i18n_namespaceObject.__)('Margin'), + sides: marginSides, + units: units, + allowReset: false, + splitOnAxis: isAxialMargin, + onMouseOver: onMouseOverMargin, + onMouseOut: onMouseLeaveControls + }), showSpacingPresetsControl && (0,external_React_.createElement)(SpacingSizesControl, { + values: marginValues, + onChange: setMarginValues, + label: (0,external_wp_i18n_namespaceObject.__)('Margin'), + sides: marginSides, + units: units, + allowReset: false, + onMouseOver: onMouseOverMargin, + onMouseOut: onMouseLeaveControls + })), showGapControl && (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalToolsPanelItem, { + hasValue: hasGapValue, + label: (0,external_wp_i18n_namespaceObject.__)('Block spacing'), + onDeselect: resetGapValue, + isShownByDefault: (_defaultControls$bloc = defaultControls.blockGap) !== null && _defaultControls$bloc !== void 0 ? _defaultControls$bloc : dimensions_panel_DEFAULT_CONTROLS.blockGap, + className: classnames_default()({ + 'tools-panel-item-spacing': showSpacingPresetsControl + }), + panelId: panelId + }, !showSpacingPresetsControl && (isAxialGap ? (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalBoxControl, { + label: (0,external_wp_i18n_namespaceObject.__)('Block spacing'), + min: 0, + onChange: setGapValues, + units: units, + sides: gapSides, + values: gapValues, + allowReset: false, + splitOnAxis: isAxialGap + }) : (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalUnitControl, { + label: (0,external_wp_i18n_namespaceObject.__)('Block spacing'), + __unstableInputWidth: "80px", + min: 0, + onChange: setGapValue, + units: units, + value: gapValue + })), showSpacingPresetsControl && (0,external_React_.createElement)(SpacingSizesControl, { + label: (0,external_wp_i18n_namespaceObject.__)('Block spacing'), + min: 0, + onChange: setGapValues, + showSideInLabel: false, + sides: isAxialGap ? gapSides : ['top'] // Use 'top' as the shorthand property in non-axial configurations. + , + values: gapValues, + allowReset: false + })), showMinHeightControl && (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalToolsPanelItem, { + hasValue: hasMinHeightValue, + label: (0,external_wp_i18n_namespaceObject.__)('Minimum height'), + onDeselect: resetMinHeightValue, + isShownByDefault: (_defaultControls$minH = defaultControls.minHeight) !== null && _defaultControls$minH !== void 0 ? _defaultControls$minH : dimensions_panel_DEFAULT_CONTROLS.minHeight, + panelId: panelId + }, (0,external_React_.createElement)(HeightControl, { + label: (0,external_wp_i18n_namespaceObject.__)('Minimum height'), + value: minHeightValue, + onChange: setMinHeightValue + })), showAspectRatioControl && (0,external_React_.createElement)(AspectRatioTool, { + hasValue: hasAspectRatioValue, + value: aspectRatioValue, + onChange: setAspectRatioValue, + panelId: panelId, + isShownByDefault: (_defaultControls$aspe = defaultControls.aspectRatio) !== null && _defaultControls$aspe !== void 0 ? _defaultControls$aspe : dimensions_panel_DEFAULT_CONTROLS.aspectRatio + }), showChildLayoutControl && (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalVStack, { + as: external_wp_components_namespaceObject.__experimentalToolsPanelItem, + spacing: 2, + hasValue: hasChildLayoutValue, + label: childLayoutOrientationLabel, + onDeselect: resetChildLayoutValue, + isShownByDefault: (_defaultControls$chil = defaultControls.childLayout) !== null && _defaultControls$chil !== void 0 ? _defaultControls$chil : dimensions_panel_DEFAULT_CONTROLS.childLayout, + panelId: panelId + }, (0,external_React_.createElement)(ChildLayoutControl, { + value: childLayout, + onChange: setChildLayout, + parentLayout: settings?.parentLayout }))); - return (0,external_wp_element_namespaceObject.createElement)(inserter_listbox, null, !showBlockDirectory && !hasItems && (0,external_wp_element_namespaceObject.createElement)(no_results, null), prioritizePatterns ? patternsUI : blocksUI, !!filteredBlockTypes.length && !!filteredBlockPatterns.length && (0,external_wp_element_namespaceObject.createElement)("div", { - className: "block-editor-inserter__quick-inserter-separator" - }), prioritizePatterns ? blocksUI : patternsUI, showBlockDirectory && (0,external_wp_element_namespaceObject.createElement)(inserter_menu_extension.Slot, { - fillProps: { - onSelect: onSelectBlockType, - onHover, - filterValue, - hasItems, - rootClientId: destinationRootClientId - } - }, fills => { - if (fills.length) { - return fills; - } - if (!hasItems) { - return (0,external_wp_element_namespaceObject.createElement)(no_results, null); - } - return null; - })); } -/* harmony default export */ var search_results = (InserterSearchResults); - -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/inserter/tabs.js +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/block-popover/use-popover-scroll.js /** * WordPress dependencies */ - -const blocksTab = { - name: 'blocks', - /* translators: Blocks tab title in the block inserter. */ - title: (0,external_wp_i18n_namespaceObject.__)('Blocks') -}; -const patternsTab = { - name: 'patterns', - /* translators: Theme and Directory Patterns tab title in the block inserter. */ - title: (0,external_wp_i18n_namespaceObject.__)('Patterns') -}; -const mediaTab = { - name: 'media', - /* translators: Media tab title in the block inserter. */ - title: (0,external_wp_i18n_namespaceObject.__)('Media') -}; -function InserterTabs({ - children, - showPatterns = false, - showMedia = false, - onSelect, - prioritizePatterns -}) { - const tabs = (0,external_wp_element_namespaceObject.useMemo)(() => { - const tempTabs = []; - if (prioritizePatterns && showPatterns) { - tempTabs.push(patternsTab); - } - tempTabs.push(blocksTab); - if (!prioritizePatterns && showPatterns) { - tempTabs.push(patternsTab); +/** + * Allow scrolling "through" popovers over the canvas. This is only called for + * as long as the pointer is over a popover. Do not use React events because it + * will bubble through portals. + * + * @param {Object} scrollableRef + */ +function usePopoverScroll(scrollableRef) { + return (0,external_wp_compose_namespaceObject.useRefEffect)(node => { + if (!scrollableRef) { + return; } - if (showMedia) { - tempTabs.push(mediaTab); + function onWheel(event) { + const { + deltaX, + deltaY + } = event; + scrollableRef.current.scrollBy(deltaX, deltaY); } - return tempTabs; - }, [prioritizePatterns, showPatterns, showMedia]); - return (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.TabPanel, { - className: "block-editor-inserter__tabs", - tabs: tabs, - onSelect: onSelect - }, children); + // Tell the browser that we do not call event.preventDefault + // See https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#improving_scrolling_performance_with_passive_listeners + const options = { + passive: true + }; + node.addEventListener('wheel', onWheel, options); + return () => { + node.removeEventListener('wheel', onWheel, options); + }; + }, [scrollableRef]); } -/* harmony default export */ var tabs = (InserterTabs); +/* harmony default export */ const use_popover_scroll = (usePopoverScroll); -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/inserter/menu.js +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/block-popover/index.js /** * External dependencies @@ -25386,196 +30561,198 @@ function InserterTabs({ - /** * Internal dependencies */ +const MAX_POPOVER_RECOMPUTE_COUNTER = Number.MAX_SAFE_INTEGER; +function BlockPopover({ + clientId, + bottomClientId, + children, + __unstableRefreshSize, + __unstableCoverTarget = false, + __unstablePopoverSlot, + __unstableContentRef, + shift = true, + ...props +}, ref) { + const selectedElement = useBlockElement(clientId); + const lastSelectedElement = useBlockElement(bottomClientId !== null && bottomClientId !== void 0 ? bottomClientId : clientId); + const mergedRefs = (0,external_wp_compose_namespaceObject.useMergeRefs)([ref, use_popover_scroll(__unstableContentRef)]); + const [popoverDimensionsRecomputeCounter, forceRecomputePopoverDimensions] = (0,external_wp_element_namespaceObject.useReducer)( + // Module is there to make sure that the counter doesn't overflow. + s => (s + 1) % MAX_POPOVER_RECOMPUTE_COUNTER, 0); + + // When blocks are moved up/down, they are animated to their new position by + // updating the `transform` property manually (i.e. without using CSS + // transitions or animations). The animation, which can also scroll the block + // editor, can sometimes cause the position of the Popover to get out of sync. + // A MutationObserver is therefore used to make sure that changes to the + // selectedElement's attribute (i.e. `transform`) can be tracked and used to + // trigger the Popover to rerender. + (0,external_wp_element_namespaceObject.useLayoutEffect)(() => { + if (!selectedElement) { + return; + } + const observer = new window.MutationObserver(forceRecomputePopoverDimensions); + observer.observe(selectedElement, { + attributes: true + }); + return () => { + observer.disconnect(); + }; + }, [selectedElement]); + const style = (0,external_wp_element_namespaceObject.useMemo)(() => { + if ( + // popoverDimensionsRecomputeCounter is by definition always equal or greater + // than 0. This check is only there to satisfy the correctness of the + // exhaustive-deps rule for the `useMemo` hook. + popoverDimensionsRecomputeCounter < 0 || !selectedElement || lastSelectedElement !== selectedElement) { + return {}; + } + return { + position: 'absolute', + width: selectedElement.offsetWidth, + height: selectedElement.offsetHeight + }; + }, [selectedElement, lastSelectedElement, __unstableRefreshSize, popoverDimensionsRecomputeCounter]); + const popoverAnchor = (0,external_wp_element_namespaceObject.useMemo)(() => { + if ( + // popoverDimensionsRecomputeCounter is by definition always equal or greater + // than 0. This check is only there to satisfy the correctness of the + // exhaustive-deps rule for the `useMemo` hook. + popoverDimensionsRecomputeCounter < 0 || !selectedElement || bottomClientId && !lastSelectedElement) { + return undefined; + } + return { + getBoundingClientRect() { + var _lastSelectedBCR$left, _lastSelectedBCR$top, _lastSelectedBCR$righ, _lastSelectedBCR$bott; + const selectedBCR = selectedElement.getBoundingClientRect(); + const lastSelectedBCR = lastSelectedElement?.getBoundingClientRect(); + // Get the biggest rectangle that encompasses completely the currently + // selected element and the last selected element: + // - for top/left coordinates, use the smaller numbers + // - for the bottom/right coordinates, use the largest numbers + const left = Math.min(selectedBCR.left, (_lastSelectedBCR$left = lastSelectedBCR?.left) !== null && _lastSelectedBCR$left !== void 0 ? _lastSelectedBCR$left : Infinity); + const top = Math.min(selectedBCR.top, (_lastSelectedBCR$top = lastSelectedBCR?.top) !== null && _lastSelectedBCR$top !== void 0 ? _lastSelectedBCR$top : Infinity); + const right = Math.max(selectedBCR.right, (_lastSelectedBCR$righ = lastSelectedBCR.right) !== null && _lastSelectedBCR$righ !== void 0 ? _lastSelectedBCR$righ : -Infinity); + const bottom = Math.max(selectedBCR.bottom, (_lastSelectedBCR$bott = lastSelectedBCR.bottom) !== null && _lastSelectedBCR$bott !== void 0 ? _lastSelectedBCR$bott : -Infinity); + const width = right - left; + const height = bottom - top; + return new window.DOMRect(left, top, width, height); + }, + contextElement: selectedElement + }; + }, [bottomClientId, lastSelectedElement, selectedElement, popoverDimensionsRecomputeCounter]); + if (!selectedElement || bottomClientId && !lastSelectedElement) { + return null; + } + return (0,external_React_.createElement)(external_wp_components_namespaceObject.Popover, { + ref: mergedRefs, + animate: false, + focusOnMount: false, + anchor: popoverAnchor + // Render in the old slot if needed for backward compatibility, + // otherwise render in place (not in the default popover slot). + , + __unstableSlotName: __unstablePopoverSlot, + inline: !__unstablePopoverSlot, + placement: "top-start", + resize: false, + flip: false, + shift: shift, + ...props, + className: classnames_default()('block-editor-block-popover', props.className), + variant: "unstyled" + }, __unstableCoverTarget && (0,external_React_.createElement)("div", { + style: style + }, children), !__unstableCoverTarget && children); +} +/* harmony default export */ const block_popover = ((0,external_wp_element_namespaceObject.forwardRef)(BlockPopover)); +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/hooks/margin.js +/** + * WordPress dependencies + */ +/** + * Internal dependencies + */ -function InserterMenu({ - rootClientId, +function getComputedCSS(element, property) { + return element.ownerDocument.defaultView.getComputedStyle(element).getPropertyValue(property); +} +function MarginVisualizer({ clientId, - isAppender, - __experimentalInsertionIndex, - onSelect, - showInserterHelpPanel, - showMostUsedBlocks, - __experimentalFilterValue = '', - shouldFocusBlock = true, - prioritizePatterns -}, ref) { - const [filterValue, setFilterValue, delayedFilterValue] = useDebouncedInput(__experimentalFilterValue); - const [hoveredItem, setHoveredItem] = (0,external_wp_element_namespaceObject.useState)(null); - const [selectedPatternCategory, setSelectedPatternCategory] = (0,external_wp_element_namespaceObject.useState)(null); - const [patternFilter, setPatternFilter] = (0,external_wp_element_namespaceObject.useState)('all'); - const [selectedMediaCategory, setSelectedMediaCategory] = (0,external_wp_element_namespaceObject.useState)(null); - const [selectedTab, setSelectedTab] = (0,external_wp_element_namespaceObject.useState)(null); - const [destinationRootClientId, onInsertBlocks, onToggleInsertionPoint] = use_insertion_point({ - rootClientId, - clientId, - isAppender, - insertionIndex: __experimentalInsertionIndex, - shouldFocusBlock - }); - const { - showPatterns, - inserterItems - } = (0,external_wp_data_namespaceObject.useSelect)(select => { - const { - __experimentalGetAllowedPatterns, - getInserterItems - } = select(store); - return { - showPatterns: !!__experimentalGetAllowedPatterns(destinationRootClientId).length, - inserterItems: getInserterItems(destinationRootClientId) - }; - }, [destinationRootClientId]); - const hasReusableBlocks = (0,external_wp_element_namespaceObject.useMemo)(() => { - return inserterItems.some(({ - category - }) => category === 'reusable'); - }, [inserterItems]); - const mediaCategories = useMediaCategories(destinationRootClientId); - const showMedia = !!mediaCategories.length; - const onInsert = (0,external_wp_element_namespaceObject.useCallback)((blocks, meta, shouldForceFocusBlock) => { - onInsertBlocks(blocks, meta, shouldForceFocusBlock); - onSelect(); - }, [onInsertBlocks, onSelect]); - const onInsertPattern = (0,external_wp_element_namespaceObject.useCallback)((blocks, patternName) => { - onInsertBlocks(blocks, { - patternName - }); - onSelect(); - }, [onInsertBlocks, onSelect]); - const onHover = (0,external_wp_element_namespaceObject.useCallback)(item => { - onToggleInsertionPoint(!!item); - setHoveredItem(item); - }, [onToggleInsertionPoint, setHoveredItem]); - const onHoverPattern = (0,external_wp_element_namespaceObject.useCallback)(item => { - onToggleInsertionPoint(!!item); - }, [onToggleInsertionPoint]); - const onClickPatternCategory = (0,external_wp_element_namespaceObject.useCallback)((patternCategory, filter) => { - setSelectedPatternCategory(patternCategory); - setPatternFilter(filter); - }, [setSelectedPatternCategory]); - const blocksTab = (0,external_wp_element_namespaceObject.useMemo)(() => (0,external_wp_element_namespaceObject.createElement)(external_wp_element_namespaceObject.Fragment, null, (0,external_wp_element_namespaceObject.createElement)("div", { - className: "block-editor-inserter__block-list" - }, (0,external_wp_element_namespaceObject.createElement)(block_types_tab, { - rootClientId: destinationRootClientId, - onInsert: onInsert, - onHover: onHover, - showMostUsedBlocks: showMostUsedBlocks - })), showInserterHelpPanel && (0,external_wp_element_namespaceObject.createElement)("div", { - className: "block-editor-inserter__tips" - }, (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.VisuallyHidden, { - as: "h2" - }, (0,external_wp_i18n_namespaceObject.__)('A tip for using the block editor')), (0,external_wp_element_namespaceObject.createElement)(tips, null))), [destinationRootClientId, onInsert, onHover, showMostUsedBlocks, showInserterHelpPanel]); - const patternsTab = (0,external_wp_element_namespaceObject.useMemo)(() => (0,external_wp_element_namespaceObject.createElement)(block_patterns_tab, { - rootClientId: destinationRootClientId, - onInsert: onInsertPattern, - onSelectCategory: onClickPatternCategory, - selectedCategory: selectedPatternCategory - }), [destinationRootClientId, onInsertPattern, onClickPatternCategory, selectedPatternCategory]); - const mediaTab = (0,external_wp_element_namespaceObject.useMemo)(() => (0,external_wp_element_namespaceObject.createElement)(media_tab, { - rootClientId: destinationRootClientId, - selectedCategory: selectedMediaCategory, - onSelectCategory: setSelectedMediaCategory, - onInsert: onInsert - }), [destinationRootClientId, onInsert, selectedMediaCategory, setSelectedMediaCategory]); - const getCurrentTab = (0,external_wp_element_namespaceObject.useCallback)(tab => { - if (tab.name === 'blocks') { - return blocksTab; - } else if (tab.name === 'patterns') { - return patternsTab; - } else if (tab.name === 'media') { - return mediaTab; - } - }, [blocksTab, patternsTab, mediaTab]); - const searchRef = (0,external_wp_element_namespaceObject.useRef)(); - (0,external_wp_element_namespaceObject.useImperativeHandle)(ref, () => ({ - focusSearch: () => { - searchRef.current.focus(); + attributes, + forceShow +}) { + const blockElement = useBlockElement(clientId); + const [style, setStyle] = (0,external_wp_element_namespaceObject.useState)(); + const margin = attributes?.style?.spacing?.margin; + (0,external_wp_element_namespaceObject.useEffect)(() => { + if (!blockElement || null === blockElement.ownerDocument.defaultView) { + return; } - })); - const showPatternPanel = selectedTab === 'patterns' && !delayedFilterValue && selectedPatternCategory; - const showAsTabs = !delayedFilterValue && (showPatterns || hasReusableBlocks || showMedia); - const showMediaPanel = selectedTab === 'media' && !delayedFilterValue && selectedMediaCategory; - const handleSetSelectedTab = value => { - // If no longer on patterns tab remove the category setting. - if (value !== 'patterns') { - setSelectedPatternCategory(null); + const top = getComputedCSS(blockElement, 'margin-top'); + const right = getComputedCSS(blockElement, 'margin-right'); + const bottom = getComputedCSS(blockElement, 'margin-bottom'); + const left = getComputedCSS(blockElement, 'margin-left'); + setStyle({ + borderTopWidth: top, + borderRightWidth: right, + borderBottomWidth: bottom, + borderLeftWidth: left, + top: top ? `-${top}` : 0, + right: right ? `-${right}` : 0, + bottom: bottom ? `-${bottom}` : 0, + left: left ? `-${left}` : 0 + }); + }, [blockElement, margin]); + const [isActive, setIsActive] = (0,external_wp_element_namespaceObject.useState)(false); + const valueRef = (0,external_wp_element_namespaceObject.useRef)(margin); + const timeoutRef = (0,external_wp_element_namespaceObject.useRef)(); + const clearTimer = () => { + if (timeoutRef.current) { + window.clearTimeout(timeoutRef.current); } - setSelectedTab(value); }; - return (0,external_wp_element_namespaceObject.createElement)("div", { - className: "block-editor-inserter__menu" - }, (0,external_wp_element_namespaceObject.createElement)("div", { - className: classnames_default()('block-editor-inserter__main-area', { - 'show-as-tabs': showAsTabs - }) - }, (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.SearchControl, { - __nextHasNoMarginBottom: true, - className: "block-editor-inserter__search", - onChange: value => { - if (hoveredItem) setHoveredItem(null); - setFilterValue(value); - }, - value: filterValue, - label: (0,external_wp_i18n_namespaceObject.__)('Search for blocks and patterns'), - placeholder: (0,external_wp_i18n_namespaceObject.__)('Search'), - ref: searchRef - }), !!delayedFilterValue && (0,external_wp_element_namespaceObject.createElement)("div", { - className: "block-editor-inserter__no-tab-container" - }, (0,external_wp_element_namespaceObject.createElement)(search_results, { - filterValue: delayedFilterValue, - onSelect: onSelect, - onHover: onHover, - onHoverPattern: onHoverPattern, - rootClientId: rootClientId, + (0,external_wp_element_namespaceObject.useEffect)(() => { + if (!external_wp_isShallowEqual_default()(margin, valueRef.current) && !forceShow) { + setIsActive(true); + valueRef.current = margin; + timeoutRef.current = setTimeout(() => { + setIsActive(false); + }, 400); + } + return () => { + setIsActive(false); + clearTimer(); + }; + }, [margin, forceShow]); + if (!isActive && !forceShow) { + return null; + } + return (0,external_React_.createElement)(block_popover, { clientId: clientId, - isAppender: isAppender, - __experimentalInsertionIndex: __experimentalInsertionIndex, - showBlockDirectory: true, - shouldFocusBlock: shouldFocusBlock - })), showAsTabs && (0,external_wp_element_namespaceObject.createElement)(tabs, { - showPatterns: showPatterns, - showReusableBlocks: hasReusableBlocks, - showMedia: showMedia, - prioritizePatterns: prioritizePatterns, - onSelect: handleSetSelectedTab - }, getCurrentTab), !delayedFilterValue && !showAsTabs && (0,external_wp_element_namespaceObject.createElement)("div", { - className: "block-editor-inserter__no-tab-container" - }, blocksTab)), showMediaPanel && (0,external_wp_element_namespaceObject.createElement)(MediaCategoryDialog, { - rootClientId: destinationRootClientId, - onInsert: onInsert, - category: selectedMediaCategory - }), showInserterHelpPanel && hoveredItem && (0,external_wp_element_namespaceObject.createElement)(preview_panel, { - item: hoveredItem - }), showPatternPanel && (0,external_wp_element_namespaceObject.createElement)(BlockPatternsCategoryDialog, { - rootClientId: destinationRootClientId, - onInsert: onInsertPattern, - onHover: onHoverPattern, - category: selectedPatternCategory, - patternFilter: patternFilter, - showTitlesAsTooltip: true + __unstableCoverTarget: true, + __unstableRefreshSize: margin, + __unstablePopoverSlot: "block-toolbar", + shift: false + }, (0,external_React_.createElement)("div", { + className: "block-editor__padding-visualizer", + style: style })); } -/* harmony default export */ var menu = ((0,external_wp_element_namespaceObject.forwardRef)(InserterMenu)); - -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/inserter/quick-inserter.js - -/** - * External dependencies - */ +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/hooks/padding.js /** * WordPress dependencies @@ -25583,111 +30760,70 @@ function InserterMenu({ - - /** * Internal dependencies */ - - - -const SEARCH_THRESHOLD = 6; -const SHOWN_BLOCK_TYPES = 6; -const SHOWN_BLOCK_PATTERNS = 2; -const SHOWN_BLOCK_PATTERNS_WITH_PRIORITIZATION = 4; -function QuickInserter({ - onSelect, - rootClientId, +function padding_getComputedCSS(element, property) { + return element.ownerDocument.defaultView.getComputedStyle(element).getPropertyValue(property); +} +function PaddingVisualizer({ clientId, - isAppender, - prioritizePatterns, - selectBlockOnInsert + value, + forceShow }) { - const [filterValue, setFilterValue] = (0,external_wp_element_namespaceObject.useState)(''); - const [destinationRootClientId, onInsertBlocks] = use_insertion_point({ - onSelect, - rootClientId, - clientId, - isAppender, - selectBlockOnInsert - }); - const [blockTypes] = use_block_types_state(destinationRootClientId, onInsertBlocks); - const [patterns] = use_patterns_state(onInsertBlocks, destinationRootClientId); - const { - setInserterIsOpened, - insertionIndex - } = (0,external_wp_data_namespaceObject.useSelect)(select => { - const { - getSettings, - getBlockIndex, - getBlockCount - } = select(store); - const settings = getSettings(); - const index = getBlockIndex(clientId); - const blockCount = getBlockCount(); - return { - setInserterIsOpened: settings.__experimentalSetIsInserterOpened, - insertionIndex: index === -1 ? blockCount : index - }; - }, [clientId]); - const showPatterns = patterns.length && (!!filterValue || prioritizePatterns); - const showSearch = showPatterns && patterns.length > SEARCH_THRESHOLD || blockTypes.length > SEARCH_THRESHOLD; + const blockElement = useBlockElement(clientId); + const [style, setStyle] = (0,external_wp_element_namespaceObject.useState)(); + const padding = value?.spacing?.padding; (0,external_wp_element_namespaceObject.useEffect)(() => { - if (setInserterIsOpened) { - setInserterIsOpened(false); + if (!blockElement || null === blockElement.ownerDocument.defaultView) { + return; } - }, [setInserterIsOpened]); - - // When clicking Browse All select the appropriate block so as - // the insertion point can work as expected. - const onBrowseAll = () => { - setInserterIsOpened({ - rootClientId, - insertionIndex, - filterValue + setStyle({ + borderTopWidth: padding_getComputedCSS(blockElement, 'padding-top'), + borderRightWidth: padding_getComputedCSS(blockElement, 'padding-right'), + borderBottomWidth: padding_getComputedCSS(blockElement, 'padding-bottom'), + borderLeftWidth: padding_getComputedCSS(blockElement, 'padding-left') }); + }, [blockElement, padding]); + const [isActive, setIsActive] = (0,external_wp_element_namespaceObject.useState)(false); + const valueRef = (0,external_wp_element_namespaceObject.useRef)(padding); + const timeoutRef = (0,external_wp_element_namespaceObject.useRef)(); + const clearTimer = () => { + if (timeoutRef.current) { + window.clearTimeout(timeoutRef.current); + } }; - let maxBlockPatterns = 0; - if (showPatterns) { - maxBlockPatterns = prioritizePatterns ? SHOWN_BLOCK_PATTERNS_WITH_PRIORITIZATION : SHOWN_BLOCK_PATTERNS; + (0,external_wp_element_namespaceObject.useEffect)(() => { + if (!external_wp_isShallowEqual_default()(padding, valueRef.current) && !forceShow) { + setIsActive(true); + valueRef.current = padding; + timeoutRef.current = setTimeout(() => { + setIsActive(false); + }, 400); + } + return () => { + setIsActive(false); + clearTimer(); + }; + }, [padding, forceShow]); + if (!isActive && !forceShow) { + return null; } - return (0,external_wp_element_namespaceObject.createElement)("div", { - className: classnames_default()('block-editor-inserter__quick-inserter', { - 'has-search': showSearch, - 'has-expand': setInserterIsOpened - }) - }, showSearch && (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.SearchControl, { - __nextHasNoMarginBottom: true, - className: "block-editor-inserter__search", - value: filterValue, - onChange: value => { - setFilterValue(value); - }, - label: (0,external_wp_i18n_namespaceObject.__)('Search for blocks and patterns'), - placeholder: (0,external_wp_i18n_namespaceObject.__)('Search') - }), (0,external_wp_element_namespaceObject.createElement)("div", { - className: "block-editor-inserter__quick-inserter-results" - }, (0,external_wp_element_namespaceObject.createElement)(search_results, { - filterValue: filterValue, - onSelect: onSelect, - rootClientId: rootClientId, + return (0,external_React_.createElement)(block_popover, { clientId: clientId, - isAppender: isAppender, - maxBlockPatterns: maxBlockPatterns, - maxBlockTypes: SHOWN_BLOCK_TYPES, - isDraggable: false, - prioritizePatterns: prioritizePatterns, - selectBlockOnInsert: selectBlockOnInsert - })), setInserterIsOpened && (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.Button, { - className: "block-editor-inserter__quick-inserter-expand", - onClick: onBrowseAll, - "aria-label": (0,external_wp_i18n_namespaceObject.__)('Browse all. This will open the main inserter panel in the editor toolbar.') - }, (0,external_wp_i18n_namespaceObject.__)('Browse all'))); + __unstableCoverTarget: true, + __unstableRefreshSize: padding, + __unstablePopoverSlot: "block-toolbar", + shift: false + }, (0,external_React_.createElement)("div", { + className: "block-editor__padding-visualizer", + style: style + })); } -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/inserter/index.js +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/hooks/dimensions.js /** * External dependencies @@ -25702,556 +30838,595 @@ function QuickInserter({ - - - - /** * Internal dependencies */ -const defaultRenderToggle = ({ - onToggle, - disabled, - isOpen, - blockTitle, - hasSingleBlockType, - toggleProps = {}, - prioritizePatterns -}) => { - const { - as: Wrapper = external_wp_components_namespaceObject.Button, - label: labelProp, - onClick, - ...rest - } = toggleProps; - let label = labelProp; - if (!label && hasSingleBlockType) { - label = (0,external_wp_i18n_namespaceObject.sprintf)( - // translators: %s: the name of the block when there is only one - (0,external_wp_i18n_namespaceObject._x)('Add %s', 'directly add the only allowed block'), blockTitle); - } else if (!label && prioritizePatterns) { - label = (0,external_wp_i18n_namespaceObject.__)('Add pattern'); - } else if (!label) { - label = (0,external_wp_i18n_namespaceObject._x)('Add block', 'Generic label for block inserter button'); - } - // Handle both onClick functions from the toggle and the parent component. - function handleClick(event) { - if (onToggle) { - onToggle(event); - } - if (onClick) { - onClick(event); - } - } - return (0,external_wp_element_namespaceObject.createElement)(Wrapper, { - icon: library_plus, - label: label, - tooltipPosition: "bottom", - onClick: handleClick, - className: "block-editor-inserter__toggle", - "aria-haspopup": !hasSingleBlockType ? 'true' : false, - "aria-expanded": !hasSingleBlockType ? isOpen : false, - disabled: disabled, - ...rest - }); -}; -class PrivateInserter extends external_wp_element_namespaceObject.Component { - constructor() { - super(...arguments); - this.onToggle = this.onToggle.bind(this); - this.renderToggle = this.renderToggle.bind(this); - this.renderContent = this.renderContent.bind(this); - } - onToggle(isOpen) { - const { - onToggle - } = this.props; - // Surface toggle callback to parent component. - if (onToggle) { - onToggle(isOpen); - } - } - /** - * Render callback to display Dropdown toggle element. - * - * @param {Object} options - * @param {Function} options.onToggle Callback to invoke when toggle is - * pressed. - * @param {boolean} options.isOpen Whether dropdown is currently open. - * - * @return {WPElement} Dropdown toggle element. - */ - renderToggle({ - onToggle, - isOpen - }) { - const { - disabled, - blockTitle, - hasSingleBlockType, - directInsertBlock, - toggleProps, - hasItems, - renderToggle = defaultRenderToggle, - prioritizePatterns - } = this.props; - return renderToggle({ - onToggle, - isOpen, - disabled: disabled || !hasItems, - blockTitle, - hasSingleBlockType, - directInsertBlock, - toggleProps, - prioritizePatterns - }); - } - /** - * Render callback to display Dropdown content element. - * - * @param {Object} options - * @param {Function} options.onClose Callback to invoke when dropdown is - * closed. - * - * @return {WPElement} Dropdown content element. - */ - renderContent({ - onClose - }) { - const { - rootClientId, - clientId, - isAppender, - showInserterHelpPanel, - // This prop is experimental to give some time for the quick inserter to mature - // Feel free to make them stable after a few releases. - __experimentalIsQuick: isQuick, - prioritizePatterns, - onSelectOrClose, - selectBlockOnInsert - } = this.props; - if (isQuick) { - return (0,external_wp_element_namespaceObject.createElement)(QuickInserter, { - onSelect: blocks => { - const firstBlock = Array.isArray(blocks) && blocks?.length ? blocks[0] : blocks; - if (onSelectOrClose && typeof onSelectOrClose === 'function') { - onSelectOrClose(firstBlock); - } - onClose(); - }, - rootClientId: rootClientId, - clientId: clientId, - isAppender: isAppender, - prioritizePatterns: prioritizePatterns, - selectBlockOnInsert: selectBlockOnInsert - }); +const DIMENSIONS_SUPPORT_KEY = 'dimensions'; +const SPACING_SUPPORT_KEY = 'spacing'; +const dimensions_ALL_SIDES = (/* unused pure expression or super */ null && (['top', 'right', 'bottom', 'left'])); +const dimensions_AXIAL_SIDES = (/* unused pure expression or super */ null && (['vertical', 'horizontal'])); +function useVisualizer() { + const [property, setProperty] = (0,external_wp_element_namespaceObject.useState)(false); + const { + hideBlockInterface, + showBlockInterface + } = unlock((0,external_wp_data_namespaceObject.useDispatch)(store)); + (0,external_wp_element_namespaceObject.useEffect)(() => { + if (!property) { + showBlockInterface(); + } else { + hideBlockInterface(); } - return (0,external_wp_element_namespaceObject.createElement)(menu, { - onSelect: () => { - onClose(); - }, - rootClientId: rootClientId, - clientId: clientId, - isAppender: isAppender, - showInserterHelpPanel: showInserterHelpPanel, - prioritizePatterns: prioritizePatterns + }, [property, showBlockInterface, hideBlockInterface]); + return [property, setProperty]; +} +function DimensionsInspectorControl({ + children, + resetAllFilter +}) { + const attributesResetAllFilter = (0,external_wp_element_namespaceObject.useCallback)(attributes => { + const existingStyle = attributes.style; + const updatedStyle = resetAllFilter(existingStyle); + return { + ...attributes, + style: updatedStyle + }; + }, [resetAllFilter]); + return (0,external_React_.createElement)(inspector_controls, { + group: "dimensions", + resetAllFilter: attributesResetAllFilter + }, children); +} +function dimensions_DimensionsPanel({ + clientId, + name, + setAttributes, + settings +}) { + const isEnabled = useHasDimensionsPanel(settings); + const value = (0,external_wp_data_namespaceObject.useSelect)(select => select(store).getBlockAttributes(clientId)?.style, [clientId]); + const [visualizedProperty, setVisualizedProperty] = useVisualizer(); + const onChange = newStyle => { + setAttributes({ + style: utils_cleanEmptyObject(newStyle) }); + }; + if (!isEnabled) { + return null; } - render() { - const { - position, - hasSingleBlockType, - directInsertBlock, - insertOnlyAllowedBlock, - __experimentalIsQuick: isQuick, - onSelectOrClose - } = this.props; - if (hasSingleBlockType || directInsertBlock) { - return this.renderToggle({ - onToggle: insertOnlyAllowedBlock - }); - } - return (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.Dropdown, { - className: "block-editor-inserter", - contentClassName: classnames_default()('block-editor-inserter__popover', { - 'is-quick': isQuick - }), - popoverProps: { - position, - shift: true - }, - onToggle: this.onToggle, - expandOnMobile: true, - headerTitle: (0,external_wp_i18n_namespaceObject.__)('Add a block'), - renderToggle: this.renderToggle, - renderContent: this.renderContent, - onClose: onSelectOrClose - }); + const defaultDimensionsControls = (0,external_wp_blocks_namespaceObject.getBlockSupport)(name, [DIMENSIONS_SUPPORT_KEY, '__experimentalDefaultControls']); + const defaultSpacingControls = (0,external_wp_blocks_namespaceObject.getBlockSupport)(name, [SPACING_SUPPORT_KEY, '__experimentalDefaultControls']); + const defaultControls = { + ...defaultDimensionsControls, + ...defaultSpacingControls + }; + return (0,external_React_.createElement)(external_React_.Fragment, null, (0,external_React_.createElement)(DimensionsPanel, { + as: DimensionsInspectorControl, + panelId: clientId, + settings: settings, + value: value, + onChange: onChange, + defaultControls: defaultControls, + onVisualize: setVisualizedProperty + }), !!settings?.spacing?.padding && (0,external_React_.createElement)(PaddingVisualizer, { + forceShow: visualizedProperty === 'padding', + clientId: clientId, + value: value + }), !!settings?.spacing?.margin && (0,external_React_.createElement)(MarginVisualizer, { + forceShow: visualizedProperty === 'margin', + clientId: clientId, + value: value + })); +} + +/** + * Determine whether there is block support for dimensions. + * + * @param {string} blockName Block name. + * @param {string} feature Background image feature to check for. + * + * @return {boolean} Whether there is support. + */ +function hasDimensionsSupport(blockName, feature = 'any') { + if (external_wp_element_namespaceObject.Platform.OS !== 'web') { + return false; } + const support = (0,external_wp_blocks_namespaceObject.getBlockSupport)(blockName, DIMENSIONS_SUPPORT_KEY); + if (support === true) { + return true; + } + if (feature === 'any') { + return !!(support?.aspectRatio || !!support?.minHeight); + } + return !!support?.[feature]; } -const ComposedPrivateInserter = (0,external_wp_compose_namespaceObject.compose)([(0,external_wp_data_namespaceObject.withSelect)((select, { - clientId, - rootClientId, - shouldDirectInsert = true -}) => { - const { - getBlockRootClientId, - hasInserterItems, - getAllowedBlocks, - getDirectInsertBlock, - getSettings - } = select(store); - const { - getBlockVariations - } = select(external_wp_blocks_namespaceObject.store); - rootClientId = rootClientId || getBlockRootClientId(clientId) || undefined; - const allowedBlocks = getAllowedBlocks(rootClientId); - const directInsertBlock = shouldDirectInsert && getDirectInsertBlock(rootClientId); - const settings = getSettings(); - const hasSingleBlockType = allowedBlocks?.length === 1 && getBlockVariations(allowedBlocks[0].name, 'inserter')?.length === 0; - let allowedBlockType = false; - if (hasSingleBlockType) { - allowedBlockType = allowedBlocks[0]; +/* harmony default export */ const dimensions = ({ + useBlockProps: dimensions_useBlockProps, + attributeKeys: ['minHeight', 'style'], + hasSupport(name) { + return hasDimensionsSupport(name, 'aspectRatio'); + } +}); +function dimensions_useBlockProps({ + name, + minHeight, + style +}) { + if (!hasDimensionsSupport(name, 'aspectRatio') || shouldSkipSerialization(name, DIMENSIONS_SUPPORT_KEY, 'aspectRatio')) { + return {}; + } + const className = classnames_default()({ + 'has-aspect-ratio': !!style?.dimensions?.aspectRatio + }); + + // Allow dimensions-based inline style overrides to override any global styles rules that + // might be set for the block, and therefore affect the display of the aspect ratio. + const inlineStyleOverrides = {}; + + // Apply rules to unset incompatible styles. + // Note that a set `aspectRatio` will win out if both an aspect ratio and a minHeight are set. + // This is because the aspect ratio is a newer block support, so (in theory) any aspect ratio + // that is set should be intentional and should override any existing minHeight. The Cover block + // and dimensions controls have logic that will manually clear the aspect ratio if a minHeight + // is set. + if (style?.dimensions?.aspectRatio) { + // To ensure the aspect ratio does not get overridden by `minHeight` unset any existing rule. + inlineStyleOverrides.minHeight = 'unset'; + } else if (minHeight || style?.dimensions?.minHeight) { + // To ensure the minHeight does not get overridden by `aspectRatio` unset any existing rule. + inlineStyleOverrides.aspectRatio = 'unset'; } return { - hasItems: hasInserterItems(rootClientId), - hasSingleBlockType, - blockTitle: allowedBlockType ? allowedBlockType.title : '', - allowedBlockType, - directInsertBlock, - rootClientId, - prioritizePatterns: settings.__experimentalPreferPatternsOnRoot && !rootClientId + className, + style: inlineStyleOverrides }; -}), (0,external_wp_data_namespaceObject.withDispatch)((dispatch, ownProps, { - select -}) => { - return { - insertOnlyAllowedBlock() { - const { - rootClientId, - clientId, - isAppender, - hasSingleBlockType, - allowedBlockType, - directInsertBlock, - onSelectOrClose, - selectBlockOnInsert - } = ownProps; - if (!hasSingleBlockType && !directInsertBlock) { - return; - } - function getAdjacentBlockAttributes(attributesToCopy) { - const { - getBlock, - getPreviousBlockClientId - } = select(store); - if (!attributesToCopy || !clientId && !rootClientId) { - return {}; - } - const result = {}; - let adjacentAttributes = {}; +} - // If there is no clientId, then attempt to get attributes - // from the last block within innerBlocks of the root block. - if (!clientId) { - const parentBlock = getBlock(rootClientId); - if (parentBlock?.innerBlocks?.length) { - const lastInnerBlock = parentBlock.innerBlocks[parentBlock.innerBlocks.length - 1]; - if (directInsertBlock && directInsertBlock?.name === lastInnerBlock.name) { - adjacentAttributes = lastInnerBlock.attributes; - } - } - } else { - // Otherwise, attempt to get attributes from the - // previous block relative to the current clientId. - const currentBlock = getBlock(clientId); - const previousBlock = getBlock(getPreviousBlockClientId(clientId)); - if (currentBlock?.name === previousBlock?.name) { - adjacentAttributes = previousBlock?.attributes || {}; - } - } +/** + * @deprecated + */ +function useCustomSides() { + external_wp_deprecated_default()('wp.blockEditor.__experimentalUseCustomSides', { + since: '6.3', + version: '6.4' + }); +} - // Copy over only those attributes flagged to be copied. - attributesToCopy.forEach(attribute => { - if (adjacentAttributes.hasOwnProperty(attribute)) { - result[attribute] = adjacentAttributes[attribute]; - } - }); - return result; - } - function getInsertionIndex() { - const { - getBlockIndex, - getBlockSelectionEnd, - getBlockOrder, - getBlockRootClientId - } = select(store); +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/hooks/style.js + +/** + * WordPress dependencies + */ - // If the clientId is defined, we insert at the position of the block. - if (clientId) { - return getBlockIndex(clientId); - } - // If there a selected block, we insert after the selected block. - const end = getBlockSelectionEnd(); - if (!isAppender && end && getBlockRootClientId(end) === rootClientId) { - return getBlockIndex(end) + 1; - } - // Otherwise, we insert at the end of the current rootClientId. - return getBlockOrder(rootClientId).length; - } - const { - insertBlock - } = dispatch(store); - let blockToInsert; - // Attempt to augment the directInsertBlock with attributes from an adjacent block. - // This ensures styling from nearby blocks is preserved in the newly inserted block. - // See: https://github.com/WordPress/gutenberg/issues/37904 - if (directInsertBlock) { - const newAttributes = getAdjacentBlockAttributes(directInsertBlock.attributesToCopy); - blockToInsert = (0,external_wp_blocks_namespaceObject.createBlock)(directInsertBlock.name, { - ...(directInsertBlock.attributes || {}), - ...newAttributes - }); - } else { - blockToInsert = (0,external_wp_blocks_namespaceObject.createBlock)(allowedBlockType.name); - } - insertBlock(blockToInsert, getInsertionIndex(), rootClientId, selectBlockOnInsert); - if (onSelectOrClose) { - onSelectOrClose({ - clientId: blockToInsert?.clientId - }); - } - const message = (0,external_wp_i18n_namespaceObject.sprintf)( - // translators: %s: the name of the block that has been added - (0,external_wp_i18n_namespaceObject.__)('%s block added'), allowedBlockType.title); - (0,external_wp_a11y_namespaceObject.speak)(message); - } - }; -}), -// The global inserter should always be visible, we are using ( ! isAppender && ! rootClientId && ! clientId ) as -// a way to detect the global Inserter. -(0,external_wp_compose_namespaceObject.ifCondition)(({ - hasItems, - isAppender, - rootClientId, - clientId -}) => hasItems || !isAppender && !rootClientId && !clientId)])(PrivateInserter); -const Inserter = (0,external_wp_element_namespaceObject.forwardRef)((props, ref) => { - return (0,external_wp_element_namespaceObject.createElement)(ComposedPrivateInserter, { - ref: ref, - ...props - }); -}); -/* harmony default export */ var inserter = (Inserter); -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/default-block-appender/index.js /** - * External dependencies + * Internal dependencies */ + + + + + + +const styleSupportKeys = [...TYPOGRAPHY_SUPPORT_KEYS, BORDER_SUPPORT_KEY, COLOR_SUPPORT_KEY, DIMENSIONS_SUPPORT_KEY, BACKGROUND_SUPPORT_KEY, SPACING_SUPPORT_KEY, SHADOW_SUPPORT_KEY]; +const hasStyleSupport = nameOrType => styleSupportKeys.some(key => (0,external_wp_blocks_namespaceObject.hasBlockSupport)(nameOrType, key)); + /** - * WordPress dependencies + * Returns the inline styles to add depending on the style object + * + * @param {Object} styles Styles configuration. + * + * @return {Object} Flattened CSS variables declaration. */ +function getInlineStyles(styles = {}) { + const output = {}; + // The goal is to move everything to server side generated engine styles + // This is temporary as we absorb more and more styles into the engine. + (0,external_wp_styleEngine_namespaceObject.getCSSRules)(styles).forEach(rule => { + output[rule.key] = rule.value; + }); + return output; +} +/** + * Filters registered block settings, extending attributes to include `style` attribute. + * + * @param {Object} settings Original block settings. + * + * @return {Object} Filtered block settings. + */ +function style_addAttribute(settings) { + if (!hasStyleSupport(settings)) { + return settings; + } + // Allow blocks to specify their own attribute definition with default values if needed. + if (!settings.attributes.style) { + Object.assign(settings.attributes, { + style: { + type: 'object' + } + }); + } + return settings; +} +/** + * A dictionary of paths to flag skipping block support serialization as the key, + * with values providing the style paths to be omitted from serialization. + * + * @constant + * @type {Record } + */ +const skipSerializationPathsEdit = { + [`${BORDER_SUPPORT_KEY}.__experimentalSkipSerialization`]: ['border'], + [`${COLOR_SUPPORT_KEY}.__experimentalSkipSerialization`]: [COLOR_SUPPORT_KEY], + [`${TYPOGRAPHY_SUPPORT_KEY}.__experimentalSkipSerialization`]: [TYPOGRAPHY_SUPPORT_KEY], + [`${DIMENSIONS_SUPPORT_KEY}.__experimentalSkipSerialization`]: [DIMENSIONS_SUPPORT_KEY], + [`${SPACING_SUPPORT_KEY}.__experimentalSkipSerialization`]: [SPACING_SUPPORT_KEY], + [`${SHADOW_SUPPORT_KEY}.__experimentalSkipSerialization`]: [SHADOW_SUPPORT_KEY] +}; - +/** + * A dictionary of paths to flag skipping block support serialization as the key, + * with values providing the style paths to be omitted from serialization. + * + * Extends the Edit skip paths to enable skipping additional paths in just + * the Save component. This allows a block support to be serialized within the + * editor, while using an alternate approach, such as server-side rendering, when + * the support is saved. + * + * @constant + * @type {Record } + */ +const skipSerializationPathsSave = { + ...skipSerializationPathsEdit, + [`${DIMENSIONS_SUPPORT_KEY}.aspectRatio`]: [`${DIMENSIONS_SUPPORT_KEY}.aspectRatio`], + // Skip serialization of aspect ratio in save mode. + [`${BACKGROUND_SUPPORT_KEY}`]: [BACKGROUND_SUPPORT_KEY] // Skip serialization of background support in save mode. +}; +const skipSerializationPathsSaveChecks = { + [`${DIMENSIONS_SUPPORT_KEY}.aspectRatio`]: true, + [`${BACKGROUND_SUPPORT_KEY}`]: true +}; /** - * Internal dependencies + * A dictionary used to normalize feature names between support flags, style + * object properties and __experimentSkipSerialization configuration arrays. + * + * This allows not having to provide a migration for a support flag and possible + * backwards compatibility bridges, while still achieving consistency between + * the support flag and the skip serialization array. + * + * @constant + * @type {Record } */ +const renamedFeatures = { + gradients: 'gradient' +}; - +/** + * A utility function used to remove one or more paths from a style object. + * Works in a way similar to Lodash's `omit()`. See unit tests and examples below. + * + * It supports a single string path: + * + * ``` + * omitStyle( { color: 'red' }, 'color' ); // {} + * ``` + * + * or an array of paths: + * + * ``` + * omitStyle( { color: 'red', background: '#fff' }, [ 'color', 'background' ] ); // {} + * ``` + * + * It also allows you to specify paths at multiple levels in a string. + * + * ``` + * omitStyle( { typography: { textDecoration: 'underline' } }, 'typography.textDecoration' ); // {} + * ``` + * + * You can remove multiple paths at the same time: + * + * ``` + * omitStyle( + * { + * typography: { + * textDecoration: 'underline', + * textTransform: 'uppercase', + * } + * }, + * [ + * 'typography.textDecoration', + * 'typography.textTransform', + * ] + * ); + * // {} + * ``` + * + * You can also specify nested paths as arrays: + * + * ``` + * omitStyle( + * { + * typography: { + * textDecoration: 'underline', + * textTransform: 'uppercase', + * } + * }, + * [ + * [ 'typography', 'textDecoration' ], + * [ 'typography', 'textTransform' ], + * ] + * ); + * // {} + * ``` + * + * With regards to nesting of styles, infinite depth is supported: + * + * ``` + * omitStyle( + * { + * border: { + * radius: { + * topLeft: '10px', + * topRight: '0.5rem', + * } + * } + * }, + * [ + * [ 'border', 'radius', 'topRight' ], + * ] + * ); + * // { border: { radius: { topLeft: '10px' } } } + * ``` + * + * The third argument, `preserveReference`, defines how to treat the input style object. + * It is mostly necessary to properly handle mutation when recursively handling the style object. + * Defaulting to `false`, this will always create a new object, avoiding to mutate `style`. + * However, when recursing, we change that value to `true` in order to work with a single copy + * of the original style object. + * + * @see https://lodash.com/docs/4.17.15#omit + * + * @param {Object} style Styles object. + * @param {Array|string} paths Paths to remove. + * @param {boolean} preserveReference True to mutate the `style` object, false otherwise. + * @return {Object} Styles object with the specified paths removed. + */ +function omitStyle(style, paths, preserveReference = false) { + if (!style) { + return style; + } + let newStyle = style; + if (!preserveReference) { + newStyle = JSON.parse(JSON.stringify(style)); + } + if (!Array.isArray(paths)) { + paths = [paths]; + } + paths.forEach(path => { + if (!Array.isArray(path)) { + path = path.split('.'); + } + if (path.length > 1) { + const [firstSubpath, ...restPath] = path; + omitStyle(newStyle[firstSubpath], [restPath], true); + } else if (path.length === 1) { + delete newStyle[path[0]]; + } + }); + return newStyle; +} /** - * Zero width non-breaking space, used as padding for the paragraph when it is - * empty. + * Override props assigned to save component to inject the CSS variables definition. + * + * @param {Object} props Additional props applied to save element. + * @param {Object|string} blockNameOrType Block type. + * @param {Object} attributes Block attributes. + * @param {?Record } skipPaths An object of keys and paths to skip serialization. + * + * @return {Object} Filtered props applied to save element. */ -const ZWNBSP = '\ufeff'; -function DefaultBlockAppender({ - isLocked, - onAppend, - showPrompt, - placeholder, - rootClientId +function style_addSaveProps(props, blockNameOrType, attributes, skipPaths = skipSerializationPathsSave) { + if (!hasStyleSupport(blockNameOrType)) { + return props; + } + let { + style + } = attributes; + Object.entries(skipPaths).forEach(([indicator, path]) => { + const skipSerialization = skipSerializationPathsSaveChecks[indicator] || (0,external_wp_blocks_namespaceObject.getBlockSupport)(blockNameOrType, indicator); + if (skipSerialization === true) { + style = omitStyle(style, path); + } + if (Array.isArray(skipSerialization)) { + skipSerialization.forEach(featureName => { + const feature = renamedFeatures[featureName] || featureName; + style = omitStyle(style, [[...path, feature]]); + }); + } + }); + props.style = { + ...getInlineStyles(style), + ...props.style + }; + return props; +} +function BlockStyleControls({ + clientId, + name, + setAttributes, + __unstableParentLayout }) { - if (isLocked) { + const settings = useBlockSettings(name, __unstableParentLayout); + const blockEditingMode = useBlockEditingMode(); + const passedProps = { + clientId, + name, + setAttributes, + settings + }; + if (blockEditingMode !== 'default') { return null; } - const value = (0,external_wp_htmlEntities_namespaceObject.decodeEntities)(placeholder) || (0,external_wp_i18n_namespaceObject.__)('Type / to choose a block'); - return (0,external_wp_element_namespaceObject.createElement)("div", { - "data-root-client-id": rootClientId || '', - className: classnames_default()('block-editor-default-block-appender', { - 'has-visible-prompt': showPrompt - }) - }, (0,external_wp_element_namespaceObject.createElement)("p", { - tabIndex: "0" - // We want this element to be styled as a paragraph by themes. - // eslint-disable-next-line jsx-a11y/no-noninteractive-element-to-interactive-role - , - role: "button", - "aria-label": (0,external_wp_i18n_namespaceObject.__)('Add default block') - // A wrapping container for this one already has the wp-block className. - , - className: "block-editor-default-block-appender__content", - onKeyDown: event => { - if (external_wp_keycodes_namespaceObject.ENTER === event.keyCode || external_wp_keycodes_namespaceObject.SPACE === event.keyCode) { - onAppend(); - } - }, - onClick: () => onAppend(), - onFocus: () => { - if (showPrompt) { - onAppend(); - } - } - }, showPrompt ? value : ZWNBSP), (0,external_wp_element_namespaceObject.createElement)(inserter, { - rootClientId: rootClientId, - position: "bottom right", - isAppender: true, - __experimentalIsQuick: true + return (0,external_React_.createElement)(external_React_.Fragment, null, (0,external_React_.createElement)(ColorEdit, { + ...passedProps + }), (0,external_React_.createElement)(BackgroundImagePanel, { + ...passedProps + }), (0,external_React_.createElement)(typography_TypographyPanel, { + ...passedProps + }), (0,external_React_.createElement)(border_BorderPanel, { + ...passedProps + }), (0,external_React_.createElement)(dimensions_DimensionsPanel, { + ...passedProps })); } -/* harmony default export */ var default_block_appender = ((0,external_wp_compose_namespaceObject.compose)((0,external_wp_data_namespaceObject.withSelect)((select, ownProps) => { - const { - getBlockCount, - getSettings, - getTemplateLock - } = select(store); - const isEmpty = !getBlockCount(ownProps.rootClientId); - const { - bodyPlaceholder - } = getSettings(); - return { - showPrompt: isEmpty, - isLocked: !!getTemplateLock(ownProps.rootClientId), - placeholder: bodyPlaceholder - }; -}), (0,external_wp_data_namespaceObject.withDispatch)((dispatch, ownProps) => { - const { - insertDefaultBlock, - startTyping - } = dispatch(store); - return { - onAppend() { - const { - rootClientId - } = ownProps; - insertDefaultBlock(undefined, rootClientId); - startTyping(); +/* harmony default export */ const style = ({ + edit: BlockStyleControls, + hasSupport: hasStyleSupport, + addSaveProps: style_addSaveProps, + attributeKeys: ['style'], + useBlockProps: style_useBlockProps +}); + +// Defines which element types are supported, including their hover styles or +// any other elements that have been included under a single element type +// e.g. heading and h1-h6. +const elementTypes = [{ + elementType: 'button' +}, { + elementType: 'link', + pseudo: [':hover'] +}, { + elementType: 'heading', + elements: ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'] +}]; +function style_useBlockProps({ + name, + style +}) { + const blockElementsContainerIdentifier = `wp-elements-${(0,external_wp_compose_namespaceObject.useInstanceId)(style_useBlockProps)}`; + + // The .editor-styles-wrapper selector is required on elements styles. As it is + // added to all other editor styles, not providing it causes reset and global + // styles to override element styles because of higher specificity. + const baseElementSelector = `.editor-styles-wrapper .${blockElementsContainerIdentifier}`; + const blockElementStyles = style?.elements; + const styles = (0,external_wp_element_namespaceObject.useMemo)(() => { + if (!blockElementStyles) { + return; } - }; -}))(DefaultBlockAppender)); + const elementCSSRules = []; + elementTypes.forEach(({ + elementType, + pseudo, + elements + }) => { + const skipSerialization = shouldSkipSerialization(name, COLOR_SUPPORT_KEY, elementType); + if (skipSerialization) { + return; + } + const elementStyles = blockElementStyles?.[elementType]; -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/button-block-appender/index.js + // Process primary element type styles. + if (elementStyles) { + const selector = scopeSelector(baseElementSelector, external_wp_blocks_namespaceObject.__EXPERIMENTAL_ELEMENTS[elementType]); + elementCSSRules.push((0,external_wp_styleEngine_namespaceObject.compileCSS)(elementStyles, { + selector + })); -/** - * External dependencies - */ + // Process any interactive states for the element type. + if (pseudo) { + pseudo.forEach(pseudoSelector => { + if (elementStyles[pseudoSelector]) { + elementCSSRules.push((0,external_wp_styleEngine_namespaceObject.compileCSS)(elementStyles[pseudoSelector], { + selector: scopeSelector(baseElementSelector, `${external_wp_blocks_namespaceObject.__EXPERIMENTAL_ELEMENTS[elementType]}${pseudoSelector}`) + })); + } + }); + } + } + // Process related elements e.g. h1-h6 for headings + if (elements) { + elements.forEach(element => { + if (blockElementStyles[element]) { + elementCSSRules.push((0,external_wp_styleEngine_namespaceObject.compileCSS)(blockElementStyles[element], { + selector: scopeSelector(baseElementSelector, external_wp_blocks_namespaceObject.__EXPERIMENTAL_ELEMENTS[element]) + })); + } + }); + } + }); + return elementCSSRules.length > 0 ? elementCSSRules.join('') : undefined; + }, [baseElementSelector, blockElementStyles, name]); + useStyleOverride({ + css: styles + }); + return style_addSaveProps({ + className: blockElementsContainerIdentifier + }, name, { + style + }, skipSerializationPathsEdit); +} +(0,external_wp_hooks_namespaceObject.addFilter)('blocks.registerBlockType', 'core/style/addAttribute', style_addAttribute); +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/hooks/settings.js /** * WordPress dependencies */ +const hasSettingsSupport = blockType => (0,external_wp_blocks_namespaceObject.hasBlockSupport)(blockType, '__experimentalSettings', false); +function settings_addAttribute(settings) { + if (!hasSettingsSupport(settings)) { + return settings; + } - - - -/** - * Internal dependencies - */ - -function ButtonBlockAppender({ - rootClientId, - className, - onFocus, - tabIndex -}, ref) { - return (0,external_wp_element_namespaceObject.createElement)(inserter, { - position: "bottom center", - rootClientId: rootClientId, - __experimentalIsQuick: true, - renderToggle: ({ - onToggle, - disabled, - isOpen, - blockTitle, - hasSingleBlockType - }) => { - let label; - if (hasSingleBlockType) { - label = (0,external_wp_i18n_namespaceObject.sprintf)( - // translators: %s: the name of the block when there is only one - (0,external_wp_i18n_namespaceObject._x)('Add %s', 'directly add the only allowed block'), blockTitle); - } else { - label = (0,external_wp_i18n_namespaceObject._x)('Add block', 'Generic label for block inserter button'); - } - const isToggleButton = !hasSingleBlockType; - let inserterButton = (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.Button, { - ref: ref, - onFocus: onFocus, - tabIndex: tabIndex, - className: classnames_default()(className, 'block-editor-button-block-appender'), - onClick: onToggle, - "aria-haspopup": isToggleButton ? 'true' : undefined, - "aria-expanded": isToggleButton ? isOpen : undefined, - disabled: disabled, - label: label - }, !hasSingleBlockType && (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.VisuallyHidden, { - as: "span" - }, label), (0,external_wp_element_namespaceObject.createElement)(build_module_icon, { - icon: library_plus - })); - if (isToggleButton || hasSingleBlockType) { - inserterButton = (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.Tooltip, { - text: label - }, inserterButton); + // Allow blocks to specify their own attribute definition with default values if needed. + if (!settings?.attributes?.settings) { + settings.attributes = { + ...settings.attributes, + settings: { + type: 'object' } - return inserterButton; - }, - isAppender: true - }); + }; + } + return settings; } +(0,external_wp_hooks_namespaceObject.addFilter)('blocks.registerBlockType', 'core/settings/addAttribute', settings_addAttribute); -/** - * Use `ButtonBlockAppender` instead. - * - * @deprecated - */ -const ButtonBlockerAppender = (0,external_wp_element_namespaceObject.forwardRef)((props, ref) => { - external_wp_deprecated_default()(`wp.blockEditor.ButtonBlockerAppender`, { - alternative: 'wp.blockEditor.ButtonBlockAppender', - since: '5.9' - }); - return ButtonBlockAppender(props, ref); -}); +;// CONCATENATED MODULE: ./node_modules/@wordpress/icons/build-module/library/filter.js /** - * @see https://github.com/WordPress/gutenberg/blob/HEAD/packages/block-editor/src/components/button-block-appender/README.md + * WordPress dependencies */ -/* harmony default export */ var button_block_appender = ((0,external_wp_element_namespaceObject.forwardRef)(ButtonBlockAppender)); -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/block-list-appender/index.js - -/** - * External dependencies - */ +const filter = (0,external_React_.createElement)(external_wp_primitives_namespaceObject.SVG, { + xmlns: "http://www.w3.org/2000/svg", + viewBox: "0 0 24 24" +}, (0,external_React_.createElement)(external_wp_primitives_namespaceObject.Path, { + d: "M12 4 4 19h16L12 4zm0 3.2 5.5 10.3H12V7.2z" +})); +/* harmony default export */ const library_filter = (filter); +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/duotone-control/index.js /** * WordPress dependencies @@ -26259,462 +31434,284 @@ const ButtonBlockerAppender = (0,external_wp_element_namespaceObject.forwardRef) -/** - * Internal dependencies - */ - -function DefaultAppender({ - rootClientId +function DuotoneControl({ + id: idProp, + colorPalette, + duotonePalette, + disableCustomColors, + disableCustomDuotone, + value, + onChange }) { - const canInsertDefaultBlock = (0,external_wp_data_namespaceObject.useSelect)(select => select(store).canInsertBlockType((0,external_wp_blocks_namespaceObject.getDefaultBlockName)(), rootClientId)); - if (canInsertDefaultBlock) { - // Render the default block appender if the context supports use - // of the default appender. - return (0,external_wp_element_namespaceObject.createElement)(default_block_appender, { - rootClientId: rootClientId + let toolbarIcon; + if (value === 'unset') { + toolbarIcon = (0,external_React_.createElement)(external_wp_components_namespaceObject.ColorIndicator, { + className: "block-editor-duotone-control__unset-indicator" + }); + } else if (value) { + toolbarIcon = (0,external_React_.createElement)(external_wp_components_namespaceObject.DuotoneSwatch, { + values: value + }); + } else { + toolbarIcon = (0,external_React_.createElement)(build_module_icon, { + icon: library_filter }); } - - // Fallback in case the default block can't be inserted. - return (0,external_wp_element_namespaceObject.createElement)(button_block_appender, { - rootClientId: rootClientId, - className: "block-list-appender__toggle" - }); -} -function useAppender(rootClientId, CustomAppender) { - const isVisible = (0,external_wp_data_namespaceObject.useSelect)(select => { - const { - getTemplateLock, - getSelectedBlockClientId, - __unstableGetEditorMode, - getBlockEditingMode - } = select(store); - if (CustomAppender === false) { - return false; - } - if (!CustomAppender) { - const selectedBlockClientId = getSelectedBlockClientId(); - const isParentSelected = rootClientId === selectedBlockClientId || !rootClientId && !selectedBlockClientId; - if (!isParentSelected) { - return false; - } - } - if (getTemplateLock(rootClientId) || getBlockEditingMode(rootClientId) === 'disabled' || __unstableGetEditorMode() === 'zoom-out') { - return false; - } - return true; - }, [rootClientId, CustomAppender]); - if (!isVisible) { - return null; - } - return CustomAppender ? (0,external_wp_element_namespaceObject.createElement)(CustomAppender, null) : (0,external_wp_element_namespaceObject.createElement)(DefaultAppender, { - rootClientId: rootClientId - }); -} -function BlockListAppender({ - rootClientId, - renderAppender, - className, - tagName: TagName = 'div' -}) { - const appender = useAppender(rootClientId, renderAppender); - const isDragOver = (0,external_wp_data_namespaceObject.useSelect)(select => { - const { - getBlockInsertionPoint, - isBlockInsertionPointVisible, - getBlockCount - } = select(store); - const insertionPoint = getBlockInsertionPoint(); - // Ideally we should also check for `isDragging` but currently it - // requires a lot more setup. We can revisit this once we refactor - // the DnD utility hooks. - return isBlockInsertionPointVisible() && rootClientId === insertionPoint?.rootClientId && getBlockCount(rootClientId) === 0; - }, [rootClientId]); - if (!appender) { - return null; - } - return (0,external_wp_element_namespaceObject.createElement)(TagName - // A `tabIndex` is used on the wrapping `div` element in order to - // force a focus event to occur when an appender `button` element - // is clicked. In some browsers (Firefox, Safari), button clicks do - // not emit a focus event, which could cause this event to propagate - // unexpectedly. The `tabIndex` ensures that the interaction is - // captured as a focus, without also adding an extra tab stop. - // - // See: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#Clicking_and_focus - , { - tabIndex: -1, - className: classnames_default()('block-list-appender wp-block', className, { - 'is-drag-over': isDragOver - }) - // Needed in case the whole editor is content editable (for multi - // selection). It fixes an edge case where ArrowDown and ArrowRight - // should collapse the selection to the end of that selection and - // not into the appender. - , - contentEditable: false - // The appender exists to let you add the first Paragraph before - // any is inserted. To that end, this appender should visually be - // presented as a block. That means theme CSS should style it as if - // it were an empty paragraph block. That means a `wp-block` class to - // ensure the width is correct, and a [data-block] attribute to ensure - // the correct margin is applied, especially for classic themes which - // have commonly targeted that attribute for margins. - , - "data-block": true - }, appender); + const actionLabel = (0,external_wp_i18n_namespaceObject.__)('Apply duotone filter'); + const id = (0,external_wp_compose_namespaceObject.useInstanceId)(DuotoneControl, 'duotone-control', idProp); + const descriptionId = `${id}__description`; + return (0,external_React_.createElement)(external_wp_components_namespaceObject.Dropdown, { + popoverProps: { + className: 'block-editor-duotone-control__popover', + headerTitle: (0,external_wp_i18n_namespaceObject.__)('Duotone') + }, + renderToggle: ({ + isOpen, + onToggle + }) => { + const openOnArrowDown = event => { + if (!isOpen && event.keyCode === external_wp_keycodes_namespaceObject.DOWN) { + event.preventDefault(); + onToggle(); + } + }; + return (0,external_React_.createElement)(external_wp_components_namespaceObject.ToolbarButton, { + showTooltip: true, + onClick: onToggle, + "aria-haspopup": "true", + "aria-expanded": isOpen, + onKeyDown: openOnArrowDown, + label: actionLabel, + icon: toolbarIcon + }); + }, + renderContent: () => (0,external_React_.createElement)(external_wp_components_namespaceObject.MenuGroup, { + label: (0,external_wp_i18n_namespaceObject.__)('Duotone') + }, (0,external_React_.createElement)("p", null, (0,external_wp_i18n_namespaceObject.__)('Create a two-tone color effect without losing your original image.')), (0,external_React_.createElement)(external_wp_components_namespaceObject.DuotonePicker, { + "aria-label": actionLabel, + "aria-describedby": descriptionId, + colorPalette: colorPalette, + duotonePalette: duotonePalette, + disableCustomColors: disableCustomColors, + disableCustomDuotone: disableCustomDuotone, + value: value, + onChange: onChange + })) + }); } -/* harmony default export */ var block_list_appender = (BlockListAppender); +/* harmony default export */ const duotone_control = (DuotoneControl); -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/block-popover/use-popover-scroll.js +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/duotone/utils.js /** - * WordPress dependencies + * External dependencies */ /** - * Allow scrolling "through" popovers over the canvas. This is only called for - * as long as the pointer is over a popover. Do not use React events because it - * will bubble through portals. + * Convert a list of colors to an object of R, G, and B values. * - * @param {Object} scrollableRef + * @param {string[]} colors Array of RBG color strings. + * + * @return {Object} R, G, and B values. */ -function usePopoverScroll(scrollableRef) { - return (0,external_wp_compose_namespaceObject.useRefEffect)(node => { - if (!scrollableRef) { - return; - } - function onWheel(event) { - const { - deltaX, - deltaY - } = event; - scrollableRef.current.scrollBy(deltaX, deltaY); - } - // Tell the browser that we do not call event.preventDefault - // See https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#improving_scrolling_performance_with_passive_listeners - const options = { - passive: true - }; - node.addEventListener('wheel', onWheel, options); - return () => { - node.removeEventListener('wheel', onWheel, options); - }; - }, [scrollableRef]); +function getValuesFromColors(colors = []) { + const values = { + r: [], + g: [], + b: [], + a: [] + }; + colors.forEach(color => { + const rgbColor = w(color).toRgb(); + values.r.push(rgbColor.r / 255); + values.g.push(rgbColor.g / 255); + values.b.push(rgbColor.b / 255); + values.a.push(rgbColor.a); + }); + return values; } -/* harmony default export */ var use_popover_scroll = (usePopoverScroll); - -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/block-popover/inbetween.js /** - * External dependencies + * Stylesheet for disabling a global styles duotone filter. + * + * @param {string} selector Selector to disable the filter for. + * + * @return {string} Filter none style. */ - +function getDuotoneUnsetStylesheet(selector) { + return `${selector}{filter:none}`; +} /** - * WordPress dependencies + * SVG and stylesheet needed for rendering the duotone filter. + * + * @param {string} selector Selector to apply the filter to. + * @param {string} id Unique id for this duotone filter. + * + * @return {string} Duotone filter style. */ +function getDuotoneStylesheet(selector, id) { + return `${selector}{filter:url(#${id})}`; +} +/** + * The SVG part of the duotone filter. + * + * @param {string} id Unique id for this duotone filter. + * @param {string[]} colors Color strings from dark to light. + * + * @return {string} Duotone SVG. + */ +function getDuotoneFilter(id, colors) { + const values = getValuesFromColors(colors); + return ` + `; +} - - - +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/global-styles/get-block-css-selector.js /** * Internal dependencies */ -const MAX_POPOVER_RECOMPUTE_COUNTER = Number.MAX_SAFE_INTEGER; -const InsertionPointOpenRef = (0,external_wp_element_namespaceObject.createContext)(); -function BlockPopoverInbetween({ - previousClientId, - nextClientId, - children, - __unstablePopoverSlot, - __unstableContentRef, - ...props -}) { - // This is a temporary hack to get the inbetween inserter to recompute properly. - const [popoverRecomputeCounter, forcePopoverRecompute] = (0,external_wp_element_namespaceObject.useReducer)( - // Module is there to make sure that the counter doesn't overflow. - s => (s + 1) % MAX_POPOVER_RECOMPUTE_COUNTER, 0); - const { - orientation, - rootClientId, - isVisible - } = (0,external_wp_data_namespaceObject.useSelect)(select => { - const { - getBlockListSettings, - getBlockRootClientId, - isBlockVisible - } = select(store); - const _rootClientId = getBlockRootClientId(previousClientId !== null && previousClientId !== void 0 ? previousClientId : nextClientId); - return { - orientation: getBlockListSettings(_rootClientId)?.orientation || 'vertical', - rootClientId: _rootClientId, - isVisible: isBlockVisible(previousClientId) && isBlockVisible(nextClientId) - }; - }, [previousClientId, nextClientId]); - const previousElement = useBlockElement(previousClientId); - const nextElement = useBlockElement(nextClientId); - const isVertical = orientation === 'vertical'; - const popoverAnchor = (0,external_wp_element_namespaceObject.useMemo)(() => { - if ( - // popoverRecomputeCounter is by definition always equal or greater than 0. - // This check is only there to satisfy the correctness of the - // exhaustive-deps rule for the `useMemo` hook. - popoverRecomputeCounter < 0 || !previousElement && !nextElement || !isVisible) { - return undefined; - } - const contextElement = previousElement || nextElement; - return { - contextElement, - getBoundingClientRect() { - const previousRect = previousElement ? previousElement.getBoundingClientRect() : null; - const nextRect = nextElement ? nextElement.getBoundingClientRect() : null; - let left = 0; - let top = 0; - let width = 0; - let height = 0; - if (isVertical) { - // vertical - top = previousRect ? previousRect.bottom : nextRect.top; - width = previousRect ? previousRect.width : nextRect.width; - height = nextRect && previousRect ? nextRect.top - previousRect.bottom : 0; - left = previousRect ? previousRect.left : nextRect.left; - } else { - top = previousRect ? previousRect.top : nextRect.top; - height = previousRect ? previousRect.height : nextRect.height; - if ((0,external_wp_i18n_namespaceObject.isRTL)()) { - // non vertical, rtl - left = nextRect ? nextRect.right : previousRect.left; - width = previousRect && nextRect ? previousRect.left - nextRect.right : 0; - } else { - // non vertical, ltr - left = previousRect ? previousRect.right : nextRect.left; - width = previousRect && nextRect ? nextRect.left - previousRect.right : 0; - } - } - return new window.DOMRect(left, top, width, height); - } - }; - }, [previousElement, nextElement, popoverRecomputeCounter, isVertical, isVisible]); - const popoverScrollRef = use_popover_scroll(__unstableContentRef); - - // This is only needed for a smooth transition when moving blocks. - // When blocks are moved up/down, their position can be set by - // updating the `transform` property manually (i.e. without using CSS - // transitions or animations). The animation, which can also scroll the block - // editor, can sometimes cause the position of the Popover to get out of sync. - // A MutationObserver is therefore used to make sure that changes to the - // selectedElement's attribute (i.e. `transform`) can be tracked and used to - // trigger the Popover to rerender. - (0,external_wp_element_namespaceObject.useLayoutEffect)(() => { - if (!previousElement) { - return; - } - const observer = new window.MutationObserver(forcePopoverRecompute); - observer.observe(previousElement, { - attributes: true - }); - return () => { - observer.disconnect(); - }; - }, [previousElement]); - (0,external_wp_element_namespaceObject.useLayoutEffect)(() => { - if (!nextElement) { - return; - } - const observer = new window.MutationObserver(forcePopoverRecompute); - observer.observe(nextElement, { - attributes: true - }); - return () => { - observer.disconnect(); - }; - }, [nextElement]); - (0,external_wp_element_namespaceObject.useLayoutEffect)(() => { - if (!previousElement) { - return; - } - previousElement.ownerDocument.defaultView.addEventListener('resize', forcePopoverRecompute); - return () => { - previousElement.ownerDocument.defaultView?.removeEventListener('resize', forcePopoverRecompute); - }; - }, [previousElement]); - - // If there's either a previous or a next element, show the inbetween popover. - // Note that drag and drop uses the inbetween popover to show the drop indicator - // before the first block and after the last block. - if (!previousElement && !nextElement || !isVisible) { +/** + * Determine the CSS selector for the block type and target provided, returning + * it if available. + * + * @param {import('@wordpress/blocks').Block} blockType The block's type. + * @param {string|string[]} target The desired selector's target e.g. `root`, delimited string, or array path. + * @param {Object} options Options object. + * @param {boolean} options.fallback Whether or not to fallback to broader selector. + * + * @return {?string} The CSS selector or `null` if no selector available. + */ +function getBlockCSSSelector(blockType, target = 'root', options = {}) { + if (!target) { return null; } + const { + fallback = false + } = options; + const { + name, + selectors, + supports + } = blockType; + const hasSelectors = selectors && Object.keys(selectors).length > 0; + const path = Array.isArray(target) ? target.join('.') : target; - /* eslint-disable jsx-a11y/no-static-element-interactions, jsx-a11y/click-events-have-key-events */ - // While ideally it would be enough to capture the - // bubbling focus event from the Inserter, due to the - // characteristics of click focusing of `button`s in - // Firefox and Safari, it is not reliable. - // - // See: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#Clicking_and_focus - return (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.Popover, { - ref: popoverScrollRef, - animate: false, - anchor: popoverAnchor, - focusOnMount: false - // Render in the old slot if needed for backward compatibility, - // otherwise render in place (not in the default popover slot). - , - __unstableSlotName: __unstablePopoverSlot, - inline: !__unstablePopoverSlot - // Forces a remount of the popover when its position changes - // This makes sure the popover doesn't animate from its previous position. - , - key: nextClientId + '--' + rootClientId, - ...props, - className: classnames_default()('block-editor-block-popover', 'block-editor-block-popover__inbetween', props.className), - resize: false, - flip: false, - placement: "overlay", - variant: "unstyled" - }, (0,external_wp_element_namespaceObject.createElement)("div", { - className: "block-editor-block-popover__inbetween-container" - }, children)); - /* eslint-enable jsx-a11y/no-static-element-interactions, jsx-a11y/click-events-have-key-events */ -} + // Root selector. -/* harmony default export */ var inbetween = (BlockPopoverInbetween); + // Calculated before returning as it can be used as a fallback for feature + // selectors later on. + let rootSelector = null; + if (hasSelectors && selectors.root) { + // Use the selectors API if available. + rootSelector = selectors?.root; + } else if (supports?.__experimentalSelector) { + // Use the old experimental selector supports property if set. + rootSelector = supports.__experimentalSelector; + } else { + // If no root selector found, generate default block class selector. + rootSelector = '.wp-block-' + name.replace('core/', '').replace('/', '-'); + } -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/block-popover/index.js + // Return selector if it's the root target we are looking for. + if (path === 'root') { + return rootSelector; + } -/** - * External dependencies - */ + // If target is not `root` or `duotone` we have a feature or subfeature + // as the target. If the target is a string convert to an array. + const pathArray = Array.isArray(target) ? target : target.split('.'); + // Feature selectors ( may fallback to root selector ); + if (pathArray.length === 1) { + const fallbackSelector = fallback ? rootSelector : null; -/** - * WordPress dependencies - */ + // Prefer the selectors API if available. + if (hasSelectors) { + // Get selector from either `feature.root` or shorthand path. + const featureSelector = getValueFromObjectPath(selectors, `${path}.root`, null) || getValueFromObjectPath(selectors, path, null); + // Return feature selector if found or any available fallback. + return featureSelector || fallbackSelector; + } + // Try getting old experimental supports selector value. + const featureSelector = getValueFromObjectPath(supports, `${path}.__experimentalSelector`, null); + // If nothing to work with, provide fallback selector if available. + if (!featureSelector) { + return fallbackSelector; + } -/** - * Internal dependencies - */ + // Scope the feature selector by the block's root selector. + return scopeSelector(rootSelector, featureSelector); + } + // Subfeature selector. + // This may fallback either to parent feature or root selector. + let subfeatureSelector; -const block_popover_MAX_POPOVER_RECOMPUTE_COUNTER = Number.MAX_SAFE_INTEGER; -function BlockPopover({ - clientId, - bottomClientId, - children, - __unstableRefreshSize, - __unstableCoverTarget = false, - __unstablePopoverSlot, - __unstableContentRef, - shift = true, - ...props -}, ref) { - const selectedElement = useBlockElement(clientId); - const lastSelectedElement = useBlockElement(bottomClientId !== null && bottomClientId !== void 0 ? bottomClientId : clientId); - const mergedRefs = (0,external_wp_compose_namespaceObject.useMergeRefs)([ref, use_popover_scroll(__unstableContentRef)]); - const [popoverDimensionsRecomputeCounter, forceRecomputePopoverDimensions] = (0,external_wp_element_namespaceObject.useReducer)( - // Module is there to make sure that the counter doesn't overflow. - s => (s + 1) % block_popover_MAX_POPOVER_RECOMPUTE_COUNTER, 0); + // Use selectors API if available. + if (hasSelectors) { + subfeatureSelector = getValueFromObjectPath(selectors, path, null); + } - // When blocks are moved up/down, they are animated to their new position by - // updating the `transform` property manually (i.e. without using CSS - // transitions or animations). The animation, which can also scroll the block - // editor, can sometimes cause the position of the Popover to get out of sync. - // A MutationObserver is therefore used to make sure that changes to the - // selectedElement's attribute (i.e. `transform`) can be tracked and used to - // trigger the Popover to rerender. - (0,external_wp_element_namespaceObject.useLayoutEffect)(() => { - if (!selectedElement) { - return; - } - const observer = new window.MutationObserver(forceRecomputePopoverDimensions); - observer.observe(selectedElement, { - attributes: true - }); - return () => { - observer.disconnect(); - }; - }, [selectedElement]); - const style = (0,external_wp_element_namespaceObject.useMemo)(() => { - if ( - // popoverDimensionsRecomputeCounter is by definition always equal or greater - // than 0. This check is only there to satisfy the correctness of the - // exhaustive-deps rule for the `useMemo` hook. - popoverDimensionsRecomputeCounter < 0 || !selectedElement || lastSelectedElement !== selectedElement) { - return {}; - } - return { - position: 'absolute', - width: selectedElement.offsetWidth, - height: selectedElement.offsetHeight - }; - }, [selectedElement, lastSelectedElement, __unstableRefreshSize, popoverDimensionsRecomputeCounter]); - const popoverAnchor = (0,external_wp_element_namespaceObject.useMemo)(() => { - if ( - // popoverDimensionsRecomputeCounter is by definition always equal or greater - // than 0. This check is only there to satisfy the correctness of the - // exhaustive-deps rule for the `useMemo` hook. - popoverDimensionsRecomputeCounter < 0 || !selectedElement || bottomClientId && !lastSelectedElement) { - return undefined; - } - return { - getBoundingClientRect() { - var _lastSelectedBCR$left, _lastSelectedBCR$top, _lastSelectedBCR$righ, _lastSelectedBCR$bott; - const selectedBCR = selectedElement.getBoundingClientRect(); - const lastSelectedBCR = lastSelectedElement?.getBoundingClientRect(); + // Only return if we have a subfeature selector. + if (subfeatureSelector) { + return subfeatureSelector; + } - // Get the biggest rectangle that encompasses completely the currently - // selected element and the last selected element: - // - for top/left coordinates, use the smaller numbers - // - for the bottom/right coordinates, use the largest numbers - const left = Math.min(selectedBCR.left, (_lastSelectedBCR$left = lastSelectedBCR?.left) !== null && _lastSelectedBCR$left !== void 0 ? _lastSelectedBCR$left : Infinity); - const top = Math.min(selectedBCR.top, (_lastSelectedBCR$top = lastSelectedBCR?.top) !== null && _lastSelectedBCR$top !== void 0 ? _lastSelectedBCR$top : Infinity); - const right = Math.max(selectedBCR.right, (_lastSelectedBCR$righ = lastSelectedBCR.right) !== null && _lastSelectedBCR$righ !== void 0 ? _lastSelectedBCR$righ : -Infinity); - const bottom = Math.max(selectedBCR.bottom, (_lastSelectedBCR$bott = lastSelectedBCR.bottom) !== null && _lastSelectedBCR$bott !== void 0 ? _lastSelectedBCR$bott : -Infinity); - const width = right - left; - const height = bottom - top; - return new window.DOMRect(left, top, width, height); - }, - contextElement: selectedElement - }; - }, [bottomClientId, lastSelectedElement, selectedElement, popoverDimensionsRecomputeCounter]); - if (!selectedElement || bottomClientId && !lastSelectedElement) { - return null; + // To this point we don't have a subfeature selector. If a fallback has been + // requested, remove subfeature from target path and return results of a + // call for the parent feature's selector. + if (fallback) { + return getBlockCSSSelector(blockType, pathArray[0], options); } - return (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.Popover, { - ref: mergedRefs, - animate: false, - focusOnMount: false, - anchor: popoverAnchor - // Render in the old slot if needed for backward compatibility, - // otherwise render in place (not in the default popover slot). - , - __unstableSlotName: __unstablePopoverSlot, - inline: !__unstablePopoverSlot, - placement: "top-start", - resize: false, - flip: false, - shift: shift, - ...props, - className: classnames_default()('block-editor-block-popover', props.className), - variant: "unstyled" - }, __unstableCoverTarget && (0,external_wp_element_namespaceObject.createElement)("div", { - style: style - }, children), !__unstableCoverTarget && children); + + // We tried. + return null; } -/* harmony default export */ var block_popover = ((0,external_wp_element_namespaceObject.forwardRef)(BlockPopover)); -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/block-popover/drop-zone.js +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/global-styles/filters-panel.js + +/** + * External dependencies + */ + /** * WordPress dependencies @@ -26728,64 +31725,172 @@ function BlockPopover({ */ -const animateVariants = { - hide: { - opacity: 0, - scaleY: 0.75 - }, - show: { - opacity: 1, - scaleY: 1 - }, - exit: { - opacity: 0, - scaleY: 0.9 - } +const filters_panel_EMPTY_ARRAY = []; +function useMultiOriginColorPresets(settings, { + presetSetting, + defaultSetting +}) { + const disableDefault = !settings?.color?.[defaultSetting]; + const userPresets = settings?.color?.[presetSetting]?.custom || filters_panel_EMPTY_ARRAY; + const themePresets = settings?.color?.[presetSetting]?.theme || filters_panel_EMPTY_ARRAY; + const defaultPresets = settings?.color?.[presetSetting]?.default || filters_panel_EMPTY_ARRAY; + return (0,external_wp_element_namespaceObject.useMemo)(() => [...userPresets, ...themePresets, ...(disableDefault ? filters_panel_EMPTY_ARRAY : defaultPresets)], [disableDefault, userPresets, themePresets, defaultPresets]); +} +function useHasFiltersPanel(settings) { + return useHasDuotoneControl(settings); +} +function useHasDuotoneControl(settings) { + return settings.color.customDuotone || settings.color.defaultDuotone || settings.color.duotone.length > 0; +} +function FiltersToolsPanel({ + resetAllFilter, + onChange, + value, + panelId, + children +}) { + const resetAll = () => { + const updatedValue = resetAllFilter(value); + onChange(updatedValue); + }; + return (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalToolsPanel, { + label: (0,external_wp_i18n_namespaceObject._x)('Filters', 'Name for applying graphical effects'), + resetAll: resetAll, + panelId: panelId, + dropdownMenuProps: TOOLSPANEL_DROPDOWNMENU_PROPS + }, children); +} +const filters_panel_DEFAULT_CONTROLS = { + duotone: true }; -function BlockDropZonePopover({ - __unstablePopoverSlot, - __unstableContentRef +const filters_panel_popoverProps = { + placement: 'left-start', + offset: 36, + shift: true, + className: 'block-editor-duotone-control__popover', + headerTitle: (0,external_wp_i18n_namespaceObject.__)('Duotone') +}; +const LabeledColorIndicator = ({ + indicator, + label +}) => (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalHStack, { + justify: "flex-start" +}, (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalZStack, { + isLayered: false, + offset: -8 +}, (0,external_React_.createElement)(external_wp_components_namespaceObject.Flex, { + expanded: false +}, indicator === 'unset' || !indicator ? (0,external_React_.createElement)(external_wp_components_namespaceObject.ColorIndicator, { + className: "block-editor-duotone-control__unset-indicator" +}) : (0,external_React_.createElement)(external_wp_components_namespaceObject.DuotoneSwatch, { + values: indicator +}))), (0,external_React_.createElement)(external_wp_components_namespaceObject.FlexItem, { + title: label +}, label)); +function FiltersPanel({ + as: Wrapper = FiltersToolsPanel, + value, + onChange, + inheritedValue = value, + settings, + panelId, + defaultControls = filters_panel_DEFAULT_CONTROLS }) { - const { - clientId - } = (0,external_wp_data_namespaceObject.useSelect)(select => { - const { - getBlockOrder, - getBlockInsertionPoint - } = select(store); - const insertionPoint = getBlockInsertionPoint(); - const order = getBlockOrder(insertionPoint.rootClientId); - if (!order.length) { - return {}; - } + const decodeValue = rawValue => getValueFromVariable({ + settings + }, '', rawValue); + + // Duotone + const hasDuotoneEnabled = useHasDuotoneControl(settings); + const duotonePalette = useMultiOriginColorPresets(settings, { + presetSetting: 'duotone', + defaultSetting: 'defaultDuotone' + }); + const colorPalette = useMultiOriginColorPresets(settings, { + presetSetting: 'palette', + defaultSetting: 'defaultPalette' + }); + const duotone = decodeValue(inheritedValue?.filter?.duotone); + const setDuotone = newValue => { + const duotonePreset = duotonePalette.find(({ + colors + }) => { + return colors === newValue; + }); + const settedValue = duotonePreset ? `var:preset|duotone|${duotonePreset.slug}` : newValue; + onChange(setImmutably(value, ['filter', 'duotone'], settedValue)); + }; + const hasDuotone = () => !!value?.filter?.duotone; + const resetDuotone = () => setDuotone(undefined); + const resetAllFilter = (0,external_wp_element_namespaceObject.useCallback)(previousValue => { return { - clientId: order[insertionPoint.index] + ...previousValue, + filter: { + ...previousValue.filter, + duotone: undefined + } }; }, []); - const reducedMotion = (0,external_wp_compose_namespaceObject.useReducedMotion)(); - return (0,external_wp_element_namespaceObject.createElement)(block_popover, { - clientId: clientId, - __unstableCoverTarget: true, - __unstablePopoverSlot: __unstablePopoverSlot, - __unstableContentRef: __unstableContentRef, - className: "block-editor-block-popover__drop-zone" - }, (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.__unstableMotion.div, { - "data-testid": "block-popover-drop-zone", - initial: reducedMotion ? animateVariants.show : animateVariants.hide, - animate: animateVariants.show, - exit: reducedMotion ? animateVariants.show : animateVariants.exit, - className: "block-editor-block-popover__drop-zone-foreground" - })); + return (0,external_React_.createElement)(Wrapper, { + resetAllFilter: resetAllFilter, + value: value, + onChange: onChange, + panelId: panelId + }, hasDuotoneEnabled && (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalToolsPanelItem, { + label: (0,external_wp_i18n_namespaceObject.__)('Duotone'), + hasValue: hasDuotone, + onDeselect: resetDuotone, + isShownByDefault: defaultControls.duotone, + panelId: panelId + }, (0,external_React_.createElement)(external_wp_components_namespaceObject.Dropdown, { + popoverProps: filters_panel_popoverProps, + className: "block-editor-global-styles-filters-panel__dropdown", + renderToggle: ({ + onToggle, + isOpen + }) => { + const toggleProps = { + onClick: onToggle, + className: classnames_default()({ + 'is-open': isOpen + }), + 'aria-expanded': isOpen + }; + return (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalItemGroup, { + isBordered: true, + isSeparated: true + }, (0,external_React_.createElement)(external_wp_components_namespaceObject.Button, { + ...toggleProps + }, (0,external_React_.createElement)(LabeledColorIndicator, { + indicator: duotone, + label: (0,external_wp_i18n_namespaceObject.__)('Duotone') + }))); + }, + renderContent: () => (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalDropdownContentWrapper, { + paddingSize: "small" + }, (0,external_React_.createElement)(external_wp_components_namespaceObject.MenuGroup, { + label: (0,external_wp_i18n_namespaceObject.__)('Duotone') + }, (0,external_React_.createElement)("p", null, (0,external_wp_i18n_namespaceObject.__)('Create a two-tone color effect without losing your original image.')), (0,external_React_.createElement)(external_wp_components_namespaceObject.DuotonePicker, { + colorPalette: colorPalette, + duotonePalette: duotonePalette + // TODO: Re-enable both when custom colors are supported for block-level styles. + , + disableCustomColors: true, + disableCustomDuotone: true, + value: duotone, + onChange: setDuotone + }))) + }))); } -/* harmony default export */ var drop_zone = (BlockDropZonePopover); -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/block-tools/insertion-point.js +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/hooks/duotone.js /** * External dependencies */ + /** * WordPress dependencies */ @@ -26801,201 +31906,277 @@ function BlockDropZonePopover({ -const insertion_point_InsertionPointOpenRef = (0,external_wp_element_namespaceObject.createContext)(); -function InbetweenInsertionPointPopover({ - __unstablePopoverSlot, - __unstableContentRef + + + + +const duotone_EMPTY_ARRAY = []; + +// Safari does not always update the duotone filter when the duotone colors +// are changed. This browser check is later used to force a re-render of the block +// element to ensure the duotone filter is updated. The check is included at the +// root of this file as it only needs to be run once per page load. +const isSafari = window?.navigator.userAgent && window.navigator.userAgent.includes('Safari') && !window.navigator.userAgent.includes('Chrome') && !window.navigator.userAgent.includes('Chromium'); +k([names]); +function useMultiOriginPresets({ + presetSetting, + defaultSetting }) { - const { - selectBlock, - hideInsertionPoint - } = (0,external_wp_data_namespaceObject.useDispatch)(store); - const openRef = (0,external_wp_element_namespaceObject.useContext)(insertion_point_InsertionPointOpenRef); - const ref = (0,external_wp_element_namespaceObject.useRef)(); - const { - orientation, - previousClientId, - nextClientId, - rootClientId, - isInserterShown, - isDistractionFree, - isNavigationMode - } = (0,external_wp_data_namespaceObject.useSelect)(select => { - const { - getBlockOrder, - getBlockListSettings, - getBlockInsertionPoint, - isBlockBeingDragged, - getPreviousBlockClientId, - getNextBlockClientId, - getSettings, - isNavigationMode: _isNavigationMode - } = select(store); - const insertionPoint = getBlockInsertionPoint(); - const order = getBlockOrder(insertionPoint.rootClientId); - if (!order.length) { - return {}; - } - let _previousClientId = order[insertionPoint.index - 1]; - let _nextClientId = order[insertionPoint.index]; - while (isBlockBeingDragged(_previousClientId)) { - _previousClientId = getPreviousBlockClientId(_previousClientId); - } - while (isBlockBeingDragged(_nextClientId)) { - _nextClientId = getNextBlockClientId(_nextClientId); - } - const settings = getSettings(); - return { - previousClientId: _previousClientId, - nextClientId: _nextClientId, - orientation: getBlockListSettings(insertionPoint.rootClientId)?.orientation || 'vertical', - rootClientId: insertionPoint.rootClientId, - isNavigationMode: _isNavigationMode(), - isDistractionFree: settings.isDistractionFree, - isInserterShown: insertionPoint?.__unstableWithInserter - }; - }, []); - const disableMotion = (0,external_wp_compose_namespaceObject.useReducedMotion)(); - function onClick(event) { - if (event.target === ref.current && nextClientId) { - selectBlock(nextClientId, -1); - } + const [enableDefault, userPresets, themePresets, defaultPresets] = use_settings_useSettings(defaultSetting, `${presetSetting}.custom`, `${presetSetting}.theme`, `${presetSetting}.default`); + return (0,external_wp_element_namespaceObject.useMemo)(() => [...(userPresets || duotone_EMPTY_ARRAY), ...(themePresets || duotone_EMPTY_ARRAY), ...(enableDefault && defaultPresets || duotone_EMPTY_ARRAY)], [enableDefault, userPresets, themePresets, defaultPresets]); +} +function getColorsFromDuotonePreset(duotone, duotonePalette) { + if (!duotone) { + return; } - function maybeHideInserterPoint(event) { - // Only hide the inserter if it's triggered on the wrapper, - // and the inserter is not open. - if (event.target === ref.current && !openRef.current) { - hideInsertionPoint(); - } + const preset = duotonePalette?.find(({ + slug + }) => { + return duotone === `var:preset|duotone|${slug}`; + }); + return preset ? preset.colors : undefined; +} +function getDuotonePresetFromColors(colors, duotonePalette) { + if (!colors || !Array.isArray(colors)) { + return; } - function onFocus(event) { - // Only handle click on the wrapper specifically, and not an event - // bubbled from the inserter itself. - if (event.target !== ref.current) { - openRef.current = true; - } + const preset = duotonePalette?.find(duotonePreset => { + return duotonePreset?.colors?.every((val, index) => val === colors[index]); + }); + return preset ? `var:preset|duotone|${preset.slug}` : undefined; +} +function DuotonePanelPure({ + style, + setAttributes, + name +}) { + const duotoneStyle = style?.color?.duotone; + const settings = useBlockSettings(name); + const blockEditingMode = useBlockEditingMode(); + const duotonePalette = useMultiOriginPresets({ + presetSetting: 'color.duotone', + defaultSetting: 'color.defaultDuotone' + }); + const colorPalette = useMultiOriginPresets({ + presetSetting: 'color.palette', + defaultSetting: 'color.defaultPalette' + }); + const [enableCustomColors, enableCustomDuotone] = use_settings_useSettings('color.custom', 'color.customDuotone'); + const disableCustomColors = !enableCustomColors; + const disableCustomDuotone = !enableCustomDuotone || colorPalette?.length === 0 && disableCustomColors; + if (duotonePalette?.length === 0 && disableCustomDuotone) { + return null; } - const lineVariants = { - // Initial position starts from the center and invisible. - start: { - opacity: 0, - scale: 0 - }, - // The line expands to fill the container. If the inserter is visible it - // is delayed so it appears orchestrated. - rest: { - opacity: 1, - scale: 1, - transition: { - delay: isInserterShown ? 0.5 : 0, - type: 'tween' + if (blockEditingMode !== 'default') { + return null; + } + const duotonePresetOrColors = !Array.isArray(duotoneStyle) ? getColorsFromDuotonePreset(duotoneStyle, duotonePalette) : duotoneStyle; + return (0,external_React_.createElement)(external_React_.Fragment, null, (0,external_React_.createElement)(inspector_controls, { + group: "filter" + }, (0,external_React_.createElement)(FiltersPanel, { + value: { + filter: { + duotone: duotonePresetOrColors } }, - hover: { - opacity: 1, - scale: 1, - transition: { - delay: 0.5, - type: 'tween' - } - } - }; - const inserterVariants = { - start: { - scale: disableMotion ? 1 : 0 + onChange: newDuotone => { + const newStyle = { + ...style, + color: { + ...newDuotone?.filter + } + }; + setAttributes({ + style: newStyle + }); }, - rest: { - scale: 1, - transition: { - delay: 0.4, - type: 'tween' + settings: settings + })), (0,external_React_.createElement)(block_controls, { + group: "block", + __experimentalShareWithChildBlocks: true + }, (0,external_React_.createElement)(duotone_control, { + duotonePalette: duotonePalette, + colorPalette: colorPalette, + disableCustomDuotone: disableCustomDuotone, + disableCustomColors: disableCustomColors, + value: duotonePresetOrColors, + onChange: newDuotone => { + const maybePreset = getDuotonePresetFromColors(newDuotone, duotonePalette); + const newStyle = { + ...style, + color: { + ...style?.color, + duotone: maybePreset !== null && maybePreset !== void 0 ? maybePreset : newDuotone // use preset or fallback to custom colors. + } + }; + setAttributes({ + style: newStyle + }); + }, + settings: settings + }))); +} +/* harmony default export */ const duotone = ({ + shareWithChildBlocks: true, + edit: DuotonePanelPure, + useBlockProps: duotone_useBlockProps, + attributeKeys: ['style'], + hasSupport(name) { + return (0,external_wp_blocks_namespaceObject.hasBlockSupport)(name, 'filter.duotone'); + } +}); + +/** + * Filters registered block settings, extending attributes to include + * the `duotone` attribute. + * + * @param {Object} settings Original block settings. + * + * @return {Object} Filtered block settings. + */ +function addDuotoneAttributes(settings) { + // Previous `color.__experimentalDuotone` support flag is migrated via + // block_type_metadata_settings filter in `lib/block-supports/duotone.php`. + if (!(0,external_wp_blocks_namespaceObject.hasBlockSupport)(settings, 'filter.duotone')) { + return settings; + } + + // Allow blocks to specify their own attribute definition with default + // values if needed. + if (!settings.attributes.style) { + Object.assign(settings.attributes, { + style: { + type: 'object' } - } - }; - if (isDistractionFree && !isNavigationMode) { - return null; + }); } - const className = classnames_default()('block-editor-block-list__insertion-point', 'is-' + orientation); - return (0,external_wp_element_namespaceObject.createElement)(inbetween, { - previousClientId: previousClientId, - nextClientId: nextClientId, - __unstablePopoverSlot: __unstablePopoverSlot, - __unstableContentRef: __unstableContentRef - }, (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.__unstableMotion.div, { - layout: !disableMotion, - initial: disableMotion ? 'rest' : 'start', - animate: "rest", - whileHover: "hover", - whileTap: "pressed", - exit: "start", - ref: ref, - tabIndex: -1, - onClick: onClick, - onFocus: onFocus, - className: classnames_default()(className, { - 'is-with-inserter': isInserterShown - }), - onHoverEnd: maybeHideInserterPoint - }, (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.__unstableMotion.div, { - variants: lineVariants, - className: "block-editor-block-list__insertion-point-indicator", - "data-testid": "block-list-insertion-point-indicator" - }), isInserterShown && (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.__unstableMotion.div, { - variants: inserterVariants, - className: classnames_default()('block-editor-block-list__insertion-point-inserter') - }, (0,external_wp_element_namespaceObject.createElement)(inserter, { - position: "bottom center", - clientId: nextClientId, - rootClientId: rootClientId, - __experimentalIsQuick: true, - onToggle: isOpen => { - openRef.current = isOpen; - }, - onSelectOrClose: () => { - openRef.current = false; - } - })))); + return settings; } -function InsertionPoint(props) { - const { - insertionPoint, - isVisible, - isBlockListEmpty - } = (0,external_wp_data_namespaceObject.useSelect)(select => { - const { - getBlockInsertionPoint, - isBlockInsertionPointVisible, - getBlockCount - } = select(store); - const blockInsertionPoint = getBlockInsertionPoint(); - return { - insertionPoint: blockInsertionPoint, - isVisible: isBlockInsertionPointVisible(), - isBlockListEmpty: getBlockCount(blockInsertionPoint?.rootClientId) === 0 - }; - }, []); - if (!isVisible || - // Don't render the insertion point if the block list is empty. - // The insertion point will be represented by the appender instead. - isBlockListEmpty) { - return null; +function useDuotoneStyles({ + clientId, + id: filterId, + selector: duotoneSelector, + attribute: duotoneAttr +}) { + const duotonePalette = useMultiOriginPresets({ + presetSetting: 'color.duotone', + defaultSetting: 'color.defaultDuotone' + }); + + // Possible values for duotone attribute: + // 1. Array of colors - e.g. ['#000000', '#ffffff']. + // 2. Variable for an existing Duotone preset - e.g. 'var:preset|duotone|green-blue' or 'var(--wp--preset--duotone--green-blue)'' + // 3. A CSS string - e.g. 'unset' to remove globally applied duotone. + const isCustom = Array.isArray(duotoneAttr); + const duotonePreset = isCustom ? undefined : getColorsFromDuotonePreset(duotoneAttr, duotonePalette); + const isPreset = typeof duotoneAttr === 'string' && duotonePreset; + const isCSS = typeof duotoneAttr === 'string' && !isPreset; + + // Match the structure of WP_Duotone_Gutenberg::render_duotone_support() in PHP. + let colors = null; + if (isPreset) { + // Array of colors. + colors = duotonePreset; + } else if (isCSS) { + // CSS filter property string (e.g. 'unset'). + colors = duotoneAttr; + } else if (isCustom) { + // Array of colors. + colors = duotoneAttr; } - /** - * Render a popover that overlays the block when the desired operation is to replace it. - * Otherwise, render a popover in between blocks for the indication of inserting between them. - */ - return insertionPoint.operation === 'replace' ? (0,external_wp_element_namespaceObject.createElement)(drop_zone - // Force remount to trigger the animation. - , { - key: `${insertionPoint.rootClientId}-${insertionPoint.index}`, - ...props - }) : (0,external_wp_element_namespaceObject.createElement)(InbetweenInsertionPointPopover, { - ...props + // Build the CSS selectors to which the filter will be applied. + const selectors = duotoneSelector.split(','); + const selectorsScoped = selectors.map(selectorPart => { + // Extra .editor-styles-wrapper specificity is needed in the editor + // since we're not using inline styles to apply the filter. We need to + // override duotone applied by global styles and theme.json. + + // Assuming the selector part is a subclass selector (not a tag name) + // so we can prepend the filter id class. If we want to support elements + // such as `img` or namespaces, we'll need to add a case for that here. + return `.${filterId}${selectorPart.trim()}`; + }); + const selector = selectorsScoped.join(', '); + const isValidFilter = Array.isArray(colors) || colors === 'unset'; + useStyleOverride(isValidFilter ? { + css: colors !== 'unset' ? getDuotoneStylesheet(selector, filterId) : getDuotoneUnsetStylesheet(selector), + __unstableType: 'presets' + } : undefined); + useStyleOverride(isValidFilter ? { + assets: colors !== 'unset' ? getDuotoneFilter(filterId, colors) : '', + __unstableType: 'svgs' + } : undefined); + const blockElement = useBlockElement(clientId); + (0,external_wp_element_namespaceObject.useEffect)(() => { + if (!isValidFilter) return; + + // Safari does not always update the duotone filter when the duotone colors + // are changed. When using Safari, force the block element to be repainted by + // the browser to ensure any changes are reflected visually. This logic matches + // that used on the site frontend in `block-supports/duotone.php`. + if (blockElement && isSafari) { + const display = blockElement.style.display; + // Switch to `inline-block` to force a repaint. In the editor, `inline-block` + // is used instead of `none` to ensure that scroll position is not affected, + // as `none` results in the editor scrolling to the top of the block. + blockElement.style.display = 'inline-block'; + // Simply accessing el.offsetHeight flushes layout and style + // changes in WebKit without having to wait for setTimeout. + // eslint-disable-next-line no-unused-expressions + blockElement.offsetHeight; + blockElement.style.display = display; + } + }, [isValidFilter, blockElement]); +} +function duotone_useBlockProps({ + name, + style +}) { + const id = (0,external_wp_compose_namespaceObject.useInstanceId)(duotone_useBlockProps); + const selector = (0,external_wp_element_namespaceObject.useMemo)(() => { + const blockType = (0,external_wp_blocks_namespaceObject.getBlockType)(name); + if (blockType) { + // Backwards compatibility for `supports.color.__experimentalDuotone` + // is provided via the `block_type_metadata_settings` filter. If + // `supports.filter.duotone` has not been set and the + // experimental property has been, the experimental property + // value is copied into `supports.filter.duotone`. + const duotoneSupport = (0,external_wp_blocks_namespaceObject.getBlockSupport)(blockType, 'filter.duotone', false); + if (!duotoneSupport) { + return null; + } + + // If the experimental duotone support was set, that value is + // to be treated as a selector and requires scoping. + const experimentalDuotone = (0,external_wp_blocks_namespaceObject.getBlockSupport)(blockType, 'color.__experimentalDuotone', false); + if (experimentalDuotone) { + const rootSelector = getBlockCSSSelector(blockType); + return typeof experimentalDuotone === 'string' ? scopeSelector(rootSelector, experimentalDuotone) : rootSelector; + } + + // Regular filter.duotone support uses filter.duotone selectors with fallbacks. + return getBlockCSSSelector(blockType, 'filter.duotone', { + fallback: true + }); + } + }, [name]); + const attribute = style?.color?.duotone; + const filterClass = `wp-duotone-${id}`; + const shouldRender = selector && attribute; + useDuotoneStyles({ + clientId: id, + id: filterClass, + selector, + attribute }); + return { + className: shouldRender ? filterClass : '' + }; } +(0,external_wp_hooks_namespaceObject.addFilter)('blocks.registerBlockType', 'core/editor/duotone/add-attributes', addDuotoneAttributes); -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/block-list/use-in-between-inserter.js +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/use-block-display-information/index.js /** * WordPress dependencies */ @@ -27003,138 +32184,105 @@ function InsertionPoint(props) { - /** * Internal dependencies */ -function useInBetweenInserter() { - const openRef = (0,external_wp_element_namespaceObject.useContext)(insertion_point_InsertionPointOpenRef); - const isInBetweenInserterDisabled = (0,external_wp_data_namespaceObject.useSelect)(select => select(store).getSettings().isDistractionFree || select(store).__unstableGetEditorMode() === 'zoom-out', []); - const { - getBlockListSettings, - getBlockIndex, - isMultiSelecting, - getSelectedBlockClientIds, - getTemplateLock, - __unstableIsWithinBlockOverlay, - getBlockEditingMode - } = (0,external_wp_data_namespaceObject.useSelect)(store); - const { - showInsertionPoint, - hideInsertionPoint - } = (0,external_wp_data_namespaceObject.useDispatch)(store); - return (0,external_wp_compose_namespaceObject.useRefEffect)(node => { - if (isInBetweenInserterDisabled) { - return; - } - function onMouseMove(event) { - if (openRef.current) { - return; - } - - // Ignore text nodes sometimes detected in FireFox. - if (event.target.nodeType === event.target.TEXT_NODE) { - return; - } - if (isMultiSelecting()) { - return; - } - if (!event.target.classList.contains('block-editor-block-list__layout')) { - hideInsertionPoint(); - return; - } - let rootClientId; - if (!event.target.classList.contains('is-root-container')) { - const blockElement = !!event.target.getAttribute('data-block') ? event.target : event.target.closest('[data-block]'); - rootClientId = blockElement.getAttribute('data-block'); - } - if (getTemplateLock(rootClientId) || getBlockEditingMode(rootClientId) === 'disabled') { - return; - } - const orientation = getBlockListSettings(rootClientId)?.orientation || 'vertical'; - const offsetTop = event.clientY; - const offsetLeft = event.clientX; - const children = Array.from(event.target.children); - let element = children.find(blockEl => { - const blockElRect = blockEl.getBoundingClientRect(); - return blockEl.classList.contains('wp-block') && orientation === 'vertical' && blockElRect.top > offsetTop || blockEl.classList.contains('wp-block') && orientation === 'horizontal' && ((0,external_wp_i18n_namespaceObject.isRTL)() ? blockElRect.right < offsetLeft : blockElRect.left > offsetLeft); - }); - if (!element) { - hideInsertionPoint(); - return; - } - - // The block may be in an alignment wrapper, so check the first direct - // child if the element has no ID. - if (!element.id) { - element = element.firstElementChild; - if (!element) { - hideInsertionPoint(); - return; - } - } - - // Don't show the insertion point if a parent block has an "overlay" - // See https://github.com/WordPress/gutenberg/pull/34012#pullrequestreview-727762337 - const clientId = element.id.slice('block-'.length); - if (!clientId || __unstableIsWithinBlockOverlay(clientId)) { - return; - } - - // Don't show the inserter when hovering above (conflicts with - // block toolbar) or inside selected block(s). - if (getSelectedBlockClientIds().includes(clientId)) { - return; - } - const elementRect = element.getBoundingClientRect(); - if (orientation === 'horizontal' && (event.clientY > elementRect.bottom || event.clientY < elementRect.top) || orientation === 'vertical' && (event.clientX > elementRect.right || event.clientX < elementRect.left)) { - hideInsertionPoint(); - return; - } - const index = getBlockIndex(clientId); - - // Don't show the in-between inserter before the first block in - // the list (preserves the original behaviour). - if (index === 0) { - hideInsertionPoint(); - return; - } - showInsertionPoint(rootClientId, index, { - __unstableWithInserter: true - }); - } - node.addEventListener('mousemove', onMouseMove); - return () => { - node.removeEventListener('mousemove', onMouseMove); - }; - }, [openRef, getBlockListSettings, getBlockIndex, isMultiSelecting, showInsertionPoint, hideInsertionPoint, getSelectedBlockClientIds, isInBetweenInserterDisabled]); -} - -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/inner-blocks/with-client-id.js +/** @typedef {import('@wordpress/blocks').WPIcon} WPIcon */ /** - * WordPress dependencies + * Contains basic block's information for display reasons. + * + * @typedef {Object} WPBlockDisplayInformation + * + * @property {boolean} isSynced True if is a reusable block or template part + * @property {string} title Human-readable block type label. + * @property {WPIcon} icon Block type icon. + * @property {string} description A detailed block type description. + * @property {string} anchor HTML anchor. + * @property {name} name A custom, human readable name for the block. */ +/** + * Get the display label for a block's position type. + * + * @param {Object} attributes Block attributes. + * @return {string} The position type label. + */ +function getPositionTypeLabel(attributes) { + const positionType = attributes?.style?.position?.type; + if (positionType === 'sticky') { + return (0,external_wp_i18n_namespaceObject.__)('Sticky'); + } + if (positionType === 'fixed') { + return (0,external_wp_i18n_namespaceObject.__)('Fixed'); + } + return null; +} /** - * Internal dependencies + * Hook used to try to find a matching block variation and return + * the appropriate information for display reasons. In order to + * to try to find a match we need to things: + * 1. Block's client id to extract it's current attributes. + * 2. A block variation should have set `isActive` prop to a proper function. + * + * If for any reason a block variation match cannot be found, + * the returned information come from the Block Type. + * If no blockType is found with the provided clientId, returns null. + * + * @param {string} clientId Block's client id. + * @return {?WPBlockDisplayInformation} Block's display information, or `null` when the block or its type not found. */ -const withClientId = (0,external_wp_compose_namespaceObject.createHigherOrderComponent)(WrappedComponent => props => { - const { - clientId - } = useBlockEditContext(); - return (0,external_wp_element_namespaceObject.createElement)(WrappedComponent, { - ...props, - clientId: clientId - }); -}, 'withClientId'); -/* harmony default export */ var with_client_id = (withClientId); +function useBlockDisplayInformation(clientId) { + return (0,external_wp_data_namespaceObject.useSelect)(select => { + if (!clientId) return null; + const { + getBlockName, + getBlockAttributes, + __experimentalGetReusableBlockTitle + } = select(store); + const { + getBlockType, + getActiveBlockVariation + } = select(external_wp_blocks_namespaceObject.store); + const blockName = getBlockName(clientId); + const blockType = getBlockType(blockName); + if (!blockType) return null; + const attributes = getBlockAttributes(clientId); + const match = getActiveBlockVariation(blockName, attributes); + const isReusable = (0,external_wp_blocks_namespaceObject.isReusableBlock)(blockType); + const resusableTitle = isReusable ? __experimentalGetReusableBlockTitle(attributes.ref) : undefined; + const title = resusableTitle || blockType.title; + const isSynced = isReusable || (0,external_wp_blocks_namespaceObject.isTemplatePart)(blockType); + const positionLabel = getPositionTypeLabel(attributes); + const blockTypeInfo = { + isSynced, + title, + icon: blockType.icon, + description: blockType.description, + anchor: attributes?.anchor, + positionLabel, + positionType: attributes?.style?.position?.type, + name: attributes?.metadata?.name + }; + if (!match) return blockTypeInfo; + return { + isSynced, + title: match.title || blockType.title, + icon: match.icon || blockType.icon, + description: match.description || blockType.description, + anchor: attributes?.anchor, + positionLabel, + positionType: attributes?.style?.position?.type, + name: attributes?.metadata?.name + }; + }, [clientId]); +} -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/inner-blocks/button-block-appender.js +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/hooks/position.js /** * External dependencies @@ -27142,34 +32290,12 @@ const withClientId = (0,external_wp_compose_namespaceObject.createHigherOrderCom /** - * Internal dependencies + * WordPress dependencies */ -const button_block_appender_ButtonBlockAppender = ({ - clientId, - showSeparator, - isFloating, - onAddBlock, - isToggle -}) => { - return (0,external_wp_element_namespaceObject.createElement)(button_block_appender, { - className: classnames_default()({ - 'block-list-appender__toggle': isToggle - }), - rootClientId: clientId, - showSeparator: showSeparator, - isFloating: isFloating, - onAddBlock: onAddBlock - }); -}; -/* harmony default export */ var inner_blocks_button_block_appender = (with_client_id(button_block_appender_ButtonBlockAppender)); -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/inner-blocks/default-block-appender.js -/** - * WordPress dependencies - */ @@ -27179,173 +32305,320 @@ const button_block_appender_ButtonBlockAppender = ({ -const default_block_appender_DefaultBlockAppender = ({ - clientId -}) => { - return (0,external_wp_element_namespaceObject.createElement)(default_block_appender, { - rootClientId: clientId - }); -}; -/* harmony default export */ var inner_blocks_default_block_appender = ((0,external_wp_compose_namespaceObject.compose)([with_client_id, (0,external_wp_data_namespaceObject.withSelect)((select, { - clientId -}) => { - const { - getBlockOrder - } = select(store); - const blockClientIds = getBlockOrder(clientId); - return { - lastBlockClientId: blockClientIds[blockClientIds.length - 1] - }; -})])(default_block_appender_DefaultBlockAppender)); -;// CONCATENATED MODULE: external ["wp","isShallowEqual"] -var external_wp_isShallowEqual_namespaceObject = window["wp"]["isShallowEqual"]; -var external_wp_isShallowEqual_default = /*#__PURE__*/__webpack_require__.n(external_wp_isShallowEqual_namespaceObject); -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/inner-blocks/use-nested-settings-update.js -/** - * WordPress dependencies - */ +const { + CustomSelectControl +} = unlock(external_wp_components_namespaceObject.privateApis); +const POSITION_SUPPORT_KEY = 'position'; +const OPTION_CLASSNAME = 'block-editor-hooks__position-selection__select-control__option'; +const DEFAULT_OPTION = { + key: 'default', + value: '', + name: (0,external_wp_i18n_namespaceObject.__)('Default'), + className: OPTION_CLASSNAME +}; +const STICKY_OPTION = { + key: 'sticky', + value: 'sticky', + name: (0,external_wp_i18n_namespaceObject._x)('Sticky', 'Name for the value of the CSS position property'), + className: OPTION_CLASSNAME, + __experimentalHint: (0,external_wp_i18n_namespaceObject.__)('The block will stick to the top of the window instead of scrolling.') +}; +const FIXED_OPTION = { + key: 'fixed', + value: 'fixed', + name: (0,external_wp_i18n_namespaceObject._x)('Fixed', 'Name for the value of the CSS position property'), + className: OPTION_CLASSNAME, + __experimentalHint: (0,external_wp_i18n_namespaceObject.__)('The block will not move when the page is scrolled.') +}; +const POSITION_SIDES = ['top', 'right', 'bottom', 'left']; +const VALID_POSITION_TYPES = ['sticky', 'fixed']; +/** + * Get calculated position CSS. + * + * @param {Object} props Component props. + * @param {string} props.selector Selector to use. + * @param {Object} props.style Style object. + * @return {string} The generated CSS rules. + */ +function getPositionCSS({ + selector, + style +}) { + let output = ''; + const { + type: positionType + } = style?.position || {}; + if (!VALID_POSITION_TYPES.includes(positionType)) { + return output; + } + output += `${selector} {`; + output += `position: ${positionType};`; + POSITION_SIDES.forEach(side => { + if (style?.position?.[side] !== undefined) { + output += `${side}: ${style.position[side]};`; + } + }); + if (positionType === 'sticky' || positionType === 'fixed') { + // TODO: Replace hard-coded z-index value with a z-index preset approach in theme.json. + output += `z-index: 10`; + } + output += `}`; + return output; +} +/** + * Determines if there is sticky position support. + * + * @param {string|Object} blockType Block name or Block Type object. + * + * @return {boolean} Whether there is support. + */ +function hasStickyPositionSupport(blockType) { + const support = (0,external_wp_blocks_namespaceObject.getBlockSupport)(blockType, POSITION_SUPPORT_KEY); + return !!(true === support || support?.sticky); +} /** - * Internal dependencies + * Determines if there is fixed position support. + * + * @param {string|Object} blockType Block name or Block Type object. + * + * @return {boolean} Whether there is support. */ +function hasFixedPositionSupport(blockType) { + const support = (0,external_wp_blocks_namespaceObject.getBlockSupport)(blockType, POSITION_SUPPORT_KEY); + return !!(true === support || support?.fixed); +} +/** + * Determines if there is position support. + * + * @param {string|Object} blockType Block name or Block Type object. + * + * @return {boolean} Whether there is support. + */ +function hasPositionSupport(blockType) { + const support = (0,external_wp_blocks_namespaceObject.getBlockSupport)(blockType, POSITION_SUPPORT_KEY); + return !!support; +} +/** + * Checks if there is a current value in the position block support attributes. + * + * @param {Object} props Block props. + * @return {boolean} Whether or not the block has a position value set. + */ +function hasPositionValue(props) { + return props.attributes.style?.position?.type !== undefined; +} -/** @typedef {import('../../selectors').WPDirectInsertBlock } WPDirectInsertBlock */ +/** + * Checks if the block is currently set to a sticky or fixed position. + * This check is helpful for determining how to position block toolbars or other elements. + * + * @param {Object} attributes Block attributes. + * @return {boolean} Whether or not the block is set to a sticky or fixed position. + */ +function hasStickyOrFixedPositionValue(attributes) { + const positionType = attributes.style?.position?.type; + return positionType === 'sticky' || positionType === 'fixed'; +} -const pendingSettingsUpdates = new WeakMap(); -function useShallowMemo(value) { - const [prevValue, setPrevValue] = (0,external_wp_element_namespaceObject.useState)(value); - if (!external_wp_isShallowEqual_default()(prevValue, value)) { - setPrevValue(value); - } - return prevValue; +/** + * Resets the position block support attributes. This can be used when disabling + * the position support controls for a block via a `ToolsPanel`. + * + * @param {Object} props Block props. + * @param {Object} props.attributes Block's attributes. + * @param {Object} props.setAttributes Function to set block's attributes. + */ +function resetPosition({ + attributes = {}, + setAttributes +}) { + const { + style = {} + } = attributes; + setAttributes({ + style: cleanEmptyObject({ + ...style, + position: { + ...style?.position, + type: undefined, + top: undefined, + right: undefined, + bottom: undefined, + left: undefined + } + }) + }); } /** - * This hook is a side effect which updates the block-editor store when changes - * happen to inner block settings. The given props are transformed into a - * settings object, and if that is different from the current settings object in - * the block-editor store, then the store is updated with the new settings which - * came from props. + * Custom hook that checks if position settings have been disabled. * - * @param {string} clientId The client ID of the block to update. - * @param {string[]} allowedBlocks An array of block names which are permitted - * in inner blocks. - * @param {string[]} prioritizedInserterBlocks Block names and/or block variations to be prioritized in the inserter, in the format {blockName}/{variationName}. - * @param {?WPDirectInsertBlock} defaultBlock The default block to insert: [ blockName, { blockAttributes } ]. - * @param {?Function|boolean} directInsert If a default block should be inserted directly by the appender. + * @param {string} name The name of the block. * - * @param {?WPDirectInsertBlock} __experimentalDefaultBlock A deprecated prop for the default block to insert: [ blockName, { blockAttributes } ]. Use `defaultBlock` instead. + * @return {boolean} Whether padding setting is disabled. + */ +function useIsPositionDisabled({ + name: blockName +} = {}) { + const [allowFixed, allowSticky] = use_settings_useSettings('position.fixed', 'position.sticky'); + const isDisabled = !allowFixed && !allowSticky; + return !hasPositionSupport(blockName) || isDisabled; +} + +/* + * Position controls rendered in an inspector control panel. * - * @param {?Function|boolean} __experimentalDirectInsert A deprecated prop for whether a default block should be inserted directly by the appender. Use `directInsert` instead. + * @param {Object} props * - * @param {string} [templateLock] The template lock specified for the inner - * blocks component. (e.g. "all") - * @param {boolean} captureToolbars Whether or children toolbars should be shown - * in the inner blocks component rather than on - * the child block. - * @param {string} orientation The direction in which the block - * should face. - * @param {Object} layout The layout object for the block container. + * @return {Element} Position panel. */ -function useNestedSettingsUpdate(clientId, allowedBlocks, prioritizedInserterBlocks, defaultBlock, directInsert, __experimentalDefaultBlock, __experimentalDirectInsert, templateLock, captureToolbars, orientation, layout) { - const { - updateBlockListSettings - } = (0,external_wp_data_namespaceObject.useDispatch)(store); - const registry = (0,external_wp_data_namespaceObject.useRegistry)(); +function PositionPanelPure({ + style = {}, + clientId, + name: blockName, + setAttributes +}) { + const allowFixed = hasFixedPositionSupport(blockName); + const allowSticky = hasStickyPositionSupport(blockName); + const value = style?.position?.type; const { - parentLock + firstParentClientId } = (0,external_wp_data_namespaceObject.useSelect)(select => { - const rootClientId = select(store).getBlockRootClientId(clientId); + const { + getBlockParents + } = select(store); + const parents = getBlockParents(clientId); return { - parentLock: select(store).getTemplateLock(rootClientId) + firstParentClientId: parents[parents.length - 1] }; }, [clientId]); - - // Implementors often pass a new array on every render, - // and the contents of the arrays are just strings, so the entire array - // can be passed as dependencies but We need to include the length of the array, - // otherwise if the arrays change length but the first elements are equal the comparison, - // does not works as expected. - const _allowedBlocks = useShallowMemo(allowedBlocks); - const _prioritizedInserterBlocks = (0,external_wp_element_namespaceObject.useMemo)(() => prioritizedInserterBlocks, - // eslint-disable-next-line react-hooks/exhaustive-deps - prioritizedInserterBlocks); - const _templateLock = templateLock === undefined || parentLock === 'contentOnly' ? parentLock : templateLock; - (0,external_wp_element_namespaceObject.useLayoutEffect)(() => { - const newSettings = { - allowedBlocks: _allowedBlocks, - prioritizedInserterBlocks: _prioritizedInserterBlocks, - templateLock: _templateLock - }; - - // These values are not defined for RN, so only include them if they - // are defined. - if (captureToolbars !== undefined) { - newSettings.__experimentalCaptureToolbars = captureToolbars; - } - - // Orientation depends on layout, - // ideally the separate orientation prop should be deprecated. - if (orientation !== undefined) { - newSettings.orientation = orientation; - } else { - const layoutType = getLayoutType(layout?.type); - newSettings.orientation = layoutType.getOrientation(layout); - } - if (__experimentalDefaultBlock !== undefined) { - external_wp_deprecated_default()('__experimentalDefaultBlock', { - alternative: 'defaultBlock', - since: '6.3', - version: '6.4' - }); - newSettings.defaultBlock = __experimentalDefaultBlock; - } - if (defaultBlock !== undefined) { - newSettings.defaultBlock = defaultBlock; - } - if (__experimentalDirectInsert !== undefined) { - external_wp_deprecated_default()('__experimentalDirectInsert', { - alternative: 'directInsert', - since: '6.3', - version: '6.4' - }); - newSettings.directInsert = __experimentalDirectInsert; + const blockInformation = useBlockDisplayInformation(firstParentClientId); + const stickyHelpText = allowSticky && value === STICKY_OPTION.value && blockInformation ? (0,external_wp_i18n_namespaceObject.sprintf)( /* translators: %s: the name of the parent block. */ + (0,external_wp_i18n_namespaceObject.__)('The block will stick to the scrollable area of the parent %s block.'), blockInformation.title) : null; + const options = (0,external_wp_element_namespaceObject.useMemo)(() => { + const availableOptions = [DEFAULT_OPTION]; + // Display options if they are allowed, or if a block already has a valid value set. + // This allows for a block to be switched off from a position type that is not allowed. + if (allowSticky || value === STICKY_OPTION.value) { + availableOptions.push(STICKY_OPTION); } - if (directInsert !== undefined) { - newSettings.directInsert = directInsert; + if (allowFixed || value === FIXED_OPTION.value) { + availableOptions.push(FIXED_OPTION); } + return availableOptions; + }, [allowFixed, allowSticky, value]); + const onChangeType = next => { + // For now, use a hard-coded `0px` value for the position. + // `0px` is preferred over `0` as it can be used in `calc()` functions. + // In the future, it could be useful to allow for an offset value. + const placementValue = '0px'; + const newStyle = { + ...style, + position: { + ...style?.position, + type: next, + top: next === 'sticky' || next === 'fixed' ? placementValue : undefined + } + }; + setAttributes({ + style: utils_cleanEmptyObject(newStyle) + }); + }; + const selectedOption = value ? options.find(option => option.value === value) || DEFAULT_OPTION : DEFAULT_OPTION; - // Batch updates to block list settings to avoid triggering cascading renders - // for each container block included in a tree and optimize initial render. - // To avoid triggering updateBlockListSettings for each container block - // causing X re-renderings for X container blocks, - // we batch all the updatedBlockListSettings in a single "data" batch - // which results in a single re-render. - if (!pendingSettingsUpdates.get(registry)) { - pendingSettingsUpdates.set(registry, []); + // Only display position controls if there is at least one option to choose from. + return external_wp_element_namespaceObject.Platform.select({ + web: options.length > 1 ? (0,external_React_.createElement)(inspector_controls, { + group: "position" + }, (0,external_React_.createElement)(external_wp_components_namespaceObject.BaseControl, { + className: "block-editor-hooks__position-selection", + __nextHasNoMarginBottom: true, + help: stickyHelpText + }, (0,external_React_.createElement)(CustomSelectControl, { + __nextUnconstrainedWidth: true, + __next40pxDefaultSize: true, + className: "block-editor-hooks__position-selection__select-control", + label: (0,external_wp_i18n_namespaceObject.__)('Position'), + hideLabelFromVision: true, + describedBy: (0,external_wp_i18n_namespaceObject.sprintf)( + // translators: %s: Currently selected position. + (0,external_wp_i18n_namespaceObject.__)('Currently selected position: %s'), selectedOption.name), + options: options, + value: selectedOption, + __experimentalShowSelectedHint: true, + onChange: ({ + selectedItem + }) => { + onChangeType(selectedItem.value); + }, + size: '__unstable-large' + }))) : null, + native: null + }); +} +/* harmony default export */ const position = ({ + edit: function Edit(props) { + const isPositionDisabled = useIsPositionDisabled(props); + if (isPositionDisabled) { + return null; } - pendingSettingsUpdates.get(registry).push([clientId, newSettings]); - window.queueMicrotask(() => { - if (pendingSettingsUpdates.get(registry)?.length) { - registry.batch(() => { - pendingSettingsUpdates.get(registry).forEach(args => { - updateBlockListSettings(...args); - }); - pendingSettingsUpdates.set(registry, []); - }); - } + return (0,external_React_.createElement)(PositionPanelPure, { + ...props }); - }, [clientId, _allowedBlocks, _prioritizedInserterBlocks, _templateLock, defaultBlock, directInsert, __experimentalDefaultBlock, __experimentalDirectInsert, captureToolbars, orientation, updateBlockListSettings, layout, registry]); + }, + useBlockProps: position_useBlockProps, + attributeKeys: ['style'], + hasSupport(name) { + return (0,external_wp_blocks_namespaceObject.hasBlockSupport)(name, POSITION_SUPPORT_KEY); + } +}); +function position_useBlockProps({ + name, + style +}) { + const hasPositionBlockSupport = (0,external_wp_blocks_namespaceObject.hasBlockSupport)(name, POSITION_SUPPORT_KEY); + const isPositionDisabled = useIsPositionDisabled({ + name + }); + const allowPositionStyles = hasPositionBlockSupport && !isPositionDisabled; + const id = (0,external_wp_compose_namespaceObject.useInstanceId)(position_useBlockProps); + + // Higher specificity to override defaults in editor UI. + const positionSelector = `.wp-container-${id}.wp-container-${id}`; + + // Get CSS string for the current position values. + let css; + if (allowPositionStyles) { + css = getPositionCSS({ + selector: positionSelector, + style + }) || ''; + } + + // Attach a `wp-container-` id-based class name. + const className = classnames_default()({ + [`wp-container-${id}`]: allowPositionStyles && !!css, + // Only attach a container class if there is generated CSS to be attached. + [`is-position-${style?.position?.type}`]: allowPositionStyles && !!css && !!style?.position?.type + }); + useStyleOverride({ + css + }); + return { + className + }; } -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/inner-blocks/use-inner-block-template-sync.js +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/hooks/layout.js + /** * External dependencies */ @@ -27358,633 +32631,822 @@ function useNestedSettingsUpdate(clientId, allowedBlocks, prioritizedInserterBlo + + + /** * Internal dependencies */ + + + + + + +const layoutBlockSupportKey = 'layout'; +function hasLayoutBlockSupport(blockName) { + return (0,external_wp_blocks_namespaceObject.hasBlockSupport)(blockName, 'layout') || (0,external_wp_blocks_namespaceObject.hasBlockSupport)(blockName, '__experimentalLayout'); +} + /** - * This hook makes sure that a block's inner blocks stay in sync with the given - * block "template". The template is a block hierarchy to which inner blocks must - * conform. If the blocks get "out of sync" with the template and the template - * is meant to be locked (e.g. templateLock = "all" or templateLock = "contentOnly"), - * then we replace the inner blocks with the correct value after synchronizing it with the template. + * Generates the utility classnames for the given block's layout attributes. * - * @param {string} clientId The block client ID. - * @param {Object} template The template to match. - * @param {string} templateLock The template lock state for the inner blocks. For - * example, if the template lock is set to "all", - * then the inner blocks will stay in sync with the - * template. If not defined or set to false, then - * the inner blocks will not be synchronized with - * the given template. - * @param {boolean} templateInsertUpdatesSelection Whether or not to update the - * block-editor selection state when inner blocks - * are replaced after template synchronization. + * @param { Object } blockAttributes Block attributes. + * @param { string } blockName Block name. + * + * @return { Array } Array of CSS classname strings. */ -function useInnerBlockTemplateSync(clientId, template, templateLock, templateInsertUpdatesSelection) { +function useLayoutClasses(blockAttributes = {}, blockName = '') { const { - getBlocks, - getSelectedBlocksInitialCaretPosition, - isBlockSelected - } = (0,external_wp_data_namespaceObject.useSelect)(store); + kebabCase + } = unlock(external_wp_components_namespaceObject.privateApis); + const rootPaddingAlignment = (0,external_wp_data_namespaceObject.useSelect)(select => { + const { + getSettings + } = select(store); + return getSettings().__experimentalFeatures?.useRootPaddingAwareAlignments; + }, []); const { - replaceInnerBlocks, - __unstableMarkNextChangeAsNotPersistent - } = (0,external_wp_data_namespaceObject.useDispatch)(store); + layout + } = blockAttributes; const { - innerBlocks - } = (0,external_wp_data_namespaceObject.useSelect)(select => ({ - innerBlocks: select(store).getBlocks(clientId) - }), [clientId]); - - // Maintain a reference to the previous value so we can do a deep equality check. - const existingTemplate = (0,external_wp_element_namespaceObject.useRef)(null); - (0,external_wp_element_namespaceObject.useLayoutEffect)(() => { - let isCancelled = false; - - // There's an implicit dependency between useInnerBlockTemplateSync and useNestedSettingsUpdate - // The former needs to happen after the latter and since the latter is using microtasks to batch updates (performance optimization), - // we need to schedule this one in a microtask as well. - // Example: If you remove queueMicrotask here, ctrl + click to insert quote block won't close the inserter. - window.queueMicrotask(() => { - if (isCancelled) { - return; - } - - // Only synchronize innerBlocks with template if innerBlocks are empty - // or a locking "all" or "contentOnly" exists directly on the block. - const currentInnerBlocks = getBlocks(clientId); - const shouldApplyTemplate = currentInnerBlocks.length === 0 || templateLock === 'all' || templateLock === 'contentOnly'; - const hasTemplateChanged = !es6_default()(template, existingTemplate.current); - if (!shouldApplyTemplate || !hasTemplateChanged) { - return; - } - existingTemplate.current = template; - const nextBlocks = (0,external_wp_blocks_namespaceObject.synchronizeBlocksWithTemplate)(currentInnerBlocks, template); - if (!es6_default()(nextBlocks, currentInnerBlocks)) { - __unstableMarkNextChangeAsNotPersistent(); - replaceInnerBlocks(clientId, nextBlocks, currentInnerBlocks.length === 0 && templateInsertUpdatesSelection && nextBlocks.length !== 0 && isBlockSelected(clientId), - // This ensures the "initialPosition" doesn't change when applying the template - // If we're supposed to focus the block, we'll focus the first inner block - // otherwise, we won't apply any auto-focus. - // This ensures for instance that the focus stays in the inserter when inserting the "buttons" block. - getSelectedBlocksInitialCaretPosition()); - } - }); - return () => { - isCancelled = true; - }; - }, [innerBlocks, template, templateLock, clientId]); + default: defaultBlockLayout + } = (0,external_wp_blocks_namespaceObject.getBlockSupport)(blockName, layoutBlockSupportKey) || {}; + const usedLayout = layout?.inherit || layout?.contentSize || layout?.wideSize ? { + ...layout, + type: 'constrained' + } : layout || defaultBlockLayout || {}; + const layoutClassnames = []; + if (LAYOUT_DEFINITIONS[usedLayout?.type || 'default']?.className) { + const baseClassName = LAYOUT_DEFINITIONS[usedLayout?.type || 'default']?.className; + const splitBlockName = blockName.split('/'); + const fullBlockName = splitBlockName[0] === 'core' ? splitBlockName.pop() : splitBlockName.join('-'); + const compoundClassName = `wp-block-${fullBlockName}-${baseClassName}`; + layoutClassnames.push(baseClassName, compoundClassName); + } + if ((usedLayout?.inherit || usedLayout?.contentSize || usedLayout?.type === 'constrained') && rootPaddingAlignment) { + layoutClassnames.push('has-global-padding'); + } + if (usedLayout?.orientation) { + layoutClassnames.push(`is-${kebabCase(usedLayout.orientation)}`); + } + if (usedLayout?.justifyContent) { + layoutClassnames.push(`is-content-justification-${kebabCase(usedLayout.justifyContent)}`); + } + if (usedLayout?.flexWrap && usedLayout.flexWrap === 'nowrap') { + layoutClassnames.push('is-nowrap'); + } + return layoutClassnames; } -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/inner-blocks/use-block-context.js /** - * WordPress dependencies + * Generates a CSS rule with the given block's layout styles. + * + * @param { Object } blockAttributes Block attributes. + * @param { string } blockName Block name. + * @param { string } selector A selector to use in generating the CSS rule. + * + * @return { string } CSS rule. */ +function useLayoutStyles(blockAttributes = {}, blockName, selector) { + const { + layout = {}, + style = {} + } = blockAttributes; + // Update type for blocks using legacy layouts. + const usedLayout = layout?.inherit || layout?.contentSize || layout?.wideSize ? { + ...layout, + type: 'constrained' + } : layout || {}; + const fullLayoutType = getLayoutType(usedLayout?.type || 'default'); + const [blockGapSupport] = use_settings_useSettings('spacing.blockGap'); + const hasBlockGapSupport = blockGapSupport !== null; + const css = fullLayoutType?.getLayoutStyle?.({ + blockName, + selector, + layout, + style, + hasBlockGapSupport + }); + return css; +} +function LayoutPanelPure({ + layout, + setAttributes, + name: blockName +}) { + const settings = useBlockSettings(blockName); + // Block settings come from theme.json under settings.[blockName]. + const { + layout: layoutSettings + } = settings; + // Layout comes from block attributes. + const [defaultThemeLayout] = use_settings_useSettings('layout'); + const { + themeSupportsLayout + } = (0,external_wp_data_namespaceObject.useSelect)(select => { + const { + getSettings + } = select(store); + return { + themeSupportsLayout: getSettings().supportsLayout + }; + }, []); + const blockEditingMode = useBlockEditingMode(); + if (blockEditingMode !== 'default') { + return null; + } + // Layout block support comes from the block's block.json. + const layoutBlockSupport = (0,external_wp_blocks_namespaceObject.getBlockSupport)(blockName, layoutBlockSupportKey, {}); + const blockSupportAndThemeSettings = { + ...layoutSettings, + ...layoutBlockSupport + }; + const { + allowSwitching, + allowEditing = true, + allowInheriting = true, + default: defaultBlockLayout + } = blockSupportAndThemeSettings; + if (!allowEditing) { + return null; + } + // Only show the inherit toggle if it's supported, + // a default theme layout is set (e.g. one that provides `contentSize` and/or `wideSize` values), + // and either the default / flow or the constrained layout type is in use, as the toggle switches from one to the other. + const showInheritToggle = !!(allowInheriting && !!defaultThemeLayout && (!layout?.type || layout?.type === 'default' || layout?.type === 'constrained' || layout?.inherit)); + const usedLayout = layout || defaultBlockLayout || {}; + const { + inherit = false, + type = 'default', + contentSize = null + } = usedLayout; + /** + * `themeSupportsLayout` is only relevant to the `default/flow` or + * `constrained` layouts and it should not be taken into account when other + * `layout` types are used. + */ + if ((type === 'default' || type === 'constrained') && !themeSupportsLayout) { + return null; + } + const layoutType = getLayoutType(type); + const constrainedType = getLayoutType('constrained'); + const displayControlsForLegacyLayouts = !usedLayout.type && (contentSize || inherit); + const hasContentSizeOrLegacySettings = !!inherit || !!contentSize; + const onChangeType = newType => setAttributes({ + layout: { + type: newType + } + }); + const onChangeLayout = newLayout => setAttributes({ + layout: newLayout + }); + return (0,external_React_.createElement)(external_React_.Fragment, null, (0,external_React_.createElement)(inspector_controls, null, (0,external_React_.createElement)(external_wp_components_namespaceObject.PanelBody, { + title: (0,external_wp_i18n_namespaceObject.__)('Layout') + }, showInheritToggle && (0,external_React_.createElement)(external_React_.Fragment, null, (0,external_React_.createElement)(external_wp_components_namespaceObject.ToggleControl, { + __nextHasNoMarginBottom: true, + className: "block-editor-hooks__toggle-control", + label: (0,external_wp_i18n_namespaceObject.__)('Inner blocks use content width'), + checked: layoutType?.name === 'constrained' || hasContentSizeOrLegacySettings, + onChange: () => setAttributes({ + layout: { + type: layoutType?.name === 'constrained' || hasContentSizeOrLegacySettings ? 'default' : 'constrained' + } + }), + help: layoutType?.name === 'constrained' || hasContentSizeOrLegacySettings ? (0,external_wp_i18n_namespaceObject.__)('Nested blocks use content width with options for full and wide widths.') : (0,external_wp_i18n_namespaceObject.__)('Nested blocks will fill the width of this container. Toggle to constrain.') + })), !inherit && allowSwitching && (0,external_React_.createElement)(LayoutTypeSwitcher, { + type: type, + onChange: onChangeType + }), layoutType && layoutType.name !== 'default' && (0,external_React_.createElement)(layoutType.inspectorControls, { + layout: usedLayout, + onChange: onChangeLayout, + layoutBlockSupport: blockSupportAndThemeSettings + }), constrainedType && displayControlsForLegacyLayouts && (0,external_React_.createElement)(constrainedType.inspectorControls, { + layout: usedLayout, + onChange: onChangeLayout, + layoutBlockSupport: blockSupportAndThemeSettings + }))), !inherit && layoutType && (0,external_React_.createElement)(layoutType.toolBarControls, { + layout: usedLayout, + onChange: onChangeLayout, + layoutBlockSupport: layoutBlockSupport + })); +} +/* harmony default export */ const layout = ({ + shareWithChildBlocks: true, + edit: LayoutPanelPure, + attributeKeys: ['layout'], + hasSupport(name) { + return hasLayoutBlockSupport(name); + } +}); +function LayoutTypeSwitcher({ + type, + onChange +}) { + return (0,external_React_.createElement)(external_wp_components_namespaceObject.ButtonGroup, null, getLayoutTypes().map(({ + name, + label + }) => { + return (0,external_React_.createElement)(external_wp_components_namespaceObject.Button, { + key: name, + isPressed: type === name, + onClick: () => onChange(name) + }, label); + })); +} /** - * Internal dependencies + * Filters registered block settings, extending attributes to include `layout`. + * + * @param {Object} settings Original block settings. + * + * @return {Object} Filtered block settings. */ +function layout_addAttribute(settings) { + var _settings$attributes$; + if ('type' in ((_settings$attributes$ = settings.attributes?.layout) !== null && _settings$attributes$ !== void 0 ? _settings$attributes$ : {})) { + return settings; + } + if (hasLayoutBlockSupport(settings)) { + settings.attributes = { + ...settings.attributes, + layout: { + type: 'object' + } + }; + } + return settings; +} +function BlockWithLayoutStyles({ + block: BlockListBlock, + props +}) { + const { + name, + attributes + } = props; + const id = (0,external_wp_compose_namespaceObject.useInstanceId)(BlockListBlock); + const { + layout + } = attributes; + const { + default: defaultBlockLayout + } = (0,external_wp_blocks_namespaceObject.getBlockSupport)(name, layoutBlockSupportKey) || {}; + const usedLayout = layout?.inherit || layout?.contentSize || layout?.wideSize ? { + ...layout, + type: 'constrained' + } : layout || defaultBlockLayout || {}; + const layoutClasses = useLayoutClasses(attributes, name); + const { + kebabCase + } = unlock(external_wp_components_namespaceObject.privateApis); + const selectorPrefix = `wp-container-${kebabCase(name)}-is-layout-`; + // Higher specificity to override defaults from theme.json. + const selector = `.${selectorPrefix}${id}.${selectorPrefix}${id}`; + const [blockGapSupport] = use_settings_useSettings('spacing.blockGap'); + const hasBlockGapSupport = blockGapSupport !== null; + + // Get CSS string for the current layout type. + // The CSS and `style` element is only output if it is not empty. + const fullLayoutType = getLayoutType(usedLayout?.type || 'default'); + const css = fullLayoutType?.getLayoutStyle?.({ + blockName: name, + selector, + layout: usedLayout, + style: attributes?.style, + hasBlockGapSupport + }); + // Attach a `wp-container-` id-based class name as well as a layout class name such as `is-layout-flex`. + const layoutClassNames = classnames_default()({ + [`${selectorPrefix}${id}`]: !!css // Only attach a container class if there is generated CSS to be attached. + }, layoutClasses); + useStyleOverride({ + css + }); + return (0,external_React_.createElement)(BlockListBlock, { + ...props, + __unstableLayoutClassNames: layoutClassNames + }); +} /** - * Returns a context object for a given block. + * Override the default block element to add the layout styles. * - * @param {string} clientId The block client ID. + * @param {Function} BlockListBlock Original component. * - * @return {Record} Context value. + * @return {Function} Wrapped component. */ -function useBlockContext(clientId) { - return (0,external_wp_data_namespaceObject.useSelect)(select => { - const block = select(store).getBlock(clientId); - if (!block) { - return undefined; - } - const blockType = select(external_wp_blocks_namespaceObject.store).getBlockType(block.name); - if (!blockType) { - return undefined; - } - if (Object.keys(blockType.providesContext).length === 0) { - return undefined; +const withLayoutStyles = (0,external_wp_compose_namespaceObject.createHigherOrderComponent)(BlockListBlock => props => { + const blockSupportsLayout = hasLayoutBlockSupport(props.name); + const shouldRenderLayoutStyles = (0,external_wp_data_namespaceObject.useSelect)(select => { + // The callback returns early to avoid block editor subscription. + if (!blockSupportsLayout) { + return false; } - return Object.fromEntries(Object.entries(blockType.providesContext).map(([contextName, attributeName]) => [contextName, block.attributes[attributeName]])); - }, [clientId]); -} + return !select(store).getSettings().disableLayoutStyles; + }, [blockSupportsLayout]); + if (!shouldRenderLayoutStyles) { + return (0,external_React_.createElement)(BlockListBlock, { + ...props + }); + } + return (0,external_React_.createElement)(BlockWithLayoutStyles, { + block: BlockListBlock, + props: props + }); +}, 'withLayoutStyles'); +(0,external_wp_hooks_namespaceObject.addFilter)('blocks.registerBlockType', 'core/layout/addAttribute', layout_addAttribute); +(0,external_wp_hooks_namespaceObject.addFilter)('editor.BlockListBlock', 'core/editor/layout/with-layout-styles', withLayoutStyles); -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/use-on-block-drop/index.js +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/hooks/layout-child.js /** * WordPress dependencies */ - - /** * Internal dependencies */ -/** @typedef {import('@wordpress/element').WPSyntheticEvent} WPSyntheticEvent */ -/** @typedef {import('./types').WPDropOperation} WPDropOperation */ - -/** - * Retrieve the data for a block drop event. - * - * @param {WPSyntheticEvent} event The drop event. - * - * @return {Object} An object with block drag and drop data. - */ -function parseDropEvent(event) { - let result = { - srcRootClientId: null, - srcClientIds: null, - srcIndex: null, - type: null, - blocks: null - }; - if (!event.dataTransfer) { - return result; - } - try { - result = Object.assign(result, JSON.parse(event.dataTransfer.getData('wp-blocks'))); - } catch (err) { - return result; +function useBlockPropsChildLayoutStyles({ + style +}) { + var _style$layout; + const shouldRenderChildLayoutStyles = (0,external_wp_data_namespaceObject.useSelect)(select => { + return !select(store).getSettings().disableLayoutStyles; + }); + const layout = (_style$layout = style?.layout) !== null && _style$layout !== void 0 ? _style$layout : {}; + const { + selfStretch, + flexSize + } = layout; + const id = (0,external_wp_compose_namespaceObject.useInstanceId)(useBlockPropsChildLayoutStyles); + const selector = `.wp-container-content-${id}`; + let css = ''; + if (shouldRenderChildLayoutStyles) { + if (selfStretch === 'fixed' && flexSize) { + css = `${selector} { + flex-basis: ${flexSize}; + box-sizing: border-box; + }`; + } else if (selfStretch === 'fill') { + css = `${selector} { + flex-grow: 1; + }`; + } } - return result; -} + useStyleOverride({ + css + }); -/** - * A function that returns an event handler function for block drop events. - * - * @param {string} targetRootClientId The root client id where the block(s) will be inserted. - * @param {number} targetBlockIndex The index where the block(s) will be inserted. - * @param {Function} getBlockIndex A function that gets the index of a block. - * @param {Function} getClientIdsOfDescendants A function that gets the client ids of descendant blocks. - * @param {Function} moveBlocks A function that moves blocks. - * @param {Function} insertOrReplaceBlocks A function that inserts or replaces blocks. - * @param {Function} clearSelectedBlock A function that clears block selection. - * @return {Function} The event handler for a block drop event. - */ -function onBlockDrop(targetRootClientId, targetBlockIndex, getBlockIndex, getClientIdsOfDescendants, moveBlocks, insertOrReplaceBlocks, clearSelectedBlock) { - return event => { - const { - srcRootClientId: sourceRootClientId, - srcClientIds: sourceClientIds, - type: dropType, - blocks - } = parseDropEvent(event); + // Only attach a container class if there is generated CSS to be attached. + if (!css) { + return; + } - // If the user is inserting a block. - if (dropType === 'inserter') { - clearSelectedBlock(); - const blocksToInsert = blocks.map(block => (0,external_wp_blocks_namespaceObject.cloneBlock)(block)); - insertOrReplaceBlocks(blocksToInsert, true, null); - } + // Attach a `wp-container-content` id-based classname. + return { + className: `wp-container-content-${id}` + }; +} +/* harmony default export */ const layout_child = ({ + useBlockProps: useBlockPropsChildLayoutStyles, + attributeKeys: ['style'], + hasSupport() { + return true; + } +}); - // If the user is moving a block. - if (dropType === 'block') { - const sourceBlockIndex = getBlockIndex(sourceClientIds[0]); +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/convert-to-group-buttons/use-convert-to-group-button-props.js +/** + * WordPress dependencies + */ - // If the user is dropping to the same position, return early. - if (sourceRootClientId === targetRootClientId && sourceBlockIndex === targetBlockIndex) { - return; - } - // If the user is attempting to drop a block within its own - // nested blocks, return early as this would create infinite - // recursion. - if (sourceClientIds.includes(targetRootClientId) || getClientIdsOfDescendants(sourceClientIds).some(id => id === targetRootClientId)) { - return; - } - const isAtSameLevel = sourceRootClientId === targetRootClientId; - const draggedBlockCount = sourceClientIds.length; - // If the block is kept at the same level and moved downwards, - // subtract to take into account that the blocks being dragged - // were removed from the block list above the insertion point. - const insertIndex = isAtSameLevel && sourceBlockIndex < targetBlockIndex ? targetBlockIndex - draggedBlockCount : targetBlockIndex; - moveBlocks(sourceClientIds, sourceRootClientId, insertIndex); - } - }; -} +/** + * Internal dependencies + */ + /** - * A function that returns an event handler function for block-related file drop events. - * - * @param {string} targetRootClientId The root client id where the block(s) will be inserted. - * @param {number} targetBlockIndex The index where the block(s) will be inserted. - * @param {boolean} hasUploadPermissions Whether the user has upload permissions. - * @param {Function} updateBlockAttributes A function that updates a block's attributes. - * @param {Function} canInsertBlockType A function that returns checks whether a block type can be inserted. - * @param {Function} insertOrReplaceBlocks A function that inserts or replaces blocks. + * Contains the properties `ConvertToGroupButton` component needs. * - * @return {Function} The event handler for a block-related file drop event. + * @typedef {Object} ConvertToGroupButtonProps + * @property {string[]} clientIds An array of the selected client ids. + * @property {boolean} isGroupable Indicates if the selected blocks can be grouped. + * @property {boolean} isUngroupable Indicates if the selected blocks can be ungrouped. + * @property {WPBlock[]} blocksSelection An array of the selected blocks. + * @property {string} groupingBlockName The name of block used for handling grouping interactions. */ -function onFilesDrop(targetRootClientId, targetBlockIndex, hasUploadPermissions, updateBlockAttributes, canInsertBlockType, insertOrReplaceBlocks) { - return files => { - if (!hasUploadPermissions) { - return; - } - const transformation = (0,external_wp_blocks_namespaceObject.findTransform)((0,external_wp_blocks_namespaceObject.getBlockTransforms)('from'), transform => transform.type === 'files' && canInsertBlockType(transform.blockName, targetRootClientId) && transform.isMatch(files)); - if (transformation) { - const blocks = transformation.transform(files, updateBlockAttributes); - insertOrReplaceBlocks(blocks); - } - }; -} /** - * A function that returns an event handler function for block-related HTML drop events. + * Returns the properties `ConvertToGroupButton` component needs to work properly. + * It is used in `BlockSettingsMenuControls` to know if `ConvertToGroupButton` + * should be rendered, to avoid ending up with an empty MenuGroup. * - * @param {string} targetRootClientId The root client id where the block(s) will be inserted. - * @param {number} targetBlockIndex The index where the block(s) will be inserted. - * @param {Function} insertOrReplaceBlocks A function that inserts or replaces blocks. + * @param {?string[]} selectedClientIds An optional array of clientIds to group. The selected blocks + * from the block editor store are used if this is not provided. * - * @return {Function} The event handler for a block-related HTML drop event. + * @return {ConvertToGroupButtonProps} Returns the properties needed by `ConvertToGroupButton`. */ -function onHTMLDrop(targetRootClientId, targetBlockIndex, insertOrReplaceBlocks) { - return HTML => { - const blocks = (0,external_wp_blocks_namespaceObject.pasteHandler)({ - HTML, - mode: 'BLOCKS' - }); - if (blocks.length) { - insertOrReplaceBlocks(blocks); - } - }; +function useConvertToGroupButtonProps(selectedClientIds) { + return (0,external_wp_data_namespaceObject.useSelect)(select => { + const { + getBlocksByClientId, + getSelectedBlockClientIds, + isUngroupable, + isGroupable + } = select(store); + const { + getGroupingBlockName, + getBlockType + } = select(external_wp_blocks_namespaceObject.store); + const clientIds = selectedClientIds?.length ? selectedClientIds : getSelectedBlockClientIds(); + const blocksSelection = getBlocksByClientId(clientIds); + const [firstSelectedBlock] = blocksSelection; + const _isUngroupable = clientIds.length === 1 && isUngroupable(clientIds[0]); + return { + clientIds, + isGroupable: isGroupable(clientIds), + isUngroupable: _isUngroupable, + blocksSelection, + groupingBlockName: getGroupingBlockName(), + onUngroup: _isUngroupable && getBlockType(firstSelectedBlock.name)?.transforms?.ungroup + }; + }, [selectedClientIds]); } +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/convert-to-group-buttons/index.js + /** - * A React hook for handling block drop events. - * - * @param {string} targetRootClientId The root client id where the block(s) will be inserted. - * @param {number} targetBlockIndex The index where the block(s) will be inserted. - * @param {Object} options The optional options. - * @param {WPDropOperation} [options.operation] The type of operation to perform on drop. Could be `insert` or `replace` for now. - * - * @return {Function} A function to be passed to the onDrop handler. + * WordPress dependencies */ -function useOnBlockDrop(targetRootClientId, targetBlockIndex, options = {}) { - const { - operation = 'insert' - } = options; - const hasUploadPermissions = (0,external_wp_data_namespaceObject.useSelect)(select => select(store).getSettings().mediaUpload, []); - const { - canInsertBlockType, - getBlockIndex, - getClientIdsOfDescendants, - getBlockOrder, - getBlocksByClientId - } = (0,external_wp_data_namespaceObject.useSelect)(store); + + + + + +/** + * Internal dependencies + */ + + + +function ConvertToGroupButton({ + clientIds, + isGroupable, + isUngroupable, + onUngroup, + blocksSelection, + groupingBlockName, + onClose = () => {} +}) { const { - insertBlocks, - moveBlocksToPosition, - updateBlockAttributes, - clearSelectedBlock, - replaceBlocks, - removeBlocks + replaceBlocks } = (0,external_wp_data_namespaceObject.useDispatch)(store); - const registry = (0,external_wp_data_namespaceObject.useRegistry)(); - const insertOrReplaceBlocks = (0,external_wp_element_namespaceObject.useCallback)((blocks, updateSelection = true, initialPosition = 0) => { - if (operation === 'replace') { - const clientIds = getBlockOrder(targetRootClientId); - const clientId = clientIds[targetBlockIndex]; - replaceBlocks(clientId, blocks, undefined, initialPosition); - } else { - insertBlocks(blocks, targetBlockIndex, targetRootClientId, updateSelection, initialPosition); + const onConvertToGroup = () => { + // Activate the `transform` on the Grouping Block which does the conversion. + const newBlocks = (0,external_wp_blocks_namespaceObject.switchToBlockType)(blocksSelection, groupingBlockName); + if (newBlocks) { + replaceBlocks(clientIds, newBlocks); } - }, [operation, getBlockOrder, insertBlocks, replaceBlocks, targetBlockIndex, targetRootClientId]); - const moveBlocks = (0,external_wp_element_namespaceObject.useCallback)((sourceClientIds, sourceRootClientId, insertIndex) => { - if (operation === 'replace') { - const sourceBlocks = getBlocksByClientId(sourceClientIds); - const targetBlockClientIds = getBlockOrder(targetRootClientId); - const targetBlockClientId = targetBlockClientIds[targetBlockIndex]; - registry.batch(() => { - // Remove the source blocks. - removeBlocks(sourceClientIds, false); - // Replace the target block with the source blocks. - replaceBlocks(targetBlockClientId, sourceBlocks, undefined, 0); - }); - } else { - moveBlocksToPosition(sourceClientIds, sourceRootClientId, targetRootClientId, insertIndex); + }; + const onConvertFromGroup = () => { + let innerBlocks = blocksSelection[0].innerBlocks; + if (!innerBlocks.length) { + return; } - }, [operation, getBlockOrder, getBlocksByClientId, insertBlocks, moveBlocksToPosition, removeBlocks, targetBlockIndex, targetRootClientId]); - const _onDrop = onBlockDrop(targetRootClientId, targetBlockIndex, getBlockIndex, getClientIdsOfDescendants, moveBlocks, insertOrReplaceBlocks, clearSelectedBlock); - const _onFilesDrop = onFilesDrop(targetRootClientId, targetBlockIndex, hasUploadPermissions, updateBlockAttributes, canInsertBlockType, insertOrReplaceBlocks); - const _onHTMLDrop = onHTMLDrop(targetRootClientId, targetBlockIndex, insertOrReplaceBlocks); - return event => { - const files = (0,external_wp_dom_namespaceObject.getFilesFromDataTransfer)(event.dataTransfer); - const html = event.dataTransfer.getData('text/html'); - - /** - * From Windows Chrome 96, the `event.dataTransfer` returns both file object and HTML. - * The order of the checks is important to recognise the HTML drop. - */ - if (html) { - _onHTMLDrop(html); - } else if (files.length) { - _onFilesDrop(files); - } else { - _onDrop(event); + if (onUngroup) { + innerBlocks = onUngroup(blocksSelection[0].attributes, blocksSelection[0].innerBlocks); } + replaceBlocks(clientIds, innerBlocks); }; + if (!isGroupable && !isUngroupable) { + return null; + } + return (0,external_React_.createElement)(external_React_.Fragment, null, isGroupable && (0,external_React_.createElement)(external_wp_components_namespaceObject.MenuItem, { + onClick: () => { + onConvertToGroup(); + onClose(); + } + }, (0,external_wp_i18n_namespaceObject._x)('Group', 'verb')), isUngroupable && (0,external_React_.createElement)(external_wp_components_namespaceObject.MenuItem, { + onClick: () => { + onConvertFromGroup(); + onClose(); + } + }, (0,external_wp_i18n_namespaceObject._x)('Ungroup', 'Ungrouping blocks from within a grouping block back into individual blocks within the Editor '))); } -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/utils/math.js -/** - * A string representing the name of an edge. - * - * @typedef {'top'|'right'|'bottom'|'left'} WPEdgeName - */ +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/block-lock/use-block-lock.js /** - * @typedef {Object} WPPoint - * @property {number} x The horizontal position. - * @property {number} y The vertical position. + * WordPress dependencies */ + /** - * Given a point, a DOMRect and the name of an edge, returns the distance to - * that edge of the rect. - * - * This function works for edges that are horizontal or vertical (e.g. not - * rotated), the following terms are used so that the function works in both - * orientations: - * - * - Forward, meaning the axis running horizontally when an edge is vertical - * and vertically when an edge is horizontal. - * - Lateral, meaning the axis running vertically when an edge is vertical - * and horizontally when an edge is horizontal. - * - * @param {WPPoint} point The point to measure distance from. - * @param {DOMRect} rect A DOM Rect containing edge positions. - * @param {WPEdgeName} edge The edge to measure to. + * Internal dependencies */ -function getDistanceFromPointToEdge(point, rect, edge) { - const isHorizontal = edge === 'top' || edge === 'bottom'; - const { - x, - y - } = point; - const pointLateralPosition = isHorizontal ? x : y; - const pointForwardPosition = isHorizontal ? y : x; - const edgeStart = isHorizontal ? rect.left : rect.top; - const edgeEnd = isHorizontal ? rect.right : rect.bottom; - const edgeForwardPosition = rect[edge]; - // Measure the straight line distance to the edge of the rect, when the - // point is adjacent to the edge. - // Else, if the point is positioned diagonally to the edge of the rect, - // measure diagonally to the nearest corner that the edge meets. - let edgeLateralPosition; - if (pointLateralPosition >= edgeStart && pointLateralPosition <= edgeEnd) { - edgeLateralPosition = pointLateralPosition; - } else if (pointLateralPosition < edgeEnd) { - edgeLateralPosition = edgeStart; - } else { - edgeLateralPosition = edgeEnd; - } - return Math.sqrt((pointLateralPosition - edgeLateralPosition) ** 2 + (pointForwardPosition - edgeForwardPosition) ** 2); -} /** - * Given a point, a DOMRect and a list of allowed edges returns the name of and - * distance to the nearest edge. + * Return details about the block lock status. * - * @param {WPPoint} point The point to measure distance from. - * @param {DOMRect} rect A DOM Rect containing edge positions. - * @param {WPEdgeName[]} allowedEdges A list of the edges included in the - * calculation. Defaults to all edges. + * @param {string} clientId The block client Id. * - * @return {[number, string]} An array where the first value is the distance - * and a second is the edge name. + * @return {Object} Block lock status */ -function getDistanceToNearestEdge(point, rect, allowedEdges = ['top', 'bottom', 'left', 'right']) { - let candidateDistance; - let candidateEdge; - allowedEdges.forEach(edge => { - const distance = getDistanceFromPointToEdge(point, rect, edge); - if (candidateDistance === undefined || distance < candidateDistance) { - candidateDistance = distance; - candidateEdge = edge; - } - }); - return [candidateDistance, candidateEdge]; +function useBlockLock(clientId) { + return (0,external_wp_data_namespaceObject.useSelect)(select => { + const { + canEditBlock, + canMoveBlock, + canRemoveBlock, + canLockBlockType, + getBlockName, + getBlockRootClientId, + getTemplateLock + } = select(store); + const rootClientId = getBlockRootClientId(clientId); + const canEdit = canEditBlock(clientId); + const canMove = canMoveBlock(clientId, rootClientId); + const canRemove = canRemoveBlock(clientId, rootClientId); + return { + canEdit, + canMove, + canRemove, + canLock: canLockBlockType(getBlockName(clientId)), + isContentLocked: getTemplateLock(clientId) === 'contentOnly', + isLocked: !canEdit || !canMove || !canRemove + }; + }, [clientId]); } +;// CONCATENATED MODULE: ./node_modules/@wordpress/icons/build-module/library/unlock.js + /** - * Is the point contained by the rectangle. - * - * @param {WPPoint} point The point. - * @param {DOMRect} rect The rectangle. - * - * @return {boolean} True if the point is contained by the rectangle, false otherwise. + * WordPress dependencies */ -function isPointContainedByRect(point, rect) { - return rect.left <= point.x && rect.right >= point.x && rect.top <= point.y && rect.bottom >= point.y; -} -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/use-block-drop-zone/index.js +const unlock_unlock = (0,external_React_.createElement)(external_wp_primitives_namespaceObject.SVG, { + viewBox: "0 0 24 24", + xmlns: "http://www.w3.org/2000/svg" +}, (0,external_React_.createElement)(external_wp_primitives_namespaceObject.Path, { + d: "M17 10h-1.2V7c0-2.1-1.7-3.8-3.8-3.8-2.1 0-3.8 1.7-3.8 3.8h1.5c0-1.2 1-2.2 2.2-2.2s2.2 1 2.2 2.2v3H7c-.6 0-1 .4-1 1v8c0 .6.4 1 1 1h10c.6 0 1-.4 1-1v-8c0-.6-.4-1-1-1z" +})); +/* harmony default export */ const library_unlock = (unlock_unlock); + +;// CONCATENATED MODULE: ./node_modules/@wordpress/icons/build-module/library/lock-outline.js + /** * WordPress dependencies */ +const lockOutline = (0,external_React_.createElement)(external_wp_primitives_namespaceObject.SVG, { + viewBox: "0 0 24 24", + xmlns: "http://www.w3.org/2000/svg" +}, (0,external_React_.createElement)(external_wp_primitives_namespaceObject.Path, { + d: "M17 10h-1.2V7c0-2.1-1.7-3.8-3.8-3.8-2.1 0-3.8 1.7-3.8 3.8v3H7c-.6 0-1 .4-1 1v8c0 .6.4 1 1 1h10c.6 0 1-.4 1-1v-8c0-.6-.4-1-1-1zM9.8 7c0-1.2 1-2.2 2.2-2.2 1.2 0 2.2 1 2.2 2.2v3H9.8V7zm6.7 11.5h-9v-7h9v7z" +})); +/* harmony default export */ const lock_outline = (lockOutline); +;// CONCATENATED MODULE: ./node_modules/@wordpress/icons/build-module/library/lock.js +/** + * WordPress dependencies + */ +const lock_lock = (0,external_React_.createElement)(external_wp_primitives_namespaceObject.SVG, { + viewBox: "0 0 24 24", + xmlns: "http://www.w3.org/2000/svg" +}, (0,external_React_.createElement)(external_wp_primitives_namespaceObject.Path, { + d: "M17 10h-1.2V7c0-2.1-1.7-3.8-3.8-3.8-2.1 0-3.8 1.7-3.8 3.8v3H7c-.6 0-1 .4-1 1v8c0 .6.4 1 1 1h10c.6 0 1-.4 1-1v-8c0-.6-.4-1-1-1zm-2.8 0H9.8V7c0-1.2 1-2.2 2.2-2.2s2.2 1 2.2 2.2v3z" +})); +/* harmony default export */ const library_lock = (lock_lock); +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/block-lock/modal.js /** - * Internal dependencies + * WordPress dependencies */ -/** @typedef {import('../../utils/math').WPPoint} WPPoint */ -/** @typedef {import('../use-on-block-drop/types').WPDropOperation} WPDropOperation */ -/** - * The orientation of a block list. - * - * @typedef {'horizontal'|'vertical'|undefined} WPBlockListOrientation - */ -/** - * The insert position when dropping a block. - * - * @typedef {'before'|'after'} WPInsertPosition - */ -/** - * @typedef {Object} WPBlockData - * @property {boolean} isUnmodifiedDefaultBlock Is the block unmodified default block. - * @property {() => DOMRect} getBoundingClientRect Get the bounding client rect of the block. - * @property {number} blockIndex The index of the block. - */ /** - * Get the drop target position from a given drop point and the orientation. - * - * @param {WPBlockData[]} blocksData The block data list. - * @param {WPPoint} position The position of the item being dragged. - * @param {WPBlockListOrientation} orientation The orientation of the block list. - * @return {[number, WPDropOperation]} The drop target position. + * Internal dependencies */ -function getDropTargetPosition(blocksData, position, orientation = 'vertical') { - const allowedEdges = orientation === 'horizontal' ? ['left', 'right'] : ['top', 'bottom']; - const isRightToLeft = (0,external_wp_i18n_namespaceObject.isRTL)(); - let nearestIndex = 0; - let insertPosition = 'before'; - let minDistance = Infinity; - blocksData.forEach(({ - isUnmodifiedDefaultBlock, - getBoundingClientRect, - blockIndex - }) => { - const rect = getBoundingClientRect(); - let [distance, edge] = getDistanceToNearestEdge(position, rect, allowedEdges); - // Prioritize the element if the point is inside of an unmodified default block. - if (isUnmodifiedDefaultBlock && isPointContainedByRect(position, rect)) { - distance = 0; - } - if (distance < minDistance) { - // Where the dropped block will be inserted on the nearest block. - insertPosition = edge === 'bottom' || !isRightToLeft && edge === 'right' || isRightToLeft && edge === 'left' ? 'after' : 'before'; - // Update the currently known best candidate. - minDistance = distance; - nearestIndex = blockIndex; - } - }); - const adjacentIndex = nearestIndex + (insertPosition === 'after' ? 1 : -1); - const isNearestBlockUnmodifiedDefaultBlock = !!blocksData[nearestIndex]?.isUnmodifiedDefaultBlock; - const isAdjacentBlockUnmodifiedDefaultBlock = !!blocksData[adjacentIndex]?.isUnmodifiedDefaultBlock; - // If both blocks are not unmodified default blocks then just insert between them. - if (!isNearestBlockUnmodifiedDefaultBlock && !isAdjacentBlockUnmodifiedDefaultBlock) { - // If the user is dropping to the trailing edge of the block - // add 1 to the index to represent dragging after. - const insertionIndex = insertPosition === 'after' ? nearestIndex + 1 : nearestIndex; - return [insertionIndex, 'insert']; + + +// Entity based blocks which allow edit locking +const ALLOWS_EDIT_LOCKING = ['core/block', 'core/navigation']; +function getTemplateLockValue(lock) { + // Prevents all operations. + if (lock.remove && lock.move) { + return 'all'; } - // Otherwise, replace the nearest unmodified default block. - return [isNearestBlockUnmodifiedDefaultBlock ? nearestIndex : adjacentIndex, 'replace']; + // Prevents inserting or removing blocks, but allows moving existing blocks. + if (lock.remove && !lock.move) { + return 'insert'; + } + return false; +} +function BlockLockModal({ + clientId, + onClose +}) { + const [lock, setLock] = (0,external_wp_element_namespaceObject.useState)({ + move: false, + remove: false + }); + const { + canEdit, + canMove, + canRemove + } = useBlockLock(clientId); + const { + allowsEditLocking, + templateLock, + hasTemplateLock + } = (0,external_wp_data_namespaceObject.useSelect)(select => { + const { + getBlockName, + getBlockAttributes + } = select(store); + const blockName = getBlockName(clientId); + const blockType = (0,external_wp_blocks_namespaceObject.getBlockType)(blockName); + return { + allowsEditLocking: ALLOWS_EDIT_LOCKING.includes(blockName), + templateLock: getBlockAttributes(clientId)?.templateLock, + hasTemplateLock: !!blockType?.attributes?.templateLock + }; + }, [clientId]); + const [applyTemplateLock, setApplyTemplateLock] = (0,external_wp_element_namespaceObject.useState)(!!templateLock); + const { + updateBlockAttributes + } = (0,external_wp_data_namespaceObject.useDispatch)(store); + const blockInformation = useBlockDisplayInformation(clientId); + const instanceId = (0,external_wp_compose_namespaceObject.useInstanceId)(BlockLockModal, 'block-editor-block-lock-modal__options-title'); + (0,external_wp_element_namespaceObject.useEffect)(() => { + setLock({ + move: !canMove, + remove: !canRemove, + ...(allowsEditLocking ? { + edit: !canEdit + } : {}) + }); + }, [canEdit, canMove, canRemove, allowsEditLocking]); + const isAllChecked = Object.values(lock).every(Boolean); + const isMixed = Object.values(lock).some(Boolean) && !isAllChecked; + return (0,external_React_.createElement)(external_wp_components_namespaceObject.Modal, { + title: (0,external_wp_i18n_namespaceObject.sprintf)( /* translators: %s: Name of the block. */ + (0,external_wp_i18n_namespaceObject.__)('Lock %s'), blockInformation.title), + overlayClassName: "block-editor-block-lock-modal", + onRequestClose: onClose + }, (0,external_React_.createElement)("p", null, (0,external_wp_i18n_namespaceObject.__)('Choose specific attributes to restrict or lock all available options.')), (0,external_React_.createElement)("form", { + onSubmit: event => { + event.preventDefault(); + updateBlockAttributes([clientId], { + lock, + templateLock: applyTemplateLock ? getTemplateLockValue(lock) : undefined + }); + onClose(); + } + }, (0,external_React_.createElement)("div", { + role: "group", + "aria-labelledby": instanceId, + className: "block-editor-block-lock-modal__options" + }, (0,external_React_.createElement)(external_wp_components_namespaceObject.CheckboxControl, { + __nextHasNoMarginBottom: true, + className: "block-editor-block-lock-modal__options-title", + label: (0,external_React_.createElement)("span", { + id: instanceId + }, (0,external_wp_i18n_namespaceObject.__)('Lock all')), + checked: isAllChecked, + indeterminate: isMixed, + onChange: newValue => setLock({ + move: newValue, + remove: newValue, + ...(allowsEditLocking ? { + edit: newValue + } : {}) + }) + }), (0,external_React_.createElement)("ul", { + className: "block-editor-block-lock-modal__checklist" + }, allowsEditLocking && (0,external_React_.createElement)("li", { + className: "block-editor-block-lock-modal__checklist-item" + }, (0,external_React_.createElement)(external_wp_components_namespaceObject.CheckboxControl, { + __nextHasNoMarginBottom: true, + label: (0,external_wp_i18n_namespaceObject.__)('Restrict editing'), + checked: !!lock.edit, + onChange: edit => setLock(prevLock => ({ + ...prevLock, + edit + })) + }), (0,external_React_.createElement)(external_wp_components_namespaceObject.Icon, { + className: "block-editor-block-lock-modal__lock-icon", + icon: lock.edit ? library_lock : library_unlock + })), (0,external_React_.createElement)("li", { + className: "block-editor-block-lock-modal__checklist-item" + }, (0,external_React_.createElement)(external_wp_components_namespaceObject.CheckboxControl, { + __nextHasNoMarginBottom: true, + label: (0,external_wp_i18n_namespaceObject.__)('Disable movement'), + checked: lock.move, + onChange: move => setLock(prevLock => ({ + ...prevLock, + move + })) + }), (0,external_React_.createElement)(external_wp_components_namespaceObject.Icon, { + className: "block-editor-block-lock-modal__lock-icon", + icon: lock.move ? library_lock : library_unlock + })), (0,external_React_.createElement)("li", { + className: "block-editor-block-lock-modal__checklist-item" + }, (0,external_React_.createElement)(external_wp_components_namespaceObject.CheckboxControl, { + __nextHasNoMarginBottom: true, + label: (0,external_wp_i18n_namespaceObject.__)('Prevent removal'), + checked: lock.remove, + onChange: remove => setLock(prevLock => ({ + ...prevLock, + remove + })) + }), (0,external_React_.createElement)(external_wp_components_namespaceObject.Icon, { + className: "block-editor-block-lock-modal__lock-icon", + icon: lock.remove ? library_lock : library_unlock + }))), hasTemplateLock && (0,external_React_.createElement)(external_wp_components_namespaceObject.ToggleControl, { + __nextHasNoMarginBottom: true, + className: "block-editor-block-lock-modal__template-lock", + label: (0,external_wp_i18n_namespaceObject.__)('Apply to all blocks inside'), + checked: applyTemplateLock, + disabled: lock.move && !lock.remove, + onChange: () => setApplyTemplateLock(!applyTemplateLock) + })), (0,external_React_.createElement)(external_wp_components_namespaceObject.Flex, { + className: "block-editor-block-lock-modal__actions", + justify: "flex-end", + expanded: false + }, (0,external_React_.createElement)(external_wp_components_namespaceObject.FlexItem, null, (0,external_React_.createElement)(external_wp_components_namespaceObject.Button, { + variant: "tertiary", + onClick: onClose + }, (0,external_wp_i18n_namespaceObject.__)('Cancel'))), (0,external_React_.createElement)(external_wp_components_namespaceObject.FlexItem, null, (0,external_React_.createElement)(external_wp_components_namespaceObject.Button, { + variant: "primary", + type: "submit" + }, (0,external_wp_i18n_namespaceObject.__)('Apply')))))); } -/** - * @typedef {Object} WPBlockDropZoneConfig - * @property {string} rootClientId The root client id for the block list. - */ +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/block-lock/menu-item.js /** - * A React hook that can be used to make a block list handle drag and drop. - * - * @param {WPBlockDropZoneConfig} dropZoneConfig configuration data for the drop zone. + * WordPress dependencies */ -function useBlockDropZone({ - // An undefined value represents a top-level block. Default to an empty - // string for this so that `targetRootClientId` can be easily compared to - // values returned by the `getRootBlockClientId` selector, which also uses - // an empty string to represent top-level blocks. - rootClientId: targetRootClientId = '' -} = {}) { - const registry = (0,external_wp_data_namespaceObject.useRegistry)(); - const [dropTarget, setDropTarget] = (0,external_wp_element_namespaceObject.useState)({ - index: null, - operation: 'insert' - }); - const isDisabled = (0,external_wp_data_namespaceObject.useSelect)(select => { - const { - __unstableIsWithinBlockOverlay, - __unstableHasActiveBlockOverlayActive, - getBlockEditingMode - } = select(store); - const blockEditingMode = getBlockEditingMode(targetRootClientId); - return blockEditingMode !== 'default' || __unstableHasActiveBlockOverlayActive(targetRootClientId) || __unstableIsWithinBlockOverlay(targetRootClientId); - }, [targetRootClientId]); - const { - getBlockListSettings, - getBlocks, - getBlockIndex - } = (0,external_wp_data_namespaceObject.useSelect)(store); - const { - showInsertionPoint, - hideInsertionPoint - } = (0,external_wp_data_namespaceObject.useDispatch)(store); - const onBlockDrop = useOnBlockDrop(targetRootClientId, dropTarget.index, { - operation: dropTarget.operation - }); - const throttled = (0,external_wp_compose_namespaceObject.useThrottle)((0,external_wp_element_namespaceObject.useCallback)((event, ownerDocument) => { - const blocks = getBlocks(targetRootClientId); - // The block list is empty, don't show the insertion point but still allow dropping. - if (blocks.length === 0) { - registry.batch(() => { - setDropTarget({ - index: 0, - operation: 'insert' - }); - showInsertionPoint(targetRootClientId, 0, { - operation: 'insert' - }); - }); - return; - } - const blocksData = blocks.map(block => { - const clientId = block.clientId; - return { - isUnmodifiedDefaultBlock: (0,external_wp_blocks_namespaceObject.isUnmodifiedDefaultBlock)(block), - getBoundingClientRect: () => ownerDocument.getElementById(`block-${clientId}`).getBoundingClientRect(), - blockIndex: getBlockIndex(clientId) - }; - }); - const [targetIndex, operation] = getDropTargetPosition(blocksData, { - x: event.clientX, - y: event.clientY - }, getBlockListSettings(targetRootClientId)?.orientation); - registry.batch(() => { - setDropTarget({ - index: targetIndex, - operation - }); - showInsertionPoint(targetRootClientId, targetIndex, { - operation - }); - }); - }, [getBlocks, targetRootClientId, getBlockListSettings, registry, showInsertionPoint, getBlockIndex]), 200); - return (0,external_wp_compose_namespaceObject.__experimentalUseDropZone)({ - isDisabled, - onDrop: onBlockDrop, - onDragOver(event) { - // `currentTarget` is only available while the event is being - // handled, so get it now and pass it to the thottled function. - // https://developer.mozilla.org/en-US/docs/Web/API/Event/currentTarget - throttled(event, event.currentTarget.ownerDocument); - }, - onDragLeave() { - throttled.cancel(); - hideInsertionPoint(); - }, - onDragEnd() { - throttled.cancel(); - hideInsertionPoint(); - } - }); -} -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/inner-blocks/index.js + + /** - * External dependencies + * Internal dependencies */ +function BlockLockMenuItem({ + clientId +}) { + const { + canLock, + isLocked + } = useBlockLock(clientId); + const [isModalOpen, toggleModal] = (0,external_wp_element_namespaceObject.useReducer)(isActive => !isActive, false); + if (!canLock) { + return null; + } + const label = isLocked ? (0,external_wp_i18n_namespaceObject.__)('Unlock') : (0,external_wp_i18n_namespaceObject.__)('Lock'); + return (0,external_React_.createElement)(external_React_.Fragment, null, (0,external_React_.createElement)(external_wp_components_namespaceObject.MenuItem, { + icon: isLocked ? library_unlock : lock_outline, + onClick: toggleModal, + "aria-expanded": isModalOpen, + "aria-haspopup": "dialog" + }, label), isModalOpen && (0,external_React_.createElement)(BlockLockModal, { + clientId: clientId, + onClose: toggleModal + })); +} + +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/block-settings-menu/block-mode-toggle.js + /** * WordPress dependencies */ @@ -27993,195 +33455,219 @@ function useBlockDropZone({ + /** * Internal dependencies */ +const block_mode_toggle_noop = () => {}; +function BlockModeToggle({ + blockType, + mode, + onToggleMode, + small = false, + isCodeEditingEnabled = true +}) { + if (!blockType || !(0,external_wp_blocks_namespaceObject.hasBlockSupport)(blockType, 'html', true) || !isCodeEditingEnabled) { + return null; + } + const label = mode === 'visual' ? (0,external_wp_i18n_namespaceObject.__)('Edit as HTML') : (0,external_wp_i18n_namespaceObject.__)('Edit visually'); + return (0,external_React_.createElement)(external_wp_components_namespaceObject.MenuItem, { + onClick: onToggleMode + }, !small && label); +} +/* harmony default export */ const block_mode_toggle = ((0,external_wp_compose_namespaceObject.compose)([(0,external_wp_data_namespaceObject.withSelect)((select, { + clientId +}) => { + const { + getBlock, + getBlockMode, + getSettings + } = select(store); + const block = getBlock(clientId); + const isCodeEditingEnabled = getSettings().codeEditingEnabled; + return { + mode: getBlockMode(clientId), + blockType: block ? (0,external_wp_blocks_namespaceObject.getBlockType)(block.name) : null, + isCodeEditingEnabled + }; +}), (0,external_wp_data_namespaceObject.withDispatch)((dispatch, { + onToggle = block_mode_toggle_noop, + clientId +}) => ({ + onToggleMode() { + dispatch(store).toggleBlockMode(clientId); + onToggle(); + } +}))])(BlockModeToggle)); +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/block-rename/use-block-rename.js +/** + * WordPress dependencies + */ +function useBlockRename(name) { + return { + canRename: (0,external_wp_blocks_namespaceObject.getBlockSupport)(name, 'renaming', true) + }; +} +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/block-rename/is-empty-string.js +function isEmptyString(testString) { + return testString?.trim()?.length === 0; +} +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/block-rename/modal.js +/** + * WordPress dependencies + */ -const EMPTY_OBJECT = {}; - /** - * InnerBlocks is a component which allows a single block to have multiple blocks - * as children. The UncontrolledInnerBlocks component is used whenever the inner - * blocks are not controlled by another entity. In other words, it is normally - * used for inner blocks in the post editor - * - * @param {Object} props The component props. + * Internal dependencies */ -function UncontrolledInnerBlocks(props) { - const { - clientId, - allowedBlocks, - prioritizedInserterBlocks, - defaultBlock, - directInsert, - __experimentalDefaultBlock, - __experimentalDirectInsert, - template, - templateLock, - wrapperRef, - templateInsertUpdatesSelection, - __experimentalCaptureToolbars: captureToolbars, - __experimentalAppenderTagName, - renderAppender, - orientation, - placeholder, - layout - } = props; - useNestedSettingsUpdate(clientId, allowedBlocks, prioritizedInserterBlocks, defaultBlock, directInsert, __experimentalDefaultBlock, __experimentalDirectInsert, templateLock, captureToolbars, orientation, layout); - useInnerBlockTemplateSync(clientId, template, templateLock, templateInsertUpdatesSelection); - const context = useBlockContext(clientId); - const name = (0,external_wp_data_namespaceObject.useSelect)(select => { - return select(store).getBlock(clientId)?.name; - }, [clientId]); - const defaultLayoutBlockSupport = (0,external_wp_blocks_namespaceObject.getBlockSupport)(name, 'layout') || (0,external_wp_blocks_namespaceObject.getBlockSupport)(name, '__experimentalLayout') || EMPTY_OBJECT; - const { - allowSizingOnChildren = false - } = defaultLayoutBlockSupport; - const defaultLayout = use_setting_useSetting('layout') || EMPTY_OBJECT; - const usedLayout = layout || defaultLayoutBlockSupport; - const memoedLayout = (0,external_wp_element_namespaceObject.useMemo)(() => ({ - // Default layout will know about any content/wide size defined by the theme. - ...defaultLayout, - ...usedLayout, - ...(allowSizingOnChildren && { - allowSizingOnChildren: true - }) - }), [defaultLayout, usedLayout, allowSizingOnChildren]); - // This component needs to always be synchronous as it's the one changing - // the async mode depending on the block selection. - return (0,external_wp_element_namespaceObject.createElement)(BlockContextProvider, { - value: context - }, (0,external_wp_element_namespaceObject.createElement)(BlockListItems, { - rootClientId: clientId, - renderAppender: renderAppender, - __experimentalAppenderTagName: __experimentalAppenderTagName, - layout: memoedLayout, - wrapperRef: wrapperRef, - placeholder: placeholder - })); +function BlockRenameModal({ + blockName, + originalBlockName, + onClose, + onSave +}) { + const [editedBlockName, setEditedBlockName] = (0,external_wp_element_namespaceObject.useState)(blockName); + const nameHasChanged = editedBlockName !== blockName; + const nameIsOriginal = editedBlockName === originalBlockName; + const nameIsEmpty = isEmptyString(editedBlockName); + const isNameValid = nameHasChanged || nameIsOriginal; + const autoSelectInputText = event => event.target.select(); + const dialogDescription = (0,external_wp_compose_namespaceObject.useInstanceId)(BlockRenameModal, `block-editor-rename-modal__description`); + const handleSubmit = () => { + const message = nameIsOriginal || nameIsEmpty ? (0,external_wp_i18n_namespaceObject.sprintf)( /* translators: %s: new name/label for the block */ + (0,external_wp_i18n_namespaceObject.__)('Block name reset to: "%s".'), editedBlockName) : (0,external_wp_i18n_namespaceObject.sprintf)( /* translators: %s: new name/label for the block */ + (0,external_wp_i18n_namespaceObject.__)('Block name changed to: "%s".'), editedBlockName); + + // Must be assertive to immediately announce change. + (0,external_wp_a11y_namespaceObject.speak)(message, 'assertive'); + onSave(editedBlockName); + + // Immediate close avoids ability to hit save multiple times. + onClose(); + }; + return (0,external_React_.createElement)(external_wp_components_namespaceObject.Modal, { + title: (0,external_wp_i18n_namespaceObject.__)('Rename'), + onRequestClose: onClose, + overlayClassName: "block-editor-block-rename-modal", + aria: { + describedby: dialogDescription + }, + focusOnMount: "firstContentElement" + }, (0,external_React_.createElement)("p", { + id: dialogDescription + }, (0,external_wp_i18n_namespaceObject.__)('Enter a custom name for this block.')), (0,external_React_.createElement)("form", { + onSubmit: e => { + e.preventDefault(); + if (!isNameValid) { + return; + } + handleSubmit(); + } + }, (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalVStack, { + spacing: "3" + }, (0,external_React_.createElement)(external_wp_components_namespaceObject.TextControl, { + __nextHasNoMarginBottom: true, + __next40pxDefaultSize: true, + value: editedBlockName, + label: (0,external_wp_i18n_namespaceObject.__)('Block name'), + hideLabelFromVision: true, + placeholder: originalBlockName, + onChange: setEditedBlockName, + onFocus: autoSelectInputText + }), (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalHStack, { + justify: "right" + }, (0,external_React_.createElement)(external_wp_components_namespaceObject.Button, { + __next40pxDefaultSize: true, + variant: "tertiary", + onClick: onClose + }, (0,external_wp_i18n_namespaceObject.__)('Cancel')), (0,external_React_.createElement)(external_wp_components_namespaceObject.Button, { + __next40pxDefaultSize: true, + "aria-disabled": !isNameValid, + variant: "primary", + type: "submit" + }, (0,external_wp_i18n_namespaceObject.__)('Save')))))); } +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/block-rename/rename-control.js + /** - * The controlled inner blocks component wraps the uncontrolled inner blocks - * component with the blockSync hook. This keeps the innerBlocks of the block in - * the block-editor store in sync with the blocks of the controlling entity. An - * example of an inner block controller is a template part block, which provides - * its own blocks from the template part entity data source. - * - * @param {Object} props The component props. + * WordPress dependencies */ -function ControlledInnerBlocks(props) { - useBlockSync(props); - return (0,external_wp_element_namespaceObject.createElement)(UncontrolledInnerBlocks, { - ...props - }); -} -const ForwardedInnerBlocks = (0,external_wp_element_namespaceObject.forwardRef)((props, ref) => { - const innerBlocksProps = useInnerBlocksProps({ - ref - }, props); - return (0,external_wp_element_namespaceObject.createElement)("div", { - className: "block-editor-inner-blocks" - }, (0,external_wp_element_namespaceObject.createElement)("div", { - ...innerBlocksProps - })); -}); + + + + /** - * This hook is used to lightly mark an element as an inner blocks wrapper - * element. Call this hook and pass the returned props to the element to mark as - * an inner blocks wrapper, automatically rendering inner blocks as children. If - * you define a ref for the element, it is important to pass the ref to this - * hook, which the hook in turn will pass to the component through the props it - * returns. Optionally, you can also pass any other props through this hook, and - * they will be merged and returned. - * - * @param {Object} props Optional. Props to pass to the element. Must contain - * the ref if one is defined. - * @param {Object} options Optional. Inner blocks options. - * - * @see https://github.com/WordPress/gutenberg/blob/HEAD/packages/block-editor/src/components/inner-blocks/README.md + * Internal dependencies */ -function useInnerBlocksProps(props = {}, options = {}) { - const { - __unstableDisableLayoutClassNames, - __unstableDisableDropZone - } = options; - const { - clientId, - layout = null, - __unstableLayoutClassNames: layoutClassNames = '' - } = useBlockEditContext(); - const isSmallScreen = (0,external_wp_compose_namespaceObject.useViewportMatch)('medium', '<'); + + + + +function BlockRenameControl({ + clientId +}) { + const [renamingBlock, setRenamingBlock] = (0,external_wp_element_namespaceObject.useState)(false); const { - __experimentalCaptureToolbars, - hasOverlay + metadata } = (0,external_wp_data_namespaceObject.useSelect)(select => { - if (!clientId) { - return {}; - } const { - getBlockName, - isBlockSelected, - hasSelectedInnerBlock, - __unstableGetEditorMode + getBlockAttributes } = select(store); - const blockName = getBlockName(clientId); - const enableClickThrough = __unstableGetEditorMode() === 'navigation' || isSmallScreen; + const _metadata = getBlockAttributes(clientId)?.metadata; return { - __experimentalCaptureToolbars: select(external_wp_blocks_namespaceObject.store).hasBlockSupport(blockName, '__experimentalExposeControlsToChildren', false), - hasOverlay: blockName !== 'core/template' && !isBlockSelected(clientId) && !hasSelectedInnerBlock(clientId, true) && enableClickThrough + metadata: _metadata }; - }, [clientId, isSmallScreen]); - const blockDropZoneRef = useBlockDropZone({ - rootClientId: clientId - }); - const ref = (0,external_wp_compose_namespaceObject.useMergeRefs)([props.ref, __unstableDisableDropZone ? null : blockDropZoneRef]); - const innerBlocksProps = { - __experimentalCaptureToolbars, - layout, - ...options - }; - const InnerBlocks = innerBlocksProps.value && innerBlocksProps.onChange ? ControlledInnerBlocks : UncontrolledInnerBlocks; - return { - ...props, - ref, - className: classnames_default()(props.className, 'block-editor-block-list__layout', __unstableDisableLayoutClassNames ? '' : layoutClassNames, { - 'has-overlay': hasOverlay - }), - children: clientId ? (0,external_wp_element_namespaceObject.createElement)(InnerBlocks, { - ...innerBlocksProps, - clientId: clientId - }) : (0,external_wp_element_namespaceObject.createElement)(BlockListItems, { - ...options - }) - }; + }, [clientId]); + const { + updateBlockAttributes + } = (0,external_wp_data_namespaceObject.useDispatch)(store); + const customName = metadata?.name; + function onChange(newName) { + updateBlockAttributes([clientId], { + metadata: { + ...(metadata && metadata), + name: newName + } + }); + } + const blockInformation = useBlockDisplayInformation(clientId); + return (0,external_React_.createElement)(external_React_.Fragment, null, (0,external_React_.createElement)(external_wp_components_namespaceObject.MenuItem, { + onClick: () => { + setRenamingBlock(true); + }, + "aria-expanded": renamingBlock, + "aria-haspopup": "dialog" + }, (0,external_wp_i18n_namespaceObject.__)('Rename')), renamingBlock && (0,external_React_.createElement)(BlockRenameModal, { + blockName: customName || '', + originalBlockName: blockInformation?.title, + onClose: () => setRenamingBlock(false), + onSave: newName => { + // If the new value is the block's original name (e.g. `Group`) + // or it is an empty string then assume the intent is to reset + // the value. Therefore reset the metadata. + if (newName === blockInformation?.title || isEmptyString(newName)) { + newName = undefined; + } + onChange(newName); + } + })); } -useInnerBlocksProps.save = external_wp_blocks_namespaceObject.__unstableGetInnerBlocksProps; - -// Expose default appender placeholders as components. -ForwardedInnerBlocks.DefaultBlockAppender = inner_blocks_default_block_appender; -ForwardedInnerBlocks.ButtonBlockAppender = inner_blocks_button_block_appender; -ForwardedInnerBlocks.Content = () => useInnerBlocksProps.save().children; - -/** - * @see https://github.com/WordPress/gutenberg/blob/HEAD/packages/block-editor/src/components/inner-blocks/README.md - */ -/* harmony default export */ var inner_blocks = (ForwardedInnerBlocks); -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/observe-typing/index.js +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/block-settings-menu-controls/index.js /** * WordPress dependencies @@ -28196,225 +33682,239 @@ ForwardedInnerBlocks.Content = () => useInnerBlocksProps.save().children; */ -/** - * Set of key codes upon which typing is to be initiated on a keydown event. - * - * @type {Set } - */ -const KEY_DOWN_ELIGIBLE_KEY_CODES = new Set([external_wp_keycodes_namespaceObject.UP, external_wp_keycodes_namespaceObject.RIGHT, external_wp_keycodes_namespaceObject.DOWN, external_wp_keycodes_namespaceObject.LEFT, external_wp_keycodes_namespaceObject.ENTER, external_wp_keycodes_namespaceObject.BACKSPACE]); -/** - * Returns true if a given keydown event can be inferred as intent to start - * typing, or false otherwise. A keydown is considered eligible if it is a - * text navigation without shift active. - * - * @param {KeyboardEvent} event Keydown event to test. - * - * @return {boolean} Whether event is eligible to start typing. - */ -function isKeyDownEligibleForStartTyping(event) { + + +const { + Fill, + Slot +} = (0,external_wp_components_namespaceObject.createSlotFill)('BlockSettingsMenuControls'); +const BlockSettingsMenuControlsSlot = ({ + fillProps, + clientIds = null, + __unstableDisplayLocation +}) => { const { - keyCode, - shiftKey - } = event; - return !shiftKey && KEY_DOWN_ELIGIBLE_KEY_CODES.has(keyCode); + selectedBlocks, + selectedClientIds + } = (0,external_wp_data_namespaceObject.useSelect)(select => { + const { + getBlockNamesByClientId, + getSelectedBlockClientIds + } = select(store); + const ids = clientIds !== null ? clientIds : getSelectedBlockClientIds(); + return { + selectedBlocks: getBlockNamesByClientId(ids), + selectedClientIds: ids + }; + }, [clientIds]); + const { + canLock + } = useBlockLock(selectedClientIds[0]); + const { + canRename + } = useBlockRename(selectedBlocks[0]); + const showLockButton = selectedClientIds.length === 1 && canLock; + const showRenameButton = selectedClientIds.length === 1 && canRename; + + // Check if current selection of blocks is Groupable or Ungroupable + // and pass this props down to ConvertToGroupButton. + const convertToGroupButtonProps = useConvertToGroupButtonProps(selectedClientIds); + const { + isGroupable, + isUngroupable + } = convertToGroupButtonProps; + const showConvertToGroupButton = isGroupable || isUngroupable; + return (0,external_React_.createElement)(Slot, { + fillProps: { + ...fillProps, + __unstableDisplayLocation, + selectedBlocks, + selectedClientIds + } + }, fills => { + if (!fills?.length > 0 && !showConvertToGroupButton && !showLockButton) { + return null; + } + return (0,external_React_.createElement)(external_wp_components_namespaceObject.MenuGroup, null, showConvertToGroupButton && (0,external_React_.createElement)(ConvertToGroupButton, { + ...convertToGroupButtonProps, + onClose: fillProps?.onClose + }), showLockButton && (0,external_React_.createElement)(BlockLockMenuItem, { + clientId: selectedClientIds[0] + }), showRenameButton && (0,external_React_.createElement)(BlockRenameControl, { + clientId: selectedClientIds[0] + }), fills, fillProps?.canMove && !fillProps?.onlyBlock && (0,external_React_.createElement)(external_wp_components_namespaceObject.MenuItem, { + onClick: (0,external_wp_compose_namespaceObject.pipe)(fillProps?.onClose, fillProps?.onMoveTo) + }, (0,external_wp_i18n_namespaceObject.__)('Move to')), fillProps?.count === 1 && (0,external_React_.createElement)(block_mode_toggle, { + clientId: fillProps?.firstBlockClientId, + onToggle: fillProps?.onClose + })); + }); +}; + +/** + * @see https://github.com/WordPress/gutenberg/blob/HEAD/packages/block-editor/src/components/block-settings-menu-controls/README.md + * + * @param {Object} props Fill props. + * @return {Element} Element. + */ +function BlockSettingsMenuControls({ + ...props +}) { + return (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalStyleProvider, { + document: document + }, (0,external_React_.createElement)(Fill, { + ...props + })); } +BlockSettingsMenuControls.Slot = BlockSettingsMenuControlsSlot; +/* harmony default export */ const block_settings_menu_controls = (BlockSettingsMenuControls); + +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/hooks/content-lock-ui.js /** - * Removes the `isTyping` flag when the mouse moves in the document of the given - * element. + * WordPress dependencies */ -function useMouseMoveTypingReset() { - const isTyping = (0,external_wp_data_namespaceObject.useSelect)(select => select(store).isTyping(), []); - const { - stopTyping - } = (0,external_wp_data_namespaceObject.useDispatch)(store); - return (0,external_wp_compose_namespaceObject.useRefEffect)(node => { - if (!isTyping) { - return; - } - const { - ownerDocument - } = node; - let lastClientX; - let lastClientY; - /** - * On mouse move, unset typing flag if user has moved cursor. - * - * @param {MouseEvent} event Mousemove event. - */ - function stopTypingOnMouseMove(event) { - const { - clientX, - clientY - } = event; - // We need to check that the mouse really moved because Safari - // triggers mousemove events when shift or ctrl are pressed. - if (lastClientX && lastClientY && (lastClientX !== clientX || lastClientY !== clientY)) { - stopTyping(); - } - lastClientX = clientX; - lastClientY = clientY; - } - ownerDocument.addEventListener('mousemove', stopTypingOnMouseMove); - return () => { - ownerDocument.removeEventListener('mousemove', stopTypingOnMouseMove); - }; - }, [isTyping, stopTyping]); -} + + /** - * Sets and removes the `isTyping` flag based on user actions: - * - * - Sets the flag if the user types within the given element. - * - Removes the flag when the user selects some text, focusses a non-text - * field, presses ESC or TAB, or moves the mouse in the document. + * Internal dependencies */ -function useTypingObserver() { + + + + +// The implementation of content locking is mainly in this file, although the mechanism +// to stop temporarily editing as blocks when an outside block is selected is on component StopEditingAsBlocksOnOutsideSelect +// at block-editor/src/components/block-list/index.js. +// Besides the components on this file and the file referenced above the implementation +// also includes artifacts on the store (actions, reducers, and selector). + +function ContentLockControlsPure({ + clientId, + isSelected +}) { const { - isTyping, - hasInlineToolbar + getBlockListSettings, + getSettings + } = (0,external_wp_data_namespaceObject.useSelect)(store); + const { + templateLock, + isLockedByParent, + isEditingAsBlocks } = (0,external_wp_data_namespaceObject.useSelect)(select => { const { - isTyping: _isTyping, - getSettings + __unstableGetContentLockingParent, + getTemplateLock, + __unstableGetTemporarilyEditingAsBlocks } = select(store); return { - isTyping: _isTyping(), - hasInlineToolbar: getSettings().hasInlineToolbar + templateLock: getTemplateLock(clientId), + isLockedByParent: !!__unstableGetContentLockingParent(clientId), + isEditingAsBlocks: __unstableGetTemporarilyEditingAsBlocks() === clientId }; - }, []); + }, [clientId]); const { - startTyping, - stopTyping + updateSettings, + updateBlockListSettings, + __unstableSetTemporarilyEditingAsBlocks } = (0,external_wp_data_namespaceObject.useDispatch)(store); - const ref1 = useMouseMoveTypingReset(); - const ref2 = (0,external_wp_compose_namespaceObject.useRefEffect)(node => { - const { - ownerDocument - } = node; - const { - defaultView - } = ownerDocument; - const selection = defaultView.getSelection(); - - // Listeners to stop typing should only be added when typing. - // Listeners to start typing should only be added when not typing. - if (isTyping) { - let timerId; - - /** - * Stops typing when focus transitions to a non-text field element. - * - * @param {FocusEvent} event Focus event. - */ - function stopTypingOnNonTextField(event) { - const { - target - } = event; - - // Since focus to a non-text field via arrow key will trigger - // before the keydown event, wait until after current stack - // before evaluating whether typing is to be stopped. Otherwise, - // typing will re-start. - timerId = defaultView.setTimeout(() => { - if (!(0,external_wp_dom_namespaceObject.isTextField)(target)) { - stopTyping(); - } - }); - } - - /** - * Unsets typing flag if user presses Escape while typing flag is - * active. - * - * @param {KeyboardEvent} event Keypress or keydown event to - * interpret. - */ - function stopTypingOnEscapeKey(event) { - const { - keyCode - } = event; - if (keyCode === external_wp_keycodes_namespaceObject.ESCAPE || keyCode === external_wp_keycodes_namespaceObject.TAB) { - stopTyping(); - } - } - - /** - * On selection change, unset typing flag if user has made an - * uncollapsed (shift) selection. - */ - function stopTypingOnSelectionUncollapse() { - if (!selection.isCollapsed) { - stopTyping(); - } - } - node.addEventListener('focus', stopTypingOnNonTextField); - node.addEventListener('keydown', stopTypingOnEscapeKey); - if (!hasInlineToolbar) { - ownerDocument.addEventListener('selectionchange', stopTypingOnSelectionUncollapse); - } - return () => { - defaultView.clearTimeout(timerId); - node.removeEventListener('focus', stopTypingOnNonTextField); - node.removeEventListener('keydown', stopTypingOnEscapeKey); - ownerDocument.removeEventListener('selectionchange', stopTypingOnSelectionUncollapse); - }; + const { + stopEditingAsBlocks + } = unlock((0,external_wp_data_namespaceObject.useDispatch)(store)); + const isContentLocked = !isLockedByParent && templateLock === 'contentOnly'; + const { + __unstableMarkNextChangeAsNotPersistent, + updateBlockAttributes + } = (0,external_wp_data_namespaceObject.useDispatch)(store); + const stopEditingAsBlockCallback = (0,external_wp_element_namespaceObject.useCallback)(() => { + stopEditingAsBlocks(clientId); + }, [clientId, stopEditingAsBlocks]); + if (!isContentLocked && !isEditingAsBlocks) { + return null; + } + const showStopEditingAsBlocks = isEditingAsBlocks && !isContentLocked; + const showStartEditingAsBlocks = !isEditingAsBlocks && isContentLocked && isSelected; + return (0,external_React_.createElement)(external_React_.Fragment, null, showStopEditingAsBlocks && (0,external_React_.createElement)(external_React_.Fragment, null, (0,external_React_.createElement)(block_controls, { + group: "other" + }, (0,external_React_.createElement)(external_wp_components_namespaceObject.ToolbarButton, { + onClick: stopEditingAsBlockCallback + }, (0,external_wp_i18n_namespaceObject.__)('Done')))), showStartEditingAsBlocks && (0,external_React_.createElement)(block_settings_menu_controls, null, ({ + onClose + }) => (0,external_React_.createElement)(external_wp_components_namespaceObject.MenuItem, { + onClick: () => { + __unstableMarkNextChangeAsNotPersistent(); + updateBlockAttributes(clientId, { + templateLock: undefined + }); + updateBlockListSettings(clientId, { + ...getBlockListSettings(clientId), + templateLock: false + }); + const focusModeToRevert = getSettings().focusMode; + updateSettings({ + focusMode: true + }); + __unstableSetTemporarilyEditingAsBlocks(clientId, focusModeToRevert); + onClose(); } + }, (0,external_wp_i18n_namespaceObject.__)('Modify')))); +} +/* harmony default export */ const content_lock_ui = ({ + edit: ContentLockControlsPure, + hasSupport() { + return true; + } +}); - /** - * Handles a keypress or keydown event to infer intention to start - * typing. - * - * @param {KeyboardEvent} event Keypress or keydown event to interpret. - */ - function startTypingInTextField(event) { - const { - type, - target - } = event; +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/hooks/metadata.js +/** + * WordPress dependencies + */ - // Abort early if already typing, or key press is incurred outside a - // text field (e.g. arrow-ing through toolbar buttons). - // Ignore typing if outside the current DOM container - if (!(0,external_wp_dom_namespaceObject.isTextField)(target) || !node.contains(target)) { - return; - } +const META_ATTRIBUTE_NAME = 'metadata'; - // Special-case keydown because certain keys do not emit a keypress - // event. Conversely avoid keydown as the canonical event since - // there are many keydown which are explicitly not targeted for - // typing. - if (type === 'keydown' && !isKeyDownEligibleForStartTyping(event)) { - return; - } - startTyping(); +/** + * Filters registered block settings, extending attributes to include `metadata`. + * + * see: https://github.com/WordPress/gutenberg/pull/40393/files#r864632012 + * + * @param {Object} blockTypeSettings Original block settings. + * @return {Object} Filtered block settings. + */ +function addMetaAttribute(blockTypeSettings) { + // Allow blocks to specify their own attribute definition with default values if needed. + if (blockTypeSettings?.attributes?.[META_ATTRIBUTE_NAME]?.type) { + return blockTypeSettings; + } + blockTypeSettings.attributes = { + ...blockTypeSettings.attributes, + [META_ATTRIBUTE_NAME]: { + type: 'object' } - node.addEventListener('keypress', startTypingInTextField); - node.addEventListener('keydown', startTypingInTextField); - return () => { - node.removeEventListener('keypress', startTypingInTextField); - node.removeEventListener('keydown', startTypingInTextField); - }; - }, [isTyping, hasInlineToolbar, startTyping, stopTyping]); - return (0,external_wp_compose_namespaceObject.useMergeRefs)([ref1, ref2]); -} -function ObserveTyping({ - children -}) { - return (0,external_wp_element_namespaceObject.createElement)("div", { - ref: useTypingObserver() - }, children); + }; + return blockTypeSettings; } +(0,external_wp_hooks_namespaceObject.addFilter)('blocks.registerBlockType', 'core/metadata/addMetaAttribute', addMetaAttribute); + +;// CONCATENATED MODULE: ./node_modules/@wordpress/icons/build-module/library/block-default.js /** - * @see https://github.com/WordPress/gutenberg/blob/HEAD/packages/block-editor/src/components/observe-typing/README.md + * WordPress dependencies */ -/* harmony default export */ var observe_typing = (ObserveTyping); -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/block-list/index.js +const blockDefault = (0,external_React_.createElement)(external_wp_primitives_namespaceObject.SVG, { + xmlns: "http://www.w3.org/2000/svg", + viewBox: "0 0 24 24" +}, (0,external_React_.createElement)(external_wp_primitives_namespaceObject.Path, { + d: "M19 8h-1V6h-5v2h-2V6H6v2H5c-1.1 0-2 .9-2 2v8c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2v-8c0-1.1-.9-2-2-2zm.5 10c0 .3-.2.5-.5.5H5c-.3 0-.5-.2-.5-.5v-8c0-.3.2-.5.5-.5h14c.3 0 .5.2.5.5v8z" +})); +/* harmony default export */ const block_default = (blockDefault); + +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/block-icon/index.js /** * External dependencies @@ -28427,247 +33927,306 @@ function ObserveTyping({ +function BlockIcon({ + icon, + showColors = false, + className, + context +}) { + if (icon?.src === 'block-default') { + icon = { + src: block_default + }; + } + const renderedIcon = (0,external_React_.createElement)(external_wp_components_namespaceObject.Icon, { + icon: icon && icon.src ? icon.src : icon, + context: context + }); + const style = showColors ? { + backgroundColor: icon && icon.background, + color: icon && icon.foreground + } : {}; + return (0,external_React_.createElement)("span", { + style: style, + className: classnames_default()('block-editor-block-icon', className, { + 'has-colors': showColors + }) + }, renderedIcon); +} /** - * Internal dependencies + * @see https://github.com/WordPress/gutenberg/blob/HEAD/packages/block-editor/src/components/block-icon/README.md */ +/* harmony default export */ const block_icon = ((0,external_wp_element_namespaceObject.memo)(BlockIcon)); + +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/hooks/block-hooks.js +/** + * WordPress dependencies + */ +/** + * Internal dependencies + */ -const elementContext = (0,external_wp_element_namespaceObject.createContext)(); -const block_list_IntersectionObserver = (0,external_wp_element_namespaceObject.createContext)(); -const pendingBlockVisibilityUpdatesPerRegistry = new WeakMap(); -function Root({ - className, - ...settings +const EMPTY_OBJECT = {}; +function BlockHooksControlPure({ + name, + clientId, + metadata: { + ignoredHookedBlocks = [] + } = {} }) { - const [element, setElement] = (0,external_wp_element_namespaceObject.useState)(); - const isLargeViewport = (0,external_wp_compose_namespaceObject.useViewportMatch)('medium'); + const blockTypes = (0,external_wp_data_namespaceObject.useSelect)(select => select(external_wp_blocks_namespaceObject.store).getBlockTypes(), []); + + // A hooked block added via a filter will not be exposed through a block + // type's `blockHooks` property; however, if the containing layout has been + // modified, it will be present in the anchor block's `ignoredHookedBlocks` + // metadata. + const hookedBlocksForCurrentBlock = (0,external_wp_element_namespaceObject.useMemo)(() => blockTypes?.filter(({ + name: blockName, + blockHooks + }) => blockHooks && name in blockHooks || ignoredHookedBlocks.includes(blockName)), [blockTypes, name, ignoredHookedBlocks]); const { - isOutlineMode, - isFocusMode, - editorMode + blockIndex, + rootClientId, + innerBlocksLength } = (0,external_wp_data_namespaceObject.useSelect)(select => { const { - getSettings, - __unstableGetEditorMode + getBlocks, + getBlockIndex, + getBlockRootClientId } = select(store); - const { - outlineMode, - focusMode - } = getSettings(); return { - isOutlineMode: outlineMode, - isFocusMode: focusMode, - editorMode: __unstableGetEditorMode() + blockIndex: getBlockIndex(clientId), + innerBlocksLength: getBlocks(clientId)?.length, + rootClientId: getBlockRootClientId(clientId) }; - }, []); - const registry = (0,external_wp_data_namespaceObject.useRegistry)(); - const { - setBlockVisibility - } = (0,external_wp_data_namespaceObject.useDispatch)(store); - const delayedBlockVisibilityUpdates = (0,external_wp_compose_namespaceObject.useDebounce)((0,external_wp_element_namespaceObject.useCallback)(() => { - const updates = {}; - pendingBlockVisibilityUpdatesPerRegistry.get(registry).forEach(([id, isIntersecting]) => { - updates[id] = isIntersecting; - }); - setBlockVisibility(updates); - }, [registry]), 300, { - trailing: true - }); - const intersectionObserver = (0,external_wp_element_namespaceObject.useMemo)(() => { + }, [clientId]); + const hookedBlockClientIds = (0,external_wp_data_namespaceObject.useSelect)(select => { const { - IntersectionObserver: Observer - } = window; - if (!Observer) { - return; - } - return new Observer(entries => { - if (!pendingBlockVisibilityUpdatesPerRegistry.get(registry)) { - pendingBlockVisibilityUpdatesPerRegistry.set(registry, []); + getBlocks, + getGlobalBlockCount + } = select(store); + const _hookedBlockClientIds = hookedBlocksForCurrentBlock.reduce((clientIds, block) => { + // If the block doesn't exist anywhere in the block tree, + // we know that we have to set the toggle to disabled. + if (getGlobalBlockCount(block.name) === 0) { + return clientIds; } - for (const entry of entries) { - const clientId = entry.target.getAttribute('data-block'); - pendingBlockVisibilityUpdatesPerRegistry.get(registry).push([clientId, entry.isIntersecting]); + const relativePosition = block?.blockHooks?.[name]; + let candidates; + switch (relativePosition) { + case 'before': + case 'after': + // Any of the current block's siblings (with the right block type) qualifies + // as a hooked block (inserted `before` or `after` the current one), as the block + // might've been automatically inserted and then moved around a bit by the user. + candidates = getBlocks(rootClientId); + break; + case 'first_child': + case 'last_child': + // Any of the current block's child blocks (with the right block type) qualifies + // as a hooked first or last child block, as the block might've been automatically + // inserted and then moved around a bit by the user. + candidates = getBlocks(clientId); + break; + case undefined: + // If we haven't found a blockHooks field with a relative position for the hooked + // block, it means that it was added by a filter. In this case, we look for the block + // both among the current block's siblings and its children. + candidates = [...getBlocks(rootClientId), ...getBlocks(clientId)]; + break; } - delayedBlockVisibilityUpdates(); - }); - }, []); - const innerBlocksProps = useInnerBlocksProps({ - ref: (0,external_wp_compose_namespaceObject.useMergeRefs)([useBlockSelectionClearer(), useInBetweenInserter(), useTypingObserver()]), - className: classnames_default()('is-root-container', className, { - 'is-outline-mode': isOutlineMode, - 'is-focus-mode': isFocusMode && isLargeViewport, - 'is-navigate-mode': editorMode === 'navigation' - }) - }, settings); - return (0,external_wp_element_namespaceObject.createElement)(elementContext.Provider, { - value: element - }, (0,external_wp_element_namespaceObject.createElement)(block_list_IntersectionObserver.Provider, { - value: intersectionObserver - }, (0,external_wp_element_namespaceObject.createElement)("div", { - ...innerBlocksProps - }), (0,external_wp_element_namespaceObject.createElement)("div", { - ref: setElement - }))); -} -function BlockList(settings) { - return (0,external_wp_element_namespaceObject.createElement)(Provider, { - value: DEFAULT_BLOCK_EDIT_CONTEXT - }, (0,external_wp_element_namespaceObject.createElement)(Root, { - ...settings - })); -} -BlockList.__unstableElementContext = elementContext; -function Items({ - placeholder, - rootClientId, - renderAppender, - __experimentalAppenderTagName, - layout = defaultLayout -}) { - const { - order, - selectedBlocks, - visibleBlocks - } = (0,external_wp_data_namespaceObject.useSelect)(select => { - const { - getBlockOrder, - getSelectedBlockClientIds, - __unstableGetVisibleBlocks - } = select(store); - return { - order: getBlockOrder(rootClientId), - selectedBlocks: getSelectedBlockClientIds(), - visibleBlocks: __unstableGetVisibleBlocks() - }; - }, [rootClientId]); - return (0,external_wp_element_namespaceObject.createElement)(LayoutProvider, { - value: layout - }, order.map(clientId => (0,external_wp_element_namespaceObject.createElement)(external_wp_data_namespaceObject.AsyncModeProvider, { - key: clientId, - value: - // Only provide data asynchronously if the block is - // not visible and not selected. - !visibleBlocks.has(clientId) && !selectedBlocks.includes(clientId) - }, (0,external_wp_element_namespaceObject.createElement)(block, { - rootClientId: rootClientId, - clientId: clientId - }))), order.length < 1 && placeholder, (0,external_wp_element_namespaceObject.createElement)(block_list_appender, { - tagName: __experimentalAppenderTagName, - rootClientId: rootClientId, - renderAppender: renderAppender - })); -} -function BlockListItems(props) { - // This component needs to always be synchronous as it's the one changing - // the async mode depending on the block selection. - return (0,external_wp_element_namespaceObject.createElement)(external_wp_data_namespaceObject.AsyncModeProvider, { - value: false - }, (0,external_wp_element_namespaceObject.createElement)(Items, { - ...props - })); -} - -;// CONCATENATED MODULE: external ["wp","url"] -var external_wp_url_namespaceObject = window["wp"]["url"]; -;// CONCATENATED MODULE: ./node_modules/@wordpress/icons/build-module/library/media.js - -/** - * WordPress dependencies - */ + const hookedBlock = candidates?.find(candidate => candidate.name === block.name); -const media = (0,external_wp_element_namespaceObject.createElement)(external_wp_primitives_namespaceObject.SVG, { - xmlns: "http://www.w3.org/2000/svg", - viewBox: "0 0 24 24" -}, (0,external_wp_element_namespaceObject.createElement)(external_wp_primitives_namespaceObject.Path, { - d: "m7 6.5 4 2.5-4 2.5z" -}), (0,external_wp_element_namespaceObject.createElement)(external_wp_primitives_namespaceObject.Path, { - fillRule: "evenodd", - clipRule: "evenodd", - d: "m5 3c-1.10457 0-2 .89543-2 2v14c0 1.1046.89543 2 2 2h14c1.1046 0 2-.8954 2-2v-14c0-1.10457-.8954-2-2-2zm14 1.5h-14c-.27614 0-.5.22386-.5.5v10.7072l3.62953-2.6465c.25108-.1831.58905-.1924.84981-.0234l2.92666 1.8969 3.5712-3.4719c.2911-.2831.7545-.2831 1.0456 0l2.9772 2.8945v-9.3568c0-.27614-.2239-.5-.5-.5zm-14.5 14.5v-1.4364l4.09643-2.987 2.99567 1.9417c.2936.1903.6798.1523.9307-.0917l3.4772-3.3806 3.4772 3.3806.0228-.0234v2.5968c0 .2761-.2239.5-.5.5h-14c-.27614 0-.5-.2239-.5-.5z" -})); -/* harmony default export */ var library_media = (media); + // If the block exists in the designated location, we consider it hooked + // and show the toggle as enabled. + if (hookedBlock) { + return { + ...clientIds, + [block.name]: hookedBlock.clientId + }; + } -;// CONCATENATED MODULE: ./node_modules/@wordpress/icons/build-module/library/upload.js + // If no hooked block was found in any of its designated locations, + // we set the toggle to disabled. + return clientIds; + }, {}); + if (Object.values(_hookedBlockClientIds).length > 0) { + return _hookedBlockClientIds; + } + return EMPTY_OBJECT; + }, [hookedBlocksForCurrentBlock, name, clientId, rootClientId]); + const { + insertBlock, + removeBlock + } = (0,external_wp_data_namespaceObject.useDispatch)(store); + if (!hookedBlocksForCurrentBlock.length) { + return null; + } -/** - * WordPress dependencies - */ + // Group by block namespace (i.e. prefix before the slash). + const groupedHookedBlocks = hookedBlocksForCurrentBlock.reduce((groups, block) => { + const [namespace] = block.name.split('/'); + if (!groups[namespace]) { + groups[namespace] = []; + } + groups[namespace].push(block); + return groups; + }, {}); + const insertBlockIntoDesignatedLocation = (block, relativePosition) => { + switch (relativePosition) { + case 'before': + case 'after': + insertBlock(block, relativePosition === 'after' ? blockIndex + 1 : blockIndex, rootClientId, + // Insert as a child of the current block's parent + false); + break; + case 'first_child': + case 'last_child': + insertBlock(block, + // TODO: It'd be great if insertBlock() would accept negative indices for insertion. + relativePosition === 'first_child' ? 0 : innerBlocksLength, clientId, + // Insert as a child of the current block. + false); + break; + case undefined: + // If we do not know the relative position, it is because the block was + // added via a filter. In this case, we default to inserting it after the + // current block. + insertBlock(block, blockIndex + 1, rootClientId, + // Insert as a child of the current block's parent + false); + break; + } + }; + return (0,external_React_.createElement)(inspector_controls, null, (0,external_React_.createElement)(external_wp_components_namespaceObject.PanelBody, { + className: "block-editor-hooks__block-hooks", + title: (0,external_wp_i18n_namespaceObject.__)('Plugins'), + initialOpen: true + }, (0,external_React_.createElement)("p", { + className: "block-editor-hooks__block-hooks-helptext" + }, (0,external_wp_i18n_namespaceObject.__)('Manage the inclusion of blocks added automatically by plugins.')), Object.keys(groupedHookedBlocks).map(vendor => { + return (0,external_React_.createElement)(external_wp_element_namespaceObject.Fragment, { + key: vendor + }, (0,external_React_.createElement)("h3", null, vendor), groupedHookedBlocks[vendor].map(block => { + const checked = (block.name in hookedBlockClientIds); + return (0,external_React_.createElement)(external_wp_components_namespaceObject.ToggleControl, { + checked: checked, + key: block.title, + label: (0,external_React_.createElement)(external_wp_components_namespaceObject.__experimentalHStack, { + justify: "flex-start" + }, (0,external_React_.createElement)(block_icon, { + icon: block.icon + }), (0,external_React_.createElement)("span", null, block.title)), + onChange: () => { + if (!checked) { + // Create and insert block. + const relativePosition = block.blockHooks[name]; + insertBlockIntoDesignatedLocation((0,external_wp_blocks_namespaceObject.createBlock)(block.name), relativePosition); + return; + } -const upload = (0,external_wp_element_namespaceObject.createElement)(external_wp_primitives_namespaceObject.SVG, { - xmlns: "http://www.w3.org/2000/svg", - viewBox: "0 0 24 24" -}, (0,external_wp_element_namespaceObject.createElement)(external_wp_primitives_namespaceObject.Path, { - d: "M18.5 15v3.5H13V6.7l4.5 4.1 1-1.1-6.2-5.8-5.8 5.8 1 1.1 4-4v11.7h-6V15H4v5h16v-5z" -})); -/* harmony default export */ var library_upload = (upload); + // Remove block. + removeBlock(hookedBlockClientIds[block.name], false); + } + }); + })); + }))); +} +/* harmony default export */ const block_hooks = ({ + edit: BlockHooksControlPure, + attributeKeys: ['metadata'], + hasSupport() { + return true; + } +}); -;// CONCATENATED MODULE: ./node_modules/@wordpress/icons/build-module/library/post-featured-image.js +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/hooks/block-renaming.js /** * WordPress dependencies */ -const postFeaturedImage = (0,external_wp_element_namespaceObject.createElement)(external_wp_primitives_namespaceObject.SVG, { - xmlns: "http://www.w3.org/2000/svg", - viewBox: "0 0 24 24" -}, (0,external_wp_element_namespaceObject.createElement)(external_wp_primitives_namespaceObject.Path, { - d: "M19 3H5c-.6 0-1 .4-1 1v7c0 .5.4 1 1 1h14c.5 0 1-.4 1-1V4c0-.6-.4-1-1-1zM5.5 10.5v-.4l1.8-1.3 1.3.8c.3.2.7.2.9-.1L11 8.1l2.4 2.4H5.5zm13 0h-2.9l-4-4c-.3-.3-.8-.3-1.1 0L8.9 8l-1.2-.8c-.3-.2-.6-.2-.9 0l-1.3 1V4.5h13v6zM4 20h9v-1.5H4V20zm0-4h16v-1.5H4V16z" -})); -/* harmony default export */ var post_featured_image = (postFeaturedImage); - -;// CONCATENATED MODULE: external ["wp","preferences"] -var external_wp_preferences_namespaceObject = window["wp"]["preferences"]; -;// CONCATENATED MODULE: ./node_modules/@wordpress/icons/build-module/library/keyboard-return.js -/** - * WordPress dependencies - */ -const keyboardReturn = (0,external_wp_element_namespaceObject.createElement)(external_wp_primitives_namespaceObject.SVG, { - xmlns: "http://www.w3.org/2000/svg", - viewBox: "-2 -2 24 24" -}, (0,external_wp_element_namespaceObject.createElement)(external_wp_primitives_namespaceObject.Path, { - d: "M6.734 16.106l2.176-2.38-1.093-1.028-3.846 4.158 3.846 4.157 1.093-1.027-2.176-2.38h2.811c1.125 0 2.25.03 3.374 0 1.428-.001 3.362-.25 4.963-1.277 1.66-1.065 2.868-2.906 2.868-5.859 0-2.479-1.327-4.896-3.65-5.93-1.82-.813-3.044-.8-4.806-.788l-.567.002v1.5c.184 0 .368 0 .553-.002 1.82-.007 2.704-.014 4.21.657 1.854.827 2.76 2.657 2.76 4.561 0 2.472-.973 3.824-2.178 4.596-1.258.807-2.864 1.04-4.163 1.04h-.02c-1.115.03-2.229 0-3.344 0H6.734z" -})); -/* harmony default export */ var keyboard_return = (keyboardReturn); -;// CONCATENATED MODULE: ./node_modules/@wordpress/icons/build-module/library/chevron-left-small.js /** - * WordPress dependencies + * Internal dependencies */ -const chevronLeftSmall = (0,external_wp_element_namespaceObject.createElement)(external_wp_primitives_namespaceObject.SVG, { - xmlns: "http://www.w3.org/2000/svg", - viewBox: "0 0 24 24" -}, (0,external_wp_element_namespaceObject.createElement)(external_wp_primitives_namespaceObject.Path, { - d: "m13.1 16-3.4-4 3.4-4 1.1 1-2.6 3 2.6 3-1.1 1z" -})); -/* harmony default export */ var chevron_left_small = (chevronLeftSmall); - -;// CONCATENATED MODULE: ./node_modules/@wordpress/icons/build-module/library/chevron-right-small.js /** - * WordPress dependencies + * Filters registered block settings, adding an `__experimentalLabel` callback if one does not already exist. + * + * @param {Object} settings Original block settings. + * + * @return {Object} Filtered block settings. */ +function addLabelCallback(settings) { + // If blocks provide their own label callback, do not override it. + if (settings.__experimentalLabel) { + return settings; + } + const supportsBlockNaming = (0,external_wp_blocks_namespaceObject.hasBlockSupport)(settings, 'renaming', true // default value + ); -const chevronRightSmall = (0,external_wp_element_namespaceObject.createElement)(external_wp_primitives_namespaceObject.SVG, { - xmlns: "http://www.w3.org/2000/svg", - viewBox: "0 0 24 24" -}, (0,external_wp_element_namespaceObject.createElement)(external_wp_primitives_namespaceObject.Path, { - d: "M10.8622 8.04053L14.2805 12.0286L10.8622 16.0167L9.72327 15.0405L12.3049 12.0286L9.72327 9.01672L10.8622 8.04053Z" -})); -/* harmony default export */ var chevron_right_small = (chevronRightSmall); + // Check whether block metadata is supported before using it. + if (supportsBlockNaming) { + settings.__experimentalLabel = (attributes, { + context + }) => { + const { + metadata + } = attributes; -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/link-control/settings-drawer.js + // In the list view, use the block's name attribute as the label. + if (context === 'list-view' && metadata?.name) { + return metadata.name; + } + }; + } + return settings; +} +function BlockRenameControlPure({ + metadata, + setAttributes +}) { + return (0,external_React_.createElement)(inspector_controls, { + group: "advanced" + }, (0,external_React_.createElement)(external_wp_components_namespaceObject.TextControl, { + __nextHasNoMarginBottom: true, + __next40pxDefaultSize: true, + label: (0,external_wp_i18n_namespaceObject.__)('Block name'), + value: metadata?.name || '', + onChange: newName => { + setAttributes({ + metadata: { + ...metadata, + name: newName + } + }); + } + })); +} +/* harmony default export */ const block_renaming = ({ + edit: BlockRenameControlPure, + attributeKeys: ['metadata'], + hasSupport(name) { + return (0,external_wp_blocks_namespaceObject.hasBlockSupport)(name, 'renaming', true); + } +}); +(0,external_wp_hooks_namespaceObject.addFilter)('blocks.registerBlockType', 'core/metadata/addLabelCallback', addLabelCallback); + +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/hooks/use-bindings-attributes.js /** * WordPress dependencies @@ -28677,1040 +34236,793 @@ const chevronRightSmall = (0,external_wp_element_namespaceObject.createElement)( -function LinkSettingsDrawer({ - children, - settingsOpen, - setSettingsOpen -}) { - const prefersReducedMotion = (0,external_wp_compose_namespaceObject.useReducedMotion)(); - const MaybeAnimatePresence = prefersReducedMotion ? external_wp_element_namespaceObject.Fragment : external_wp_components_namespaceObject.__unstableAnimatePresence; - const MaybeMotionDiv = prefersReducedMotion ? 'div' : external_wp_components_namespaceObject.__unstableMotion.div; - const id = (0,external_wp_compose_namespaceObject.useInstanceId)(LinkSettingsDrawer); - const settingsDrawerId = `link-control-settings-drawer-${id}`; - return (0,external_wp_element_namespaceObject.createElement)(external_wp_element_namespaceObject.Fragment, null, (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.Button, { - className: "block-editor-link-control__drawer-toggle", - "aria-expanded": settingsOpen, - onClick: () => setSettingsOpen(!settingsOpen), - icon: (0,external_wp_i18n_namespaceObject.isRTL)() ? chevron_left_small : chevron_right_small, - "aria-controls": settingsDrawerId - }, (0,external_wp_i18n_namespaceObject._x)('Advanced', 'Additional link settings')), (0,external_wp_element_namespaceObject.createElement)(MaybeAnimatePresence, null, settingsOpen && (0,external_wp_element_namespaceObject.createElement)(MaybeMotionDiv, { - className: "block-editor-link-control__drawer", - hidden: !settingsOpen, - id: settingsDrawerId, - initial: "collapsed", - animate: "open", - exit: "collapsed", - variants: { - open: { - opacity: 1, - height: 'auto' - }, - collapsed: { - opacity: 0, - height: 0 - } - }, - transition: { - duration: 0.1 - } - }, (0,external_wp_element_namespaceObject.createElement)("div", { - className: "block-editor-link-control__drawer-inner" - }, children)))); -} -/* harmony default export */ var settings_drawer = (LinkSettingsDrawer); -// EXTERNAL MODULE: ./node_modules/dom-scroll-into-view/lib/index.js -var dom_scroll_into_view_lib = __webpack_require__(5425); -var lib_default = /*#__PURE__*/__webpack_require__.n(dom_scroll_into_view_lib); -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/url-input/index.js /** - * External dependencies + * Internal dependencies */ +/** @typedef {import('@wordpress/compose').WPHigherOrderComponent} WPHigherOrderComponent */ +/** @typedef {import('@wordpress/blocks').WPBlockSettings} WPBlockSettings */ /** - * WordPress dependencies + * Given a binding of block attributes, returns a higher order component that + * overrides its `attributes` and `setAttributes` props to sync any changes needed. + * + * @return {WPHigherOrderComponent} Higher-order component. */ - - - - - - - +const BLOCK_BINDINGS_ALLOWED_BLOCKS = { + 'core/paragraph': ['content'], + 'core/heading': ['content'], + 'core/image': ['url', 'title', 'alt'], + 'core/button': ['url', 'text', 'linkTarget'] +}; /** - * Internal dependencies + * Based on the given block name, + * check if it is possible to bind the block. + * + * @param {string} blockName - The block name. + * @return {boolean} Whether it is possible to bind the block to sources. */ - +function canBindBlock(blockName) { + return blockName in BLOCK_BINDINGS_ALLOWED_BLOCKS; +} /** - * Whether the argument is a function. + * Based on the given block name and attribute name, + * check if it is possible to bind the block attribute. * - * @param {*} maybeFunc The argument to check. - * @return {boolean} True if the argument is a function, false otherwise. + * @param {string} blockName - The block name. + * @param {string} attributeName - The attribute name. + * @return {boolean} Whether it is possible to bind the block attribute. */ -function isFunction(maybeFunc) { - return typeof maybeFunc === 'function'; +function canBindAttribute(blockName, attributeName) { + return canBindBlock(blockName) && BLOCK_BINDINGS_ALLOWED_BLOCKS[blockName].includes(attributeName); } -class URLInput extends external_wp_element_namespaceObject.Component { - constructor(props) { - super(props); - this.onChange = this.onChange.bind(this); - this.onFocus = this.onFocus.bind(this); - this.onKeyDown = this.onKeyDown.bind(this); - this.selectLink = this.selectLink.bind(this); - this.handleOnClick = this.handleOnClick.bind(this); - this.bindSuggestionNode = this.bindSuggestionNode.bind(this); - this.autocompleteRef = props.autocompleteRef || (0,external_wp_element_namespaceObject.createRef)(); - this.inputRef = (0,external_wp_element_namespaceObject.createRef)(); - this.updateSuggestions = (0,external_wp_compose_namespaceObject.debounce)(this.updateSuggestions.bind(this), 200); - this.suggestionNodes = []; - this.suggestionsRequest = null; - this.state = { - suggestions: [], - showSuggestions: false, - isUpdatingSuggestions: false, - suggestionsValue: null, - selectedSuggestion: null, - suggestionsListboxId: '', - suggestionOptionIdPrefix: '' - }; - } - componentDidUpdate(prevProps) { - const { - showSuggestions, - selectedSuggestion - } = this.state; - const { - value, - __experimentalShowInitialSuggestions = false - } = this.props; - - // Only have to worry about scrolling selected suggestion into view - // when already expanded. - if (showSuggestions && selectedSuggestion !== null && this.suggestionNodes[selectedSuggestion] && !this.scrollingIntoView) { - this.scrollingIntoView = true; - lib_default()(this.suggestionNodes[selectedSuggestion], this.autocompleteRef.current, { - onlyScrollIfNeeded: true - }); - this.props.setTimeout(() => { - this.scrollingIntoView = false; - }, 100); - } - - // Update suggestions when the value changes. - if (prevProps.value !== value && !this.props.disableSuggestions && !this.state.isUpdatingSuggestions) { - if (value?.length) { - // If the new value is not empty we need to update with suggestions for it. - this.updateSuggestions(value); - } else if (__experimentalShowInitialSuggestions) { - // If the new value is empty and we can show initial suggestions, then show initial suggestions. - this.updateSuggestions(); - } - } - } - componentDidMount() { - if (this.shouldShowInitialSuggestions()) { - this.updateSuggestions(); - } - } - componentWillUnmount() { - this.suggestionsRequest?.cancel?.(); - this.suggestionsRequest = null; - } - bindSuggestionNode(index) { - return ref => { - this.suggestionNodes[index] = ref; - }; - } - shouldShowInitialSuggestions() { - const { - __experimentalShowInitialSuggestions = false, - value - } = this.props; - return __experimentalShowInitialSuggestions && !(value && value.length); - } - updateSuggestions(value = '') { - const { - __experimentalFetchLinkSuggestions: fetchLinkSuggestions, - __experimentalHandleURLSuggestions: handleURLSuggestions - } = this.props; - if (!fetchLinkSuggestions) { - return; - } - - // Initial suggestions may only show if there is no value - // (note: this includes whitespace). - const isInitialSuggestions = !value?.length; - - // Trim only now we've determined whether or not it originally had a "length" - // (even if that value was all whitespace). - value = value.trim(); - // Allow a suggestions request if: - // - there are at least 2 characters in the search input (except manual searches where - // search input length is not required to trigger a fetch) - // - this is a direct entry (eg: a URL) - if (!isInitialSuggestions && (value.length < 2 || !handleURLSuggestions && (0,external_wp_url_namespaceObject.isURL)(value))) { - this.suggestionsRequest?.cancel?.(); - this.suggestionsRequest = null; - this.setState({ - suggestions: [], - showSuggestions: false, - suggestionsValue: value, - selectedSuggestion: null, - loading: false - }); - return; - } - this.setState({ - isUpdatingSuggestions: true, - selectedSuggestion: null, - loading: true - }); - const request = fetchLinkSuggestions(value, { - isInitialSuggestions - }); - request.then(suggestions => { - // A fetch Promise doesn't have an abort option. It's mimicked by - // comparing the request reference in on the instance, which is - // reset or deleted on subsequent requests or unmounting. - if (this.suggestionsRequest !== request) { - return; - } - this.setState({ - suggestions, - isUpdatingSuggestions: false, - suggestionsValue: value, - loading: false, - showSuggestions: !!suggestions.length - }); - if (!!suggestions.length) { - this.props.debouncedSpeak((0,external_wp_i18n_namespaceObject.sprintf)( /* translators: %s: number of results. */ - (0,external_wp_i18n_namespaceObject._n)('%d result found, use up and down arrow keys to navigate.', '%d results found, use up and down arrow keys to navigate.', suggestions.length), suggestions.length), 'assertive'); - } else { - this.props.debouncedSpeak((0,external_wp_i18n_namespaceObject.__)('No results.'), 'assertive'); - } - }).catch(() => { - if (this.suggestionsRequest !== request) { +/** + * This component is responsible for detecting and + * propagating data changes from the source to the block. + * + * @param {Object} props - The component props. + * @param {string} props.attrName - The attribute name. + * @param {Object} props.blockProps - The block props with bound attribute. + * @param {Object} props.source - Source handler. + * @param {Object} props.args - The arguments to pass to the source. + * @param {Function} props.onPropValueChange - The function to call when the attribute value changes. + * @return {null} Data-handling component. Render nothing. + */ +const BindingConnector = ({ + args, + attrName, + blockProps, + source, + onPropValueChange +}) => { + const { + placeholder, + value: propValue + } = source.useSource(blockProps, args); + const { + name: blockName + } = blockProps; + const attrValue = blockProps.attributes[attrName]; + const updateBoundAttibute = (0,external_wp_element_namespaceObject.useCallback)((newAttrValue, prevAttrValue) => { + /* + * If the attribute is a RichTextData instance, + * (core/paragraph, core/heading, core/button, etc.) + * compare its HTML representation with the new value. + * + * To do: it looks like a workaround. + * Consider improving the attribute and metadata fields types. + */ + if (prevAttrValue instanceof external_wp_richText_namespaceObject.RichTextData) { + // Bail early if the Rich Text value is the same. + if (prevAttrValue.toHTMLString() === newAttrValue) { return; } - this.setState({ - isUpdatingSuggestions: false, - loading: false - }); - }); - - // Note that this assignment is handled *before* the async search request - // as a Promise always resolves on the next tick of the event loop. - this.suggestionsRequest = request; - } - onChange(event) { - this.props.onChange(event.target.value); - } - onFocus() { - const { - suggestions - } = this.state; - const { - disableSuggestions, - value - } = this.props; - // When opening the link editor, if there's a value present, we want to load the suggestions pane with the results for this input search value - // Don't re-run the suggestions on focus if there are already suggestions present (prevents searching again when tabbing between the input and buttons) - if (value && !disableSuggestions && !this.state.isUpdatingSuggestions && !(suggestions && suggestions.length)) { - // Ensure the suggestions are updated with the current input value. - this.updateSuggestions(value); + /* + * To preserve the value type, + * convert the new value to a RichTextData instance. + */ + newAttrValue = external_wp_richText_namespaceObject.RichTextData.fromHTMLString(newAttrValue); } - } - onKeyDown(event) { - this.props.onKeyDown?.(event); - const { - showSuggestions, - selectedSuggestion, - suggestions, - loading - } = this.state; - - // If the suggestions are not shown or loading, we shouldn't handle the arrow keys - // We shouldn't preventDefault to allow block arrow keys navigation. - if (!showSuggestions || !suggestions.length || loading) { - // In the Windows version of Firefox the up and down arrows don't move the caret - // within an input field like they do for Mac Firefox/Chrome/Safari. This causes - // a form of focus trapping that is disruptive to the user experience. This disruption - // only happens if the caret is not in the first or last position in the text input. - // See: https://github.com/WordPress/gutenberg/issues/5693#issuecomment-436684747 - switch (event.keyCode) { - // When UP is pressed, if the caret is at the start of the text, move it to the 0 - // position. - case external_wp_keycodes_namespaceObject.UP: - { - if (0 !== event.target.selectionStart) { - event.preventDefault(); - - // Set the input caret to position 0. - event.target.setSelectionRange(0, 0); - } - break; - } - // When DOWN is pressed, if the caret is not at the end of the text, move it to the - // last position. - case external_wp_keycodes_namespaceObject.DOWN: - { - if (this.props.value.length !== event.target.selectionStart) { - event.preventDefault(); - - // Set the input caret to the last position. - event.target.setSelectionRange(this.props.value.length, this.props.value.length); - } - break; - } - - // Submitting while loading should trigger onSubmit. - case external_wp_keycodes_namespaceObject.ENTER: - { - if (this.props.onSubmit) { - event.preventDefault(); - this.props.onSubmit(null, event); - } - break; - } - } + if (prevAttrValue === newAttrValue) { return; } - const suggestion = this.state.suggestions[this.state.selectedSuggestion]; - switch (event.keyCode) { - case external_wp_keycodes_namespaceObject.UP: - { - event.preventDefault(); - const previousIndex = !selectedSuggestion ? suggestions.length - 1 : selectedSuggestion - 1; - this.setState({ - selectedSuggestion: previousIndex - }); - break; - } - case external_wp_keycodes_namespaceObject.DOWN: - { - event.preventDefault(); - const nextIndex = selectedSuggestion === null || selectedSuggestion === suggestions.length - 1 ? 0 : selectedSuggestion + 1; - this.setState({ - selectedSuggestion: nextIndex - }); - break; - } - case external_wp_keycodes_namespaceObject.TAB: - { - if (this.state.selectedSuggestion !== null) { - this.selectLink(suggestion); - // Announce a link has been selected when tabbing away from the input field. - this.props.speak((0,external_wp_i18n_namespaceObject.__)('Link selected.')); - } - break; - } - case external_wp_keycodes_namespaceObject.ENTER: - { - event.preventDefault(); - if (this.state.selectedSuggestion !== null) { - this.selectLink(suggestion); - if (this.props.onSubmit) { - this.props.onSubmit(suggestion, event); - } - } else if (this.props.onSubmit) { - this.props.onSubmit(null, event); - } - break; - } - } - } - selectLink(suggestion) { - this.props.onChange(suggestion.url, suggestion); - this.setState({ - selectedSuggestion: null, - showSuggestions: false - }); - } - handleOnClick(suggestion) { - this.selectLink(suggestion); - // Move focus to the input field when a link suggestion is clicked. - this.inputRef.current.focus(); - } - static getDerivedStateFromProps({ - value, - instanceId, - disableSuggestions, - __experimentalShowInitialSuggestions = false - }, { - showSuggestions - }) { - let shouldShowSuggestions = showSuggestions; - const hasValue = value && value.length; - if (!__experimentalShowInitialSuggestions && !hasValue) { - shouldShowSuggestions = false; - } - if (disableSuggestions === true) { - shouldShowSuggestions = false; - } - return { - showSuggestions: shouldShowSuggestions, - suggestionsListboxId: `block-editor-url-input-suggestions-${instanceId}`, - suggestionOptionIdPrefix: `block-editor-url-input-suggestion-${instanceId}` - }; - } - render() { - return (0,external_wp_element_namespaceObject.createElement)(external_wp_element_namespaceObject.Fragment, null, this.renderControl(), this.renderSuggestions()); - } - renderControl() { - const { - /** Start opting into the new margin-free styles that will become the default in a future version. */ - __nextHasNoMarginBottom = false, - label = null, - className, - isFullWidth, - instanceId, - placeholder = (0,external_wp_i18n_namespaceObject.__)('Paste URL or type to search'), - __experimentalRenderControl: renderControl, - value = '', - hideLabelFromVision = false - } = this.props; - const { - loading, - showSuggestions, - selectedSuggestion, - suggestionsListboxId, - suggestionOptionIdPrefix - } = this.state; - const inputId = `url-input-control-${instanceId}`; - const controlProps = { - id: inputId, - // Passes attribute to label for the for attribute - label, - className: classnames_default()('block-editor-url-input', className, { - 'is-full-width': isFullWidth - }), - hideLabelFromVision - }; - const inputProps = { - id: inputId, - value, - required: true, - className: 'block-editor-url-input__input', - type: 'text', - onChange: this.onChange, - onFocus: this.onFocus, - placeholder, - onKeyDown: this.onKeyDown, - role: 'combobox', - 'aria-label': label ? undefined : (0,external_wp_i18n_namespaceObject.__)('URL'), - // Ensure input always has an accessible label - 'aria-expanded': showSuggestions, - 'aria-autocomplete': 'list', - 'aria-owns': suggestionsListboxId, - 'aria-activedescendant': selectedSuggestion !== null ? `${suggestionOptionIdPrefix}-${selectedSuggestion}` : undefined, - ref: this.inputRef - }; - if (renderControl) { - return renderControl(controlProps, inputProps, loading); - } - if (!__nextHasNoMarginBottom) { - external_wp_deprecated_default()('Bottom margin styles for wp.blockEditor.URLInput', { - since: '6.2', - version: '6.5', - hint: 'Set the `__nextHasNoMarginBottom` prop to true to start opting into the new styles, which will become the default in a future version' - }); + onPropValueChange({ + [attrName]: newAttrValue + }); + }, [attrName, onPropValueChange]); + (0,external_wp_element_namespaceObject.useLayoutEffect)(() => { + if (typeof propValue !== 'undefined') { + updateBoundAttibute(propValue, attrValue); + } else if (placeholder) { + /* + * Placeholder fallback. + * If the attribute is `src` or `href`, + * a placeholder can't be used because it is not a valid url. + * Adding this workaround until + * attributes and metadata fields types are improved and include `url`. + */ + const htmlAttribute = (0,external_wp_blocks_namespaceObject.getBlockType)(blockName).attributes[attrName].attribute; + if (htmlAttribute === 'src' || htmlAttribute === 'href') { + updateBoundAttibute(null); + return; + } + updateBoundAttibute(placeholder); } - return (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.BaseControl, { - __nextHasNoMarginBottom: __nextHasNoMarginBottom, - ...controlProps - }, (0,external_wp_element_namespaceObject.createElement)("input", { - ...inputProps - }), loading && (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.Spinner, null)); - } - renderSuggestions() { - const { - className, - __experimentalRenderSuggestions: renderSuggestions - } = this.props; - const { - showSuggestions, - suggestions, - suggestionsValue, - selectedSuggestion, - suggestionsListboxId, - suggestionOptionIdPrefix, - loading - } = this.state; - if (!showSuggestions || suggestions.length === 0) { + }, [updateBoundAttibute, propValue, attrValue, placeholder, blockName, attrName]); + return null; +}; + +/** + * BlockBindingBridge acts like a component wrapper + * that connects the bound attributes of a block + * to the source handlers. + * For this, it creates a BindingConnector for each bound attribute. + * + * @param {Object} props - The component props. + * @param {Object} props.blockProps - The BlockEdit props object. + * @param {Object} props.bindings - The block bindings settings. + * @param {Function} props.onPropValueChange - The function to call when the attribute value changes. + * @return {null} Data-handling component. Render nothing. + */ +function BlockBindingBridge({ + blockProps, + bindings, + onPropValueChange +}) { + const blockBindingsSources = unlock((0,external_wp_data_namespaceObject.useSelect)(external_wp_blocks_namespaceObject.store)).getAllBlockBindingsSources(); + return (0,external_React_.createElement)(external_React_.Fragment, null, Object.entries(bindings).map(([attrName, boundAttribute]) => { + // Bail early if the block doesn't have a valid source handler. + const source = blockBindingsSources[boundAttribute.source]; + if (!source?.useSource) { return null; } - const suggestionsListProps = { - id: suggestionsListboxId, - ref: this.autocompleteRef, - role: 'listbox' - }; - const buildSuggestionItemProps = (suggestion, index) => { - return { - role: 'option', - tabIndex: '-1', - id: `${suggestionOptionIdPrefix}-${index}`, - ref: this.bindSuggestionNode(index), - 'aria-selected': index === selectedSuggestion ? true : undefined - }; - }; - if (isFunction(renderSuggestions)) { - return renderSuggestions({ - suggestions, - selectedSuggestion, - suggestionsListProps, - buildSuggestionItemProps, - isLoading: loading, - handleSuggestionClick: this.handleOnClick, - isInitialSuggestions: !suggestionsValue?.length, - currentInputValue: suggestionsValue - }); - } - return (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.Popover, { - placement: "bottom", - focusOnMount: false - }, (0,external_wp_element_namespaceObject.createElement)("div", { - ...suggestionsListProps, - className: classnames_default()('block-editor-url-input__suggestions', `${className}__suggestions`) - }, suggestions.map((suggestion, index) => (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.Button, { - ...buildSuggestionItemProps(suggestion, index), - key: suggestion.id, - className: classnames_default()('block-editor-url-input__suggestion', { - 'is-selected': index === selectedSuggestion - }), - onClick: () => this.handleOnClick(suggestion) - }, suggestion.title)))); - } + return (0,external_React_.createElement)(BindingConnector, { + key: attrName, + attrName: attrName, + source: source, + blockProps: blockProps, + args: boundAttribute.args, + onPropValueChange: onPropValueChange + }); + })); } +const withBlockBindingSupport = (0,external_wp_compose_namespaceObject.createHigherOrderComponent)(BlockEdit => props => { + /* + * Collect and update the bound attributes + * in a separate state. + */ + const [boundAttributes, setBoundAttributes] = (0,external_wp_element_namespaceObject.useState)({}); + const updateBoundAttributes = (0,external_wp_element_namespaceObject.useCallback)(newAttributes => setBoundAttributes(prev => ({ + ...prev, + ...newAttributes + })), []); + + /* + * Create binding object filtering + * only the attributes that can be bound. + */ + const bindings = Object.fromEntries(Object.entries(props.attributes.metadata?.bindings || {}).filter(([attrName]) => canBindAttribute(props.name, attrName))); + return (0,external_React_.createElement)(external_React_.Fragment, null, Object.keys(bindings).length > 0 && (0,external_React_.createElement)(BlockBindingBridge, { + blockProps: props, + bindings: bindings, + onPropValueChange: updateBoundAttributes + }), (0,external_React_.createElement)(BlockEdit, { + ...props, + attributes: { + ...props.attributes, + ...boundAttributes + } + })); +}, 'withBlockBindingSupport'); /** - * @see https://github.com/WordPress/gutenberg/blob/HEAD/packages/block-editor/src/components/url-input/README.md + * Filters a registered block's settings to enhance a block's `edit` component + * to upgrade bound attributes. + * + * @param {WPBlockSettings} settings - Registered block settings. + * @param {string} name - Block name. + * @return {WPBlockSettings} Filtered block settings. */ -/* harmony default export */ var url_input = ((0,external_wp_compose_namespaceObject.compose)(external_wp_compose_namespaceObject.withSafeTimeout, external_wp_components_namespaceObject.withSpokenMessages, external_wp_compose_namespaceObject.withInstanceId, (0,external_wp_data_namespaceObject.withSelect)((select, props) => { - // If a link suggestions handler is already provided then - // bail. - if (isFunction(props.__experimentalFetchLinkSuggestions)) { - return; +function shimAttributeSource(settings, name) { + if (!canBindBlock(name)) { + return settings; } - const { - getSettings - } = select(store); return { - __experimentalFetchLinkSuggestions: getSettings().__experimentalFetchLinkSuggestions + ...settings, + edit: withBlockBindingSupport(settings.edit) }; -}))(URLInput)); - -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/link-control/search-create-button.js +} +(0,external_wp_hooks_namespaceObject.addFilter)('blocks.registerBlockType', 'core/editor/custom-sources-backwards-compatibility/shim-attribute-source', shimAttributeSource); +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/hooks/use-border-props.js /** - * WordPress dependencies + * Internal dependencies */ -const LinkControlSearchCreate = ({ - searchTerm, - onClick, - itemProps, - buttonText -}) => { - if (!searchTerm) { - return null; - } - let text; - if (buttonText) { - text = typeof buttonText === 'function' ? buttonText(searchTerm) : buttonText; - } else { - text = (0,external_wp_element_namespaceObject.createInterpolateElement)((0,external_wp_i18n_namespaceObject.sprintf)( /* translators: %s: search term. */ - (0,external_wp_i18n_namespaceObject.__)('Create: %s'), searchTerm), { - mark: (0,external_wp_element_namespaceObject.createElement)("mark", null) +// This utility is intended to assist where the serialization of the border +// block support is being skipped for a block but the border related CSS classes +// & styles still need to be generated so they can be applied to inner elements. + +/** + * Provides the CSS class names and inline styles for a block's border support + * attributes. + * + * @param {Object} attributes Block attributes. + * @return {Object} Border block support derived CSS classes & styles. + */ +function getBorderClassesAndStyles(attributes) { + const border = attributes.style?.border || {}; + const className = getBorderClasses(attributes); + return { + className: className || undefined, + style: getInlineStyles({ + border + }) + }; +} + +/** + * Derives the border related props for a block from its border block support + * attributes. + * + * Inline styles are forced for named colors to ensure these selections are + * reflected when themes do not load their color stylesheets in the editor. + * + * @param {Object} attributes Block attributes. + * + * @return {Object} ClassName & style props from border block support. + */ +function useBorderProps(attributes) { + const { + colors + } = useMultipleOriginColorsAndGradients(); + const borderProps = getBorderClassesAndStyles(attributes); + const { + borderColor + } = attributes; + + // Force inline styles to apply named border colors when themes do not load + // their color stylesheets in the editor. + if (borderColor) { + const borderColorObject = getMultiOriginColor({ + colors, + namedColor: borderColor }); + borderProps.style.borderColor = borderColorObject.color; } - return (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.MenuItem, { - ...itemProps, - iconPosition: "left", - icon: library_plus, - className: "block-editor-link-control__search-item", - onClick: onClick - }, text); -}; -/* harmony default export */ var search_create_button = (LinkControlSearchCreate); - -;// CONCATENATED MODULE: ./node_modules/@wordpress/icons/build-module/library/post-list.js + return borderProps; +} +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/hooks/use-shadow-props.js /** - * WordPress dependencies + * Internal dependencies */ -const postList = (0,external_wp_element_namespaceObject.createElement)(external_wp_primitives_namespaceObject.SVG, { - viewBox: "0 0 24 24", - xmlns: "http://www.w3.org/2000/svg" -}, (0,external_wp_element_namespaceObject.createElement)(external_wp_primitives_namespaceObject.Path, { - d: "M18 4H6c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm.5 14c0 .3-.2.5-.5.5H6c-.3 0-.5-.2-.5-.5V6c0-.3.2-.5.5-.5h12c.3 0 .5.2.5.5v12zM7 11h2V9H7v2zm0 4h2v-2H7v2zm3-4h7V9h-7v2zm0 4h7v-2h-7v2z" -})); -/* harmony default export */ var post_list = (postList); -;// CONCATENATED MODULE: ./node_modules/@wordpress/icons/build-module/library/page.js +// This utility is intended to assist where the serialization of the shadow +// block support is being skipped for a block but the shadow related CSS classes +// & styles still need to be generated so they can be applied to inner elements. /** - * WordPress dependencies + * Provides the CSS class names and inline styles for a block's shadow support + * attributes. + * + * @param {Object} attributes Block attributes. + * @return {Object} Shadow block support derived CSS classes & styles. */ +function getShadowClassesAndStyles(attributes) { + const shadow = attributes.style?.shadow || ''; + return { + style: getInlineStyles({ + shadow + }) + }; +} -const page = (0,external_wp_element_namespaceObject.createElement)(external_wp_primitives_namespaceObject.SVG, { - xmlns: "http://www.w3.org/2000/svg", - viewBox: "0 0 24 24" -}, (0,external_wp_element_namespaceObject.createElement)(external_wp_primitives_namespaceObject.Path, { - d: "M7 5.5h10a.5.5 0 01.5.5v12a.5.5 0 01-.5.5H7a.5.5 0 01-.5-.5V6a.5.5 0 01.5-.5zM17 4H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V6a2 2 0 00-2-2zm-1 3.75H8v1.5h8v-1.5zM8 11h8v1.5H8V11zm6 3.25H8v1.5h6v-1.5z" -})); -/* harmony default export */ var library_page = (page); +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/hooks/use-color-props.js +/** + * External dependencies + */ -;// CONCATENATED MODULE: ./node_modules/@wordpress/icons/build-module/library/tag.js /** * WordPress dependencies */ -const tag = (0,external_wp_element_namespaceObject.createElement)(external_wp_primitives_namespaceObject.SVG, { - xmlns: "http://www.w3.org/2000/svg", - viewBox: "0 0 24 24" -}, (0,external_wp_element_namespaceObject.createElement)(external_wp_primitives_namespaceObject.Path, { - d: "M20.1 11.2l-6.7-6.7c-.1-.1-.3-.2-.5-.2H5c-.4-.1-.8.3-.8.7v7.8c0 .2.1.4.2.5l6.7 6.7c.2.2.5.4.7.5s.6.2.9.2c.3 0 .6-.1.9-.2.3-.1.5-.3.8-.5l5.6-5.6c.4-.4.7-1 .7-1.6.1-.6-.2-1.2-.6-1.6zM19 13.4L13.4 19c-.1.1-.2.1-.3.2-.2.1-.4.1-.6 0-.1 0-.2-.1-.3-.2l-6.5-6.5V5.8h6.8l6.5 6.5c.2.2.2.4.2.6 0 .1 0 .3-.2.5zM9 8c-.6 0-1 .4-1 1s.4 1 1 1 1-.4 1-1-.4-1-1-1z" -})); -/* harmony default export */ var library_tag = (tag); -;// CONCATENATED MODULE: ./node_modules/@wordpress/icons/build-module/library/category.js +/** + * Internal dependencies + */ + + + + + +// The code in this file has largely been lifted from the color block support +// hook. +// +// This utility is intended to assist where the serialization of the colors +// block support is being skipped for a block but the color related CSS classes +// & styles still need to be generated so they can be applied to inner elements. /** - * WordPress dependencies + * Provides the CSS class names and inline styles for a block's color support + * attributes. + * + * @param {Object} attributes Block attributes. + * + * @return {Object} Color block support derived CSS classes & styles. */ +function getColorClassesAndStyles(attributes) { + const { + backgroundColor, + textColor, + gradient, + style + } = attributes; -const category = (0,external_wp_element_namespaceObject.createElement)(external_wp_primitives_namespaceObject.SVG, { - viewBox: "0 0 24 24", - xmlns: "http://www.w3.org/2000/svg" -}, (0,external_wp_element_namespaceObject.createElement)(external_wp_primitives_namespaceObject.Path, { - d: "M6 5.5h3a.5.5 0 01.5.5v3a.5.5 0 01-.5.5H6a.5.5 0 01-.5-.5V6a.5.5 0 01.5-.5zM4 6a2 2 0 012-2h3a2 2 0 012 2v3a2 2 0 01-2 2H6a2 2 0 01-2-2V6zm11-.5h3a.5.5 0 01.5.5v3a.5.5 0 01-.5.5h-3a.5.5 0 01-.5-.5V6a.5.5 0 01.5-.5zM13 6a2 2 0 012-2h3a2 2 0 012 2v3a2 2 0 01-2 2h-3a2 2 0 01-2-2V6zm5 8.5h-3a.5.5 0 00-.5.5v3a.5.5 0 00.5.5h3a.5.5 0 00.5-.5v-3a.5.5 0 00-.5-.5zM15 13a2 2 0 00-2 2v3a2 2 0 002 2h3a2 2 0 002-2v-3a2 2 0 00-2-2h-3zm-9 1.5h3a.5.5 0 01.5.5v3a.5.5 0 01-.5.5H6a.5.5 0 01-.5-.5v-3a.5.5 0 01.5-.5zM4 15a2 2 0 012-2h3a2 2 0 012 2v3a2 2 0 01-2 2H6a2 2 0 01-2-2v-3z", - fillRule: "evenodd", - clipRule: "evenodd" -})); -/* harmony default export */ var library_category = (category); + // Collect color CSS classes. + const backgroundClass = getColorClassName('background-color', backgroundColor); + const textClass = getColorClassName('color', textColor); + const gradientClass = __experimentalGetGradientClass(gradient); + const hasGradient = gradientClass || style?.color?.gradient; -;// CONCATENATED MODULE: ./node_modules/@wordpress/icons/build-module/library/file.js + // Determine color CSS class name list. + const className = classnames_default()(textClass, gradientClass, { + // Don't apply the background class if there's a gradient. + [backgroundClass]: !hasGradient && !!backgroundClass, + 'has-text-color': textColor || style?.color?.text, + 'has-background': backgroundColor || style?.color?.background || gradient || style?.color?.gradient, + 'has-link-color': style?.elements?.link?.color + }); + + // Collect inline styles for colors. + const colorStyles = style?.color || {}; + const styleProp = getInlineStyles({ + color: colorStyles + }); + return { + className: className || undefined, + style: styleProp + }; +} /** - * WordPress dependencies + * Determines the color related props for a block derived from its color block + * support attributes. + * + * Inline styles are forced for named colors to ensure these selections are + * reflected when themes do not load their color stylesheets in the editor. + * + * @param {Object} attributes Block attributes. + * + * @return {Object} ClassName & style props from colors block support. */ +function useColorProps(attributes) { + const { + backgroundColor, + textColor, + gradient + } = attributes; + const [userPalette, themePalette, defaultPalette, userGradients, themeGradients, defaultGradients] = use_settings_useSettings('color.palette.custom', 'color.palette.theme', 'color.palette.default', 'color.gradients.custom', 'color.gradients.theme', 'color.gradients.default'); + const colors = (0,external_wp_element_namespaceObject.useMemo)(() => [...(userPalette || []), ...(themePalette || []), ...(defaultPalette || [])], [userPalette, themePalette, defaultPalette]); + const gradients = (0,external_wp_element_namespaceObject.useMemo)(() => [...(userGradients || []), ...(themeGradients || []), ...(defaultGradients || [])], [userGradients, themeGradients, defaultGradients]); + const colorProps = getColorClassesAndStyles(attributes); -const file = (0,external_wp_element_namespaceObject.createElement)(external_wp_primitives_namespaceObject.SVG, { - viewBox: "0 0 24 24", - xmlns: "http://www.w3.org/2000/svg" -}, (0,external_wp_element_namespaceObject.createElement)(external_wp_primitives_namespaceObject.Path, { - d: "M19 6.2h-5.9l-.6-1.1c-.3-.7-1-1.1-1.8-1.1H5c-1.1 0-2 .9-2 2v11.8c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V8.2c0-1.1-.9-2-2-2zm.5 11.6c0 .3-.2.5-.5.5H5c-.3 0-.5-.2-.5-.5V6c0-.3.2-.5.5-.5h5.8c.2 0 .4.1.4.3l1 2H19c.3 0 .5.2.5.5v9.5z" -})); -/* harmony default export */ var library_file = (file); + // Force inline styles to apply colors when themes do not load their color + // stylesheets in the editor. + if (backgroundColor) { + const backgroundColorObject = getColorObjectByAttributeValues(colors, backgroundColor); + colorProps.style.backgroundColor = backgroundColorObject.color; + } + if (gradient) { + colorProps.style.background = getGradientValueBySlug(gradients, gradient); + } + if (textColor) { + const textColorObject = getColorObjectByAttributeValues(colors, textColor); + colorProps.style.color = textColorObject.color; + } + return colorProps; +} -;// CONCATENATED MODULE: ./node_modules/@wordpress/icons/build-module/library/globe.js +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/hooks/use-spacing-props.js +/** + * Internal dependencies + */ + + +// This utility is intended to assist where the serialization of the spacing +// block support is being skipped for a block but the spacing related CSS +// styles still need to be generated so they can be applied to inner elements. /** - * WordPress dependencies + * Provides the CSS class names and inline styles for a block's spacing support + * attributes. + * + * @param {Object} attributes Block attributes. + * + * @return {Object} Spacing block support derived CSS classes & styles. */ +function getSpacingClassesAndStyles(attributes) { + const { + style + } = attributes; -const globe = (0,external_wp_element_namespaceObject.createElement)(external_wp_primitives_namespaceObject.SVG, { - xmlns: "http://www.w3.org/2000/svg", - viewBox: "0 0 24 24" -}, (0,external_wp_element_namespaceObject.createElement)(external_wp_primitives_namespaceObject.Path, { - d: "M12 3.3c-4.8 0-8.8 3.9-8.8 8.8 0 4.8 3.9 8.8 8.8 8.8 4.8 0 8.8-3.9 8.8-8.8s-4-8.8-8.8-8.8zm6.5 5.5h-2.6C15.4 7.3 14.8 6 14 5c2 .6 3.6 2 4.5 3.8zm.7 3.2c0 .6-.1 1.2-.2 1.8h-2.9c.1-.6.1-1.2.1-1.8s-.1-1.2-.1-1.8H19c.2.6.2 1.2.2 1.8zM12 18.7c-1-.7-1.8-1.9-2.3-3.5h4.6c-.5 1.6-1.3 2.9-2.3 3.5zm-2.6-4.9c-.1-.6-.1-1.1-.1-1.8 0-.6.1-1.2.1-1.8h5.2c.1.6.1 1.1.1 1.8s-.1 1.2-.1 1.8H9.4zM4.8 12c0-.6.1-1.2.2-1.8h2.9c-.1.6-.1 1.2-.1 1.8 0 .6.1 1.2.1 1.8H5c-.2-.6-.2-1.2-.2-1.8zM12 5.3c1 .7 1.8 1.9 2.3 3.5H9.7c.5-1.6 1.3-2.9 2.3-3.5zM10 5c-.8 1-1.4 2.3-1.8 3.8H5.5C6.4 7 8 5.6 10 5zM5.5 15.3h2.6c.4 1.5 1 2.8 1.8 3.7-1.8-.6-3.5-2-4.4-3.7zM14 19c.8-1 1.4-2.2 1.8-3.7h2.6C17.6 17 16 18.4 14 19z" -})); -/* harmony default export */ var library_globe = (globe); + // Collect inline styles for spacing. + const spacingStyles = style?.spacing || {}; + const styleProp = getInlineStyles({ + spacing: spacingStyles + }); + return { + style: styleProp + }; +} + +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/hooks/use-typography-props.js +/** + * External dependencies + */ -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/link-control/search-item.js /** * WordPress dependencies */ +/** + * Internal dependencies + */ -const ICONS_MAP = { - post: post_list, - page: library_page, - post_tag: library_tag, - category: library_category, - attachment: library_file -}; -function SearchItemIcon({ - isURL, - suggestion -}) { - let icon = null; - if (isURL) { - icon = library_globe; - } else if (suggestion.type in ICONS_MAP) { - icon = ICONS_MAP[suggestion.type]; - } - if (icon) { - return (0,external_wp_element_namespaceObject.createElement)(build_module_icon, { - className: "block-editor-link-control__search-item-icon", - icon: icon - }); - } - return null; -} +/* + * This utility is intended to assist where the serialization of the typography + * block support is being skipped for a block but the typography related CSS + * styles still need to be generated so they can be applied to inner elements. + */ /** - * Adds a leading slash to a url if it doesn't already have one. - * @param {string} url the url to add a leading slash to. - * @return {string} the url with a leading slash. + * Provides the CSS class names and inline styles for a block's typography support + * attributes. + * + * @param {Object} attributes Block attributes. + * @param {Object|boolean} settings Merged theme.json settings + * + * @return {Object} Typography block support derived CSS classes & styles. */ -function addLeadingSlash(url) { - const trimmedURL = url?.trim(); - if (!trimmedURL?.length) return url; - return url?.replace(/^\/?/, '/'); -} -function removeTrailingSlash(url) { - const trimmedURL = url?.trim(); - if (!trimmedURL?.length) return url; - return url?.replace(/\/$/, ''); +function getTypographyClassesAndStyles(attributes, settings) { + const { + kebabCase + } = unlock(external_wp_components_namespaceObject.privateApis); + let typographyStyles = attributes?.style?.typography || {}; + const fluidTypographySettings = getFluidTypographyOptionsFromSettings(settings); + typographyStyles = { + ...typographyStyles, + fontSize: getTypographyFontSizeValue({ + size: attributes?.style?.typography?.fontSize + }, fluidTypographySettings) + }; + const style = getInlineStyles({ + typography: typographyStyles + }); + const fontFamilyClassName = !!attributes?.fontFamily ? `has-${kebabCase(attributes.fontFamily)}-font-family` : ''; + const className = classnames_default()(fontFamilyClassName, getFontSizeClass(attributes?.fontSize)); + return { + className, + style + }; } -const partialRight = (fn, ...partialArgs) => (...args) => fn(...args, ...partialArgs); -const defaultTo = d => v => { - return v === null || v === undefined || v !== v ? d : v; -}; +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/hooks/use-cached-truthy.js /** - * Prepares a URL for display in the UI. - * - decodes the URL. - * - filters it (removes protocol, www, etc.). - * - truncates it if necessary. - * - adds a leading slash. - * @param {string} url the url. - * @return {string} the processed url to display. + * WordPress dependencies */ -function getURLForDisplay(url) { - if (!url) return url; - return (0,external_wp_compose_namespaceObject.pipe)(external_wp_url_namespaceObject.safeDecodeURI, external_wp_url_namespaceObject.getPath, defaultTo(''), partialRight(external_wp_url_namespaceObject.filterURLForDisplay, 24), removeTrailingSlash, addLeadingSlash)(url); -} -const LinkControlSearchItem = ({ - itemProps, - suggestion, - searchTerm, - onClick, - isURL = false, - shouldShowType = false -}) => { - const info = isURL ? (0,external_wp_i18n_namespaceObject.__)('Press ENTER to add this link') : getURLForDisplay(suggestion.url); - return (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.MenuItem, { - ...itemProps, - info: info, - iconPosition: "left", - icon: (0,external_wp_element_namespaceObject.createElement)(SearchItemIcon, { - suggestion: suggestion, - isURL: isURL - }), - onClick: onClick, - shortcut: shouldShowType && getVisualTypeName(suggestion), - className: "block-editor-link-control__search-item" - }, (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.TextHighlight - // The component expects a plain text string. - , { - text: (0,external_wp_dom_namespaceObject.__unstableStripHTML)(suggestion.title), - highlight: searchTerm - })); -}; -function getVisualTypeName(suggestion) { - if (suggestion.isFrontPage) { - return 'front page'; - } - // Rename 'post_tag' to 'tag'. Ideally, the API would return the localised CPT or taxonomy label. - return suggestion.type === 'post_tag' ? 'tag' : suggestion.type; + +/** + * Keeps an up-to-date copy of the passed value and returns it. If value becomes falsy, it will return the last truthy copy. + * + * @param {any} value + * @return {any} value + */ +function useCachedTruthy(value) { + const [cachedValue, setCachedValue] = (0,external_wp_element_namespaceObject.useState)(value); + (0,external_wp_element_namespaceObject.useEffect)(() => { + if (value) { + setCachedValue(value); + } + }, [value]); + return cachedValue; } -/* harmony default export */ var search_item = (LinkControlSearchItem); -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/link-control/constants.js +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/hooks/index.js /** - * WordPress dependencies + * Internal dependencies */ -// Used as a unique identifier for the "Create" option within search results. -// Used to help distinguish the "Create" suggestion within the search results in -// order to handle it as a unique case. -const CREATE_TYPE = '__CREATE__'; -const TEL_TYPE = 'tel'; -const URL_TYPE = 'link'; -const MAILTO_TYPE = 'mailto'; -const INTERNAL_TYPE = 'internal'; -const LINK_ENTRY_TYPES = [URL_TYPE, MAILTO_TYPE, TEL_TYPE, INTERNAL_TYPE]; -const DEFAULT_LINK_SETTINGS = [{ - id: 'opensInNewTab', - title: (0,external_wp_i18n_namespaceObject.__)('Open in new tab') -}]; -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/link-control/search-results.js -/** - * WordPress dependencies - */ + + + + + + + + + + + + + + + + + +createBlockEditFilter([align, hooks_anchor, custom_class_name, style, duotone, position, layout, content_lock_ui, block_hooks, block_renaming].filter(Boolean)); +createBlockListBlockFilter([align, style, color, dimensions, duotone, font_family, font_size, border, position, layout_child]); +createBlockSaveFilter([align, hooks_anchor, aria_label, custom_class_name, border, color, style, font_family, font_size]); + + + + + + + + + + +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/colors/with-colors.js + /** - * External dependencies + * WordPress dependencies */ + + /** * Internal dependencies */ -function LinkControlSearchResults({ - instanceId, - withCreateSuggestion, - currentInputValue, - handleSuggestionClick, - suggestionsListProps, - buildSuggestionItemProps, - suggestions, - selectedSuggestion, - isLoading, - isInitialSuggestions, - createSuggestionButtonText, - suggestionsQuery -}) { - const resultsListClasses = classnames_default()('block-editor-link-control__search-results', { - 'is-loading': isLoading - }); - const isSingleDirectEntryResult = suggestions.length === 1 && LINK_ENTRY_TYPES.includes(suggestions[0].type); - const shouldShowCreateSuggestion = withCreateSuggestion && !isSingleDirectEntryResult && !isInitialSuggestions; - // If the query has a specified type, then we can skip showing them in the result. See #24839. - const shouldShowSuggestionsTypes = !suggestionsQuery?.type; - - // According to guidelines aria-label should be added if the label - // itself is not visible. - // See: https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/listbox_role - const searchResultsLabelId = `block-editor-link-control-search-results-label-${instanceId}`; - const labelText = isInitialSuggestions ? (0,external_wp_i18n_namespaceObject.__)('Suggestions') : (0,external_wp_i18n_namespaceObject.sprintf)( /* translators: %s: search term. */ - (0,external_wp_i18n_namespaceObject.__)('Search results for "%s"'), currentInputValue); - const searchResultsLabel = (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.VisuallyHidden, { - id: searchResultsLabelId - }, labelText); - return (0,external_wp_element_namespaceObject.createElement)("div", { - className: "block-editor-link-control__search-results-wrapper" - }, searchResultsLabel, (0,external_wp_element_namespaceObject.createElement)("div", { - ...suggestionsListProps, - className: resultsListClasses, - "aria-labelledby": searchResultsLabelId - }, (0,external_wp_element_namespaceObject.createElement)(external_wp_components_namespaceObject.MenuGroup, null, suggestions.map((suggestion, index) => { - if (shouldShowCreateSuggestion && CREATE_TYPE === suggestion.type) { - return (0,external_wp_element_namespaceObject.createElement)(search_create_button, { - searchTerm: currentInputValue, - buttonText: createSuggestionButtonText, - onClick: () => handleSuggestionClick(suggestion) - // Intentionally only using `type` here as - // the constant is enough to uniquely - // identify the single "CREATE" suggestion. - , - key: suggestion.type, - itemProps: buildSuggestionItemProps(suggestion, index), - isSelected: index === selectedSuggestion - }); - } - // If we're not handling "Create" suggestions above then - // we don't want them in the main results so exit early. - if (CREATE_TYPE === suggestion.type) { - return null; - } - return (0,external_wp_element_namespaceObject.createElement)(search_item, { - key: `${suggestion.id}-${suggestion.type}`, - itemProps: buildSuggestionItemProps(suggestion, index), - suggestion: suggestion, - index: index, - onClick: () => { - handleSuggestionClick(suggestion); - }, - isSelected: index === selectedSuggestion, - isURL: LINK_ENTRY_TYPES.includes(suggestion.type), - searchTerm: currentInputValue, - shouldShowType: shouldShowSuggestionsTypes, - isFrontPage: suggestion?.isFrontPage - }); - })))); -} +/** + * Capitalizes the first letter in a string. + * + * @param {string} str The string whose first letter the function will capitalize. + * + * @return {string} Capitalized string. + */ +const upperFirst = ([firstLetter, ...rest]) => firstLetter.toUpperCase() + rest.join(''); -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/link-control/is-url-like.js /** - * WordPress dependencies + * Higher order component factory for injecting the `colorsArray` argument as + * the colors prop in the `withCustomColors` HOC. + * + * @param {Array} colorsArray An array of color objects. + * + * @return {Function} The higher order component. */ +const withCustomColorPalette = colorsArray => (0,external_wp_compose_namespaceObject.createHigherOrderComponent)(WrappedComponent => props => (0,external_React_.createElement)(WrappedComponent, { + ...props, + colors: colorsArray +}), 'withCustomColorPalette'); +/** + * Higher order component factory for injecting the editor colors as the + * `colors` prop in the `withColors` HOC. + * + * @return {Function} The higher order component. + */ +const withEditorColorPalette = () => (0,external_wp_compose_namespaceObject.createHigherOrderComponent)(WrappedComponent => props => { + const [userPalette, themePalette, defaultPalette] = use_settings_useSettings('color.palette.custom', 'color.palette.theme', 'color.palette.default'); + const allColors = (0,external_wp_element_namespaceObject.useMemo)(() => [...(userPalette || []), ...(themePalette || []), ...(defaultPalette || [])], [userPalette, themePalette, defaultPalette]); + return (0,external_React_.createElement)(WrappedComponent, { + ...props, + colors: allColors + }); +}, 'withEditorColorPalette'); /** - * Determines whether a given value could be a URL. Note this does not - * guarantee the value is a URL only that it looks like it might be one. For - * example, just because a string has `www.` in it doesn't make it a URL, - * but it does make it highly likely that it will be so in the context of - * creating a link it makes sense to treat it like one. + * Helper function used with `createHigherOrderComponent` to create + * higher order components for managing color logic. * - * @param {string} val the candidate for being URL-like (or not). + * @param {Array} colorTypes An array of color types (e.g. 'backgroundColor, borderColor). + * @param {Function} withColorPalette A HOC for injecting the 'colors' prop into the WrappedComponent. * - * @return {boolean} whether or not the value is potentially a URL. + * @return {Component} The component that can be used as a HOC. */ -function isURLLike(val) { - const hasSpaces = val.includes(' '); - if (hasSpaces) { - return false; - } - const protocol = (0,external_wp_url_namespaceObject.getProtocol)(val); - const protocolIsValid = (0,external_wp_url_namespaceObject.isValidProtocol)(protocol); - const mayBeTLD = hasPossibleTLD(val); - const isWWW = val?.startsWith('www.'); - const isInternal = val?.startsWith('#') && (0,external_wp_url_namespaceObject.isValidFragment)(val); - return protocolIsValid || isWWW || isInternal || mayBeTLD; +function createColorHOC(colorTypes, withColorPalette) { + const { + kebabCase + } = unlock(external_wp_components_namespaceObject.privateApis); + const colorMap = colorTypes.reduce((colorObject, colorType) => { + return { + ...colorObject, + ...(typeof colorType === 'string' ? { + [colorType]: kebabCase(colorType) + } : colorType) + }; + }, {}); + return (0,external_wp_compose_namespaceObject.compose)([withColorPalette, WrappedComponent => { + return class extends external_wp_element_namespaceObject.Component { + constructor(props) { + super(props); + this.setters = this.createSetters(); + this.colorUtils = { + getMostReadableColor: this.getMostReadableColor.bind(this) + }; + this.state = {}; + } + getMostReadableColor(colorValue) { + const { + colors + } = this.props; + return getMostReadableColor(colors, colorValue); + } + createSetters() { + return Object.keys(colorMap).reduce((settersAccumulator, colorAttributeName) => { + const upperFirstColorAttributeName = upperFirst(colorAttributeName); + const customColorAttributeName = `custom${upperFirstColorAttributeName}`; + settersAccumulator[`set${upperFirstColorAttributeName}`] = this.createSetColor(colorAttributeName, customColorAttributeName); + return settersAccumulator; + }, {}); + } + createSetColor(colorAttributeName, customColorAttributeName) { + return colorValue => { + const colorObject = getColorObjectByColorValue(this.props.colors, colorValue); + this.props.setAttributes({ + [colorAttributeName]: colorObject && colorObject.slug ? colorObject.slug : undefined, + [customColorAttributeName]: colorObject && colorObject.slug ? undefined : colorValue + }); + }; + } + static getDerivedStateFromProps({ + attributes, + colors + }, previousState) { + return Object.entries(colorMap).reduce((newState, [colorAttributeName, colorContext]) => { + const colorObject = getColorObjectByAttributeValues(colors, attributes[colorAttributeName], attributes[`custom${upperFirst(colorAttributeName)}`]); + const previousColorObject = previousState[colorAttributeName]; + const previousColor = previousColorObject?.color; + /** + * The "and previousColorObject" condition checks that a previous color object was already computed. + * At the start previousColorObject and colorValue are both equal to undefined + * bus as previousColorObject does not exist we should compute the object. + */ + if (previousColor === colorObject.color && previousColorObject) { + newState[colorAttributeName] = previousColorObject; + } else { + newState[colorAttributeName] = { + ...colorObject, + class: getColorClassName(colorContext, colorObject.slug) + }; + } + return newState; + }, {}); + } + render() { + return (0,external_React_.createElement)(WrappedComponent, { + ...this.props, + colors: undefined, + ...this.state, + ...this.setters, + colorUtils: this.colorUtils + }); + } + }; + }]); } /** - * Checks if a given URL has a valid Top-Level Domain (TLD). + * A higher-order component factory for creating a 'withCustomColors' HOC, which handles color logic + * for class generation color value, retrieval and color attribute setting. * - * @param {string} url - The URL to check. - * @param {number} maxLength - The maximum length of the TLD. - * @return {boolean} Returns true if the URL has a valid TLD, false otherwise. + * Use this higher-order component to work with a custom set of colors. + * + * @example + * + * ```jsx + * const CUSTOM_COLORS = [ { name: 'Red', slug: 'red', color: '#ff0000' }, { name: 'Blue', slug: 'blue', color: '#0000ff' } ]; + * const withCustomColors = createCustomColorsHOC( CUSTOM_COLORS ); + * // ... + * export default compose( + * withCustomColors( 'backgroundColor', 'borderColor' ), + * MyColorfulComponent, + * ); + * ``` + * + * @param {Array} colorsArray The array of color objects (name, slug, color, etc... ). + * + * @return {Function} Higher-order component. */ -function hasPossibleTLD(url, maxLength = 6) { - // Clean the URL by removing anything after the first occurrence of "?" or "#". - const cleanedURL = url.split(/[?#]/)[0]; - - // Regular expression explanation: - // - (?<=\S) : Positive lookbehind assertion to ensure there is at least one non-whitespace character before the TLD - // - \. : Matches a literal dot (.) - // - [a-zA-Z_]{2,maxLength} : Matches 2 to maxLength letters or underscores, representing the TLD - // - (?:\/|$) : Non-capturing group that matches either a forward slash (/) or the end of the string - const regex = new RegExp(`(?<=\\S)\\.(?:[a-zA-Z_]{2,${maxLength}})(?:\\/|$)`); - return regex.test(cleanedURL); +function createCustomColorsHOC(colorsArray) { + return (...colorTypes) => { + const withColorPalette = withCustomColorPalette(colorsArray); + return (0,external_wp_compose_namespaceObject.createHigherOrderComponent)(createColorHOC(colorTypes, withColorPalette), 'withCustomColors'); + }; } -;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/link-control/use-search-handler.js /** - * WordPress dependencies + * A higher-order component, which handles color logic for class generation color value, retrieval and color attribute setting. + * + * For use with the default editor/theme color palette. + * + * @example + * + * ```jsx + * export default compose( + * withColors( 'backgroundColor', { textColor: 'color' } ), + * MyColorfulComponent, + * ); + * ``` + * + * @param {...(Object|string)} colorTypes The arguments can be strings or objects. If the argument is an object, + * it should contain the color attribute name as key and the color context as value. + * If the argument is a string the value should be the color attribute name, + * the color context is computed by applying a kebab case transform to the value. + * Color context represents the context/place where the color is going to be used. + * The class name of the color is generated using 'has' followed by the color name + * and ending with the color context all in kebab case e.g: has-green-background-color. + * + * @return {Function} Higher-order component. */ +function withColors(...colorTypes) { + const withColorPalette = withEditorColorPalette(); + return (0,external_wp_compose_namespaceObject.createHigherOrderComponent)(createColorHOC(colorTypes, withColorPalette), 'withColors'); +} +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/colors/index.js -/** - * Internal dependencies - */ +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/gradients/index.js +;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/font-sizes/font-size-picker.js -const handleNoop = () => Promise.resolve([]); -const handleDirectEntry = val => { - let type = URL_TYPE; - const protocol = (0,external_wp_url_namespaceObject.getProtocol)(val) || ''; - if (protocol.includes('mailto')) { - type = MAILTO_TYPE; - } - if (protocol.includes('tel')) { - type = TEL_TYPE; - } - if (val?.startsWith('#')) { - type = INTERNAL_TYPE; - } - return Promise.resolve([{ - id: val, - title: val, - url: type === 'URL' ? (0,external_wp_url_namespaceObject.prependHTTP)(val) : val, - type - }]); -}; -const handleEntitySearch = async (val, suggestionsQuery, fetchSearchSuggestions, withCreateSuggestion, pageOnFront) => { - const { - isInitialSuggestions - } = suggestionsQuery; - const results = await fetchSearchSuggestions(val, suggestionsQuery); +/** + * WordPress dependencies + */ - // Identify front page and update type to match. - results.map(result => { - if (Number(result.id) === pageOnFront) { - result.isFrontPage = true; - return result; - } - return result; - }); - // If displaying initial suggestions just return plain results. - if (isInitialSuggestions) { - return results; - } +/** + * Internal dependencies + */ - // Here we append a faux suggestion to represent a "CREATE" option. This - // is detected in the rendering of the search results and handled as a - // special case. This is currently necessary because the suggestions - // dropdown will only appear if there are valid suggestions and - // therefore unless the create option is a suggestion it will not - // display in scenarios where there are no results returned from the - // API. In addition promoting CREATE to a first class suggestion affords - // the a11y benefits afforded by `URLInput` to all suggestions (eg: - // keyboard handling, ARIA roles...etc). - // - // Note also that the value of the `title` and `url` properties must correspond - // to the text value of the ``. This is because `title` is used - // when creating the suggestion. Similarly `url` is used when using keyboard to select - // the suggestion (the