@wordpress/block-editor 16.0.0 → 16.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -0
- package/README.md +8 -0
- package/build/components/alignment-control/ui.cjs +2 -4
- package/build/components/alignment-control/ui.cjs.map +2 -2
- package/build/components/background-image-control/index.cjs +40 -10
- package/build/components/background-image-control/index.cjs.map +2 -2
- package/build/components/block-alignment-control/ui.cjs +5 -3
- package/build/components/block-alignment-control/ui.cjs.map +2 -2
- package/build/components/block-inspector/index.cjs +73 -46
- package/build/components/block-inspector/index.cjs.map +2 -2
- package/build/components/block-list/block-html.cjs +4 -1
- package/build/components/block-list/block-html.cjs.map +2 -2
- package/build/components/block-list/use-block-props/index.cjs +5 -2
- package/build/components/block-list/use-block-props/index.cjs.map +2 -2
- package/build/components/block-list/use-block-props/use-focus-handler.cjs +2 -3
- package/build/components/block-list/use-block-props/use-focus-handler.cjs.map +2 -2
- package/build/components/block-list/use-block-props/use-register-block-event-handlers.cjs +48 -0
- package/build/components/block-list/use-block-props/use-register-block-event-handlers.cjs.map +7 -0
- package/build/components/block-parent-selector/index.cjs +2 -2
- package/build/components/block-parent-selector/index.cjs.map +2 -2
- package/build/components/block-toolbar/switch-section-style.cjs +4 -2
- package/build/components/block-toolbar/switch-section-style.cjs.map +3 -3
- package/build/components/block-tools/empty-block-inserter.cjs +2 -7
- package/build/components/block-tools/empty-block-inserter.cjs.map +2 -2
- package/build/components/block-vertical-alignment-control/ui.cjs +3 -5
- package/build/components/block-vertical-alignment-control/ui.cjs.map +2 -2
- package/build/components/border-radius-control/utils.cjs +8 -58
- package/build/components/border-radius-control/utils.cjs.map +2 -2
- package/build/components/colors-gradients/control.cjs +5 -3
- package/build/components/colors-gradients/control.cjs.map +2 -2
- package/build/components/dimension-control/index.cjs +30 -20
- package/build/components/dimension-control/index.cjs.map +3 -3
- package/build/components/dimensions-tool/aspect-ratio-tool.cjs +9 -2
- package/build/components/dimensions-tool/aspect-ratio-tool.cjs.map +3 -3
- package/build/components/editor-styles/index.cjs +9 -1
- package/build/components/editor-styles/index.cjs.map +2 -2
- package/build/components/global-styles/background-panel.cjs +66 -25
- package/build/components/global-styles/background-panel.cjs.map +3 -3
- package/build/components/global-styles/border-panel.cjs +153 -55
- package/build/components/global-styles/border-panel.cjs.map +3 -3
- package/build/components/global-styles/color-gradient-dropdown-item.cjs +46 -14
- package/build/components/global-styles/color-gradient-dropdown-item.cjs.map +3 -3
- package/build/components/global-styles/color-panel.cjs +63 -17
- package/build/components/global-styles/color-panel.cjs.map +2 -2
- package/build/components/global-styles/dimensions-panel.cjs +242 -64
- package/build/components/global-styles/dimensions-panel.cjs.map +3 -3
- package/build/components/global-styles/filters-panel.cjs +59 -20
- package/build/components/global-styles/filters-panel.cjs.map +3 -3
- package/build/components/global-styles/hooks.cjs +4 -1
- package/build/components/global-styles/hooks.cjs.map +2 -2
- package/build/components/global-styles/inheritance/index.cjs +142 -0
- package/build/components/global-styles/inheritance/index.cjs.map +7 -0
- package/build/components/global-styles/inherited-value-context.cjs +173 -0
- package/build/components/global-styles/inherited-value-context.cjs.map +7 -0
- package/build/components/global-styles/shadow-panel-components.cjs +38 -20
- package/build/components/global-styles/shadow-panel-components.cjs.map +2 -2
- package/build/components/global-styles/state-control.cjs +1 -1
- package/build/components/global-styles/state-control.cjs.map +2 -2
- package/build/components/global-styles/typography-panel.cjs +271 -78
- package/build/components/global-styles/typography-panel.cjs.map +3 -3
- package/build/components/inspector-controls-tabs/styles-tab.cjs +10 -1
- package/build/components/inspector-controls-tabs/styles-tab.cjs.map +2 -2
- package/build/components/line-height-control/index.cjs +1 -1
- package/build/components/line-height-control/index.cjs.map +2 -2
- package/build/components/link-control/index.cjs +3 -1
- package/build/components/link-control/index.cjs.map +2 -2
- package/build/components/preset-input-control/index.cjs +2 -1
- package/build/components/preset-input-control/index.cjs.map +2 -2
- package/build/components/provider/block-refs-provider.cjs +8 -2
- package/build/components/provider/block-refs-provider.cjs.map +2 -2
- package/build/components/provider/index.cjs +1 -1
- package/build/components/provider/index.cjs.map +2 -2
- package/build/components/provider/use-block-sync.cjs +18 -6
- package/build/components/provider/use-block-sync.cjs.map +2 -2
- package/build/components/provider/use-media-upload-settings.cjs +3 -1
- package/build/components/provider/use-media-upload-settings.cjs.map +2 -2
- package/build/components/rich-text/index.cjs +23 -13
- package/build/components/rich-text/index.cjs.map +2 -2
- package/build/components/spacing-sizes-control/utils.cjs +6 -19
- package/build/components/spacing-sizes-control/utils.cjs.map +2 -2
- package/build/components/text-indent-control/index.cjs +8 -2
- package/build/components/text-indent-control/index.cjs.map +2 -2
- package/build/components/url-input/index.cjs +303 -344
- package/build/components/url-input/index.cjs.map +3 -3
- package/build/components/writing-flow/index.cjs +2 -0
- package/build/components/writing-flow/index.cjs.map +3 -3
- package/build/components/writing-flow/use-arrow-nav.cjs +24 -1
- package/build/components/writing-flow/use-arrow-nav.cjs.map +2 -2
- package/build/components/writing-flow/use-click-selection.cjs +28 -2
- package/build/components/writing-flow/use-click-selection.cjs.map +2 -2
- package/build/components/writing-flow/use-editable-root-event-handlers.cjs +179 -0
- package/build/components/writing-flow/use-editable-root-event-handlers.cjs.map +7 -0
- package/build/components/writing-flow/use-editable-root.cjs +5 -42
- package/build/components/writing-flow/use-editable-root.cjs.map +2 -2
- package/build/components/writing-flow/use-home-end.cjs +1 -1
- package/build/components/writing-flow/use-home-end.cjs.map +2 -2
- package/build/components/writing-flow/use-selection-observer.cjs +17 -13
- package/build/components/writing-flow/use-selection-observer.cjs.map +2 -2
- package/build/components/writing-flow/utils.cjs +3 -14
- package/build/components/writing-flow/utils.cjs.map +2 -2
- package/build/hooks/background.cjs +13 -10
- package/build/hooks/background.cjs.map +2 -2
- package/build/hooks/block-style-variation.cjs +4 -45
- package/build/hooks/block-style-variation.cjs.map +3 -3
- package/build/hooks/border.cjs +19 -4
- package/build/hooks/border.cjs.map +2 -2
- package/build/hooks/custom-css.cjs +6 -2
- package/build/hooks/custom-css.cjs.map +2 -2
- package/build/hooks/dimensions.cjs +15 -4
- package/build/hooks/dimensions.cjs.map +2 -2
- package/build/hooks/duotone.cjs +26 -14
- package/build/hooks/duotone.cjs.map +3 -3
- package/build/hooks/elements.cjs +14 -4
- package/build/hooks/elements.cjs.map +2 -2
- package/build/hooks/line-height.cjs +1 -2
- package/build/hooks/line-height.cjs.map +2 -2
- package/build/hooks/typography.cjs +13 -4
- package/build/hooks/typography.cjs.map +2 -2
- package/build/private-apis.cjs +9 -5
- package/build/private-apis.cjs.map +3 -3
- package/build/store/actions.cjs +9 -4
- package/build/store/actions.cjs.map +2 -2
- package/build/store/private-selectors.cjs +28 -1
- package/build/store/private-selectors.cjs.map +2 -2
- package/build/store/reducer.cjs +6 -6
- package/build/store/reducer.cjs.map +2 -2
- package/build/store/selectors.cjs +23 -0
- package/build/store/selectors.cjs.map +2 -2
- package/build/utils/native-undo.cjs +55 -0
- package/build/utils/native-undo.cjs.map +7 -0
- package/build-module/components/alignment-control/ui.mjs +2 -4
- package/build-module/components/alignment-control/ui.mjs.map +2 -2
- package/build-module/components/background-image-control/index.mjs +43 -10
- package/build-module/components/background-image-control/index.mjs.map +2 -2
- package/build-module/components/block-alignment-control/ui.mjs +5 -3
- package/build-module/components/block-alignment-control/ui.mjs.map +2 -2
- package/build-module/components/block-inspector/index.mjs +74 -46
- package/build-module/components/block-inspector/index.mjs.map +2 -2
- package/build-module/components/block-list/block-html.mjs +4 -1
- package/build-module/components/block-list/block-html.mjs.map +2 -2
- package/build-module/components/block-list/use-block-props/index.mjs +5 -2
- package/build-module/components/block-list/use-block-props/index.mjs.map +2 -2
- package/build-module/components/block-list/use-block-props/use-focus-handler.mjs +2 -3
- package/build-module/components/block-list/use-block-props/use-focus-handler.mjs.map +2 -2
- package/build-module/components/block-list/use-block-props/use-register-block-event-handlers.mjs +28 -0
- package/build-module/components/block-list/use-block-props/use-register-block-event-handlers.mjs.map +7 -0
- package/build-module/components/block-parent-selector/index.mjs +2 -2
- package/build-module/components/block-parent-selector/index.mjs.map +2 -2
- package/build-module/components/block-toolbar/switch-section-style.mjs +4 -2
- package/build-module/components/block-toolbar/switch-section-style.mjs.map +2 -2
- package/build-module/components/block-tools/empty-block-inserter.mjs +2 -7
- package/build-module/components/block-tools/empty-block-inserter.mjs.map +2 -2
- package/build-module/components/block-vertical-alignment-control/ui.mjs +4 -6
- package/build-module/components/block-vertical-alignment-control/ui.mjs.map +2 -2
- package/build-module/components/border-radius-control/utils.mjs +11 -53
- package/build-module/components/border-radius-control/utils.mjs.map +2 -2
- package/build-module/components/colors-gradients/control.mjs +5 -3
- package/build-module/components/colors-gradients/control.mjs.map +2 -2
- package/build-module/components/dimension-control/index.mjs +30 -20
- package/build-module/components/dimension-control/index.mjs.map +2 -2
- package/build-module/components/dimensions-tool/aspect-ratio-tool.mjs +10 -6
- package/build-module/components/dimensions-tool/aspect-ratio-tool.mjs.map +2 -2
- package/build-module/components/editor-styles/index.mjs +9 -1
- package/build-module/components/editor-styles/index.mjs.map +2 -2
- package/build-module/components/global-styles/background-panel.mjs +71 -29
- package/build-module/components/global-styles/background-panel.mjs.map +2 -2
- package/build-module/components/global-styles/border-panel.mjs +157 -56
- package/build-module/components/global-styles/border-panel.mjs.map +2 -2
- package/build-module/components/global-styles/color-gradient-dropdown-item.mjs +51 -15
- package/build-module/components/global-styles/color-gradient-dropdown-item.mjs.map +2 -2
- package/build-module/components/global-styles/color-panel.mjs +63 -17
- package/build-module/components/global-styles/color-panel.mjs.map +2 -2
- package/build-module/components/global-styles/dimensions-panel.mjs +248 -66
- package/build-module/components/global-styles/dimensions-panel.mjs.map +2 -2
- package/build-module/components/global-styles/filters-panel.mjs +64 -21
- package/build-module/components/global-styles/filters-panel.mjs.map +2 -2
- package/build-module/components/global-styles/hooks.mjs +4 -1
- package/build-module/components/global-styles/hooks.mjs.map +2 -2
- package/build-module/components/global-styles/inheritance/index.mjs +108 -0
- package/build-module/components/global-styles/inheritance/index.mjs.map +7 -0
- package/build-module/components/global-styles/inherited-value-context.mjs +141 -0
- package/build-module/components/global-styles/inherited-value-context.mjs.map +7 -0
- package/build-module/components/global-styles/shadow-panel-components.mjs +38 -20
- package/build-module/components/global-styles/shadow-panel-components.mjs.map +2 -2
- package/build-module/components/global-styles/state-control.mjs +1 -1
- package/build-module/components/global-styles/state-control.mjs.map +2 -2
- package/build-module/components/global-styles/typography-panel.mjs +276 -79
- package/build-module/components/global-styles/typography-panel.mjs.map +2 -2
- package/build-module/components/inspector-controls-tabs/styles-tab.mjs +6 -1
- package/build-module/components/inspector-controls-tabs/styles-tab.mjs.map +2 -2
- package/build-module/components/line-height-control/index.mjs +1 -1
- package/build-module/components/line-height-control/index.mjs.map +2 -2
- package/build-module/components/link-control/index.mjs +4 -2
- package/build-module/components/link-control/index.mjs.map +2 -2
- package/build-module/components/preset-input-control/index.mjs +2 -1
- package/build-module/components/preset-input-control/index.mjs.map +2 -2
- package/build-module/components/provider/block-refs-provider.mjs +8 -2
- package/build-module/components/provider/block-refs-provider.mjs.map +2 -2
- package/build-module/components/provider/index.mjs +1 -1
- package/build-module/components/provider/index.mjs.map +2 -2
- package/build-module/components/provider/use-block-sync.mjs +18 -6
- package/build-module/components/provider/use-block-sync.mjs.map +2 -2
- package/build-module/components/provider/use-media-upload-settings.mjs +3 -1
- package/build-module/components/provider/use-media-upload-settings.mjs.map +2 -2
- package/build-module/components/rich-text/index.mjs +28 -14
- package/build-module/components/rich-text/index.mjs.map +2 -2
- package/build-module/components/spacing-sizes-control/utils.mjs +6 -18
- package/build-module/components/spacing-sizes-control/utils.mjs.map +2 -2
- package/build-module/components/text-indent-control/index.mjs +8 -2
- package/build-module/components/text-indent-control/index.mjs.map +2 -2
- package/build-module/components/url-input/index.mjs +306 -353
- package/build-module/components/url-input/index.mjs.map +2 -2
- package/build-module/components/writing-flow/index.mjs +2 -0
- package/build-module/components/writing-flow/index.mjs.map +2 -2
- package/build-module/components/writing-flow/use-arrow-nav.mjs +24 -1
- package/build-module/components/writing-flow/use-arrow-nav.mjs.map +2 -2
- package/build-module/components/writing-flow/use-click-selection.mjs +28 -2
- package/build-module/components/writing-flow/use-click-selection.mjs.map +2 -2
- package/build-module/components/writing-flow/use-editable-root-event-handlers.mjs +154 -0
- package/build-module/components/writing-flow/use-editable-root-event-handlers.mjs.map +7 -0
- package/build-module/components/writing-flow/use-editable-root.mjs +5 -37
- package/build-module/components/writing-flow/use-editable-root.mjs.map +2 -2
- package/build-module/components/writing-flow/use-home-end.mjs +1 -1
- package/build-module/components/writing-flow/use-home-end.mjs.map +2 -2
- package/build-module/components/writing-flow/use-selection-observer.mjs +18 -14
- package/build-module/components/writing-flow/use-selection-observer.mjs.map +2 -2
- package/build-module/components/writing-flow/utils.mjs +2 -11
- package/build-module/components/writing-flow/utils.mjs.map +2 -2
- package/build-module/hooks/background.mjs +13 -10
- package/build-module/hooks/background.mjs.map +2 -2
- package/build-module/hooks/block-style-variation.mjs +8 -44
- package/build-module/hooks/block-style-variation.mjs.map +2 -2
- package/build-module/hooks/border.mjs +19 -4
- package/build-module/hooks/border.mjs.map +2 -2
- package/build-module/hooks/custom-css.mjs +7 -3
- package/build-module/hooks/custom-css.mjs.map +2 -2
- package/build-module/hooks/dimensions.mjs +15 -4
- package/build-module/hooks/dimensions.mjs.map +2 -2
- package/build-module/hooks/duotone.mjs +20 -9
- package/build-module/hooks/duotone.mjs.map +2 -2
- package/build-module/hooks/elements.mjs +14 -4
- package/build-module/hooks/elements.mjs.map +2 -2
- package/build-module/hooks/line-height.mjs +1 -2
- package/build-module/hooks/line-height.mjs.map +2 -2
- package/build-module/hooks/typography.mjs +13 -4
- package/build-module/hooks/typography.mjs.map +2 -2
- package/build-module/private-apis.mjs +7 -3
- package/build-module/private-apis.mjs.map +2 -2
- package/build-module/store/actions.mjs +9 -4
- package/build-module/store/actions.mjs.map +2 -2
- package/build-module/store/private-selectors.mjs +29 -1
- package/build-module/store/private-selectors.mjs.map +2 -2
- package/build-module/store/reducer.mjs +6 -6
- package/build-module/store/reducer.mjs.map +2 -2
- package/build-module/store/selectors.mjs +23 -0
- package/build-module/store/selectors.mjs.map +2 -2
- package/build-module/utils/native-undo.mjs +29 -0
- package/build-module/utils/native-undo.mjs.map +7 -0
- package/build-style/content-rtl.css +10 -18
- package/build-style/content.css +10 -18
- package/build-style/style-rtl.css +150 -7
- package/build-style/style.css +150 -7
- package/package.json +40 -41
- package/src/components/alignment-control/test/__snapshots__/index.js.snap +1 -1
- package/src/components/alignment-control/ui.js +2 -4
- package/src/components/background-image-control/index.js +74 -37
- package/src/components/background-image-control/style.scss +13 -4
- package/src/components/block-alignment-control/test/__snapshots__/index.js.snap +3 -3
- package/src/components/block-alignment-control/test/index.js +1 -1
- package/src/components/block-alignment-control/ui.js +4 -2
- package/src/components/block-breadcrumb/test/__snapshots__/index.js.snap +1 -1
- package/src/components/block-compare/test/__snapshots__/block-view.js.snap +1 -1
- package/src/components/block-icon/test/__snapshots__/index.js.snap +1 -1
- package/src/components/block-inspector/index.js +60 -31
- package/src/components/block-list/block-html.js +5 -0
- package/src/components/block-list/content.scss +1 -11
- package/src/components/block-list/use-block-props/index.js +5 -1
- package/src/components/block-list/use-block-props/use-focus-handler.js +9 -8
- package/src/components/block-list/use-block-props/use-register-block-event-handlers.js +45 -0
- package/src/components/block-parent-selector/index.js +5 -2
- package/src/components/block-toolbar/switch-section-style.js +5 -2
- package/src/components/block-tools/empty-block-inserter.js +3 -7
- package/src/components/block-vertical-alignment-control/test/__snapshots__/index.js.snap +3 -3
- package/src/components/block-vertical-alignment-control/test/index.js +18 -2
- package/src/components/block-vertical-alignment-control/ui.js +3 -5
- package/src/components/border-radius-control/test/utils.js +0 -41
- package/src/components/border-radius-control/utils.js +16 -107
- package/src/components/button-block-appender/content.scss +6 -2
- package/src/components/color-palette/test/__snapshots__/control.js.snap +1 -1
- package/src/components/colors-gradients/control.js +6 -3
- package/src/components/default-block-appender/test/__snapshots__/index.js.snap +1 -1
- package/src/components/dimension-control/index.js +13 -1
- package/src/components/dimensions-tool/aspect-ratio-tool.js +13 -6
- package/src/components/editor-styles/index.js +16 -1
- package/src/components/global-styles/background-panel.js +107 -34
- package/src/components/global-styles/border-panel.js +219 -48
- package/src/components/global-styles/color-gradient-dropdown-item.js +93 -28
- package/src/components/global-styles/color-panel.js +92 -11
- package/src/components/global-styles/dimensions-panel.js +422 -71
- package/src/components/global-styles/filters-panel.js +88 -28
- package/src/components/global-styles/hooks.js +8 -1
- package/src/components/global-styles/inheritance/index.js +204 -0
- package/src/components/global-styles/inheritance/style.scss +184 -0
- package/src/components/global-styles/inheritance/test/index.js +281 -0
- package/src/components/global-styles/inherited-value-context.js +271 -0
- package/src/components/global-styles/shadow-panel-components.js +48 -26
- package/src/components/global-styles/state-control.js +2 -2
- package/src/components/global-styles/test/background-panel.js +338 -1
- package/src/components/global-styles/test/border-panel.js +441 -0
- package/src/components/global-styles/test/color-panel.js +182 -2
- package/src/components/global-styles/test/dimensions-panel.js +753 -0
- package/src/components/global-styles/test/filters-panel.js +194 -0
- package/src/components/global-styles/test/inherited-value-context-core.js +98 -0
- package/src/components/global-styles/test/inherited-value-context.js +454 -0
- package/src/components/global-styles/test/typography-panel-core.js +185 -0
- package/src/components/global-styles/test/typography-panel.js +849 -12
- package/src/components/global-styles/typography-panel.js +449 -100
- package/src/components/image-editor/style.scss +33 -0
- package/src/components/inner-blocks/test/__snapshots__/index.js.snap +1 -1
- package/src/components/inspector-controls-tabs/styles-tab.js +5 -1
- package/src/components/line-height-control/index.js +1 -1
- package/src/components/link-control/index.js +7 -2
- package/src/components/preset-input-control/index.js +4 -1
- package/src/components/provider/block-refs-provider.js +12 -2
- package/src/components/provider/index.js +2 -2
- package/src/components/provider/use-block-sync.js +18 -6
- package/src/components/provider/use-media-upload-settings.js +2 -0
- package/src/components/rich-text/index.js +37 -16
- package/src/components/spacing-sizes-control/test/utils.js +16 -5
- package/src/components/spacing-sizes-control/utils.js +11 -28
- package/src/components/text-indent-control/index.js +8 -0
- package/src/components/url-input/index.js +363 -431
- package/src/components/url-input/test/index.js +592 -0
- package/src/components/warning/test/__snapshots__/index.js.snap +1 -1
- package/src/components/writing-flow/index.js +2 -0
- package/src/components/writing-flow/use-arrow-nav.js +51 -1
- package/src/components/writing-flow/use-click-selection.js +54 -3
- package/src/components/writing-flow/use-editable-root-event-handlers.js +266 -0
- package/src/components/writing-flow/use-editable-root.js +7 -77
- package/src/components/writing-flow/use-home-end.js +17 -9
- package/src/components/writing-flow/use-selection-observer.js +32 -15
- package/src/components/writing-flow/utils.js +3 -24
- package/src/hooks/background.js +26 -21
- package/src/hooks/block-fields/rich-text/styles.scss +1 -1
- package/src/hooks/block-style-variation.js +8 -74
- package/src/hooks/border.js +19 -4
- package/src/hooks/custom-css.js +15 -6
- package/src/hooks/dimensions.js +17 -4
- package/src/hooks/duotone.js +26 -8
- package/src/hooks/elements.js +14 -3
- package/src/hooks/line-height.js +0 -1
- package/src/hooks/test/inherited-value-wiring.js +411 -0
- package/src/hooks/test/preset-round-trip.js +456 -0
- package/src/hooks/typography.js +35 -23
- package/src/private-apis.js +11 -2
- package/src/store/actions.js +10 -4
- package/src/store/private-selectors.js +57 -1
- package/src/store/reducer.js +9 -9
- package/src/store/selectors.js +56 -8
- package/src/store/test/private-selectors.js +160 -1
- package/src/style.scss +2 -0
- package/src/utils/native-undo.js +56 -0
- package/build/components/duotone/utils.cjs +0 -87
- package/build/components/duotone/utils.cjs.map +0 -7
- package/build-module/components/duotone/utils.mjs +0 -59
- package/build-module/components/duotone/utils.mjs.map +0 -7
- package/src/components/duotone/utils.js +0 -90
- package/src/hooks/test/get-variation-styles-with-ref-values.js +0 -91
|
@@ -6,25 +6,20 @@ import clsx from 'clsx';
|
|
|
6
6
|
/**
|
|
7
7
|
* WordPress dependencies
|
|
8
8
|
*/
|
|
9
|
+
import { speak } from '@wordpress/a11y';
|
|
9
10
|
import { __, sprintf, _n } from '@wordpress/i18n';
|
|
10
|
-
import {
|
|
11
|
+
import { useEffect, useRef, useState } from '@wordpress/element';
|
|
11
12
|
import { UP, DOWN, ENTER, TAB } from '@wordpress/keycodes';
|
|
12
13
|
import {
|
|
13
14
|
BaseControl,
|
|
14
15
|
Button,
|
|
15
16
|
__experimentalInputControl as InputControl,
|
|
16
17
|
Spinner,
|
|
17
|
-
withSpokenMessages,
|
|
18
18
|
Popover,
|
|
19
19
|
privateApis as componentsPrivateApis,
|
|
20
20
|
} from '@wordpress/components';
|
|
21
|
-
import {
|
|
22
|
-
|
|
23
|
-
debounce,
|
|
24
|
-
withInstanceId,
|
|
25
|
-
withSafeTimeout,
|
|
26
|
-
} from '@wordpress/compose';
|
|
27
|
-
import { withSelect } from '@wordpress/data';
|
|
21
|
+
import { useDebounce, useEvent, useInstanceId } from '@wordpress/compose';
|
|
22
|
+
import { useSelect } from '@wordpress/data';
|
|
28
23
|
import { isURL } from '@wordpress/url';
|
|
29
24
|
|
|
30
25
|
/**
|
|
@@ -35,6 +30,8 @@ import { unlock } from '../../lock-unlock';
|
|
|
35
30
|
|
|
36
31
|
const { ValidatedInputControl } = unlock( componentsPrivateApis );
|
|
37
32
|
|
|
33
|
+
const noop = () => {};
|
|
34
|
+
|
|
38
35
|
/**
|
|
39
36
|
* Whether the argument is a function.
|
|
40
37
|
*
|
|
@@ -45,99 +42,75 @@ function isFunction( maybeFunc ) {
|
|
|
45
42
|
return typeof maybeFunc === 'function';
|
|
46
43
|
}
|
|
47
44
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
)
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
componentWillUnmount() {
|
|
118
|
-
this.suggestionsRequest?.cancel?.();
|
|
119
|
-
this.suggestionsRequest = null;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
bindSuggestionNode( index ) {
|
|
123
|
-
return ( ref ) => {
|
|
124
|
-
this.suggestionNodes[ index ] = ref;
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
shouldShowInitialSuggestions() {
|
|
129
|
-
const { __experimentalShowInitialSuggestions = false, value } =
|
|
130
|
-
this.props;
|
|
131
|
-
return (
|
|
132
|
-
__experimentalShowInitialSuggestions && ! ( value && value.length )
|
|
133
|
-
);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
updateSuggestions( value = '' ) {
|
|
137
|
-
const {
|
|
138
|
-
__experimentalFetchLinkSuggestions: fetchLinkSuggestions,
|
|
139
|
-
__experimentalHandleURLSuggestions: handleURLSuggestions,
|
|
140
|
-
} = this.props;
|
|
45
|
+
/**
|
|
46
|
+
* Text field for entering a URL, with an autocomplete list of matching posts,
|
|
47
|
+
* pages and other link suggestions.
|
|
48
|
+
*
|
|
49
|
+
* @see https://github.com/WordPress/gutenberg/blob/HEAD/packages/block-editor/src/components/url-input/README.md
|
|
50
|
+
*
|
|
51
|
+
* @param {Object} props Component props.
|
|
52
|
+
*/
|
|
53
|
+
export default function URLInput( props ) {
|
|
54
|
+
const {
|
|
55
|
+
__experimentalFetchLinkSuggestions: fetchLinkSuggestionsProp,
|
|
56
|
+
__experimentalHandleURLSuggestions: handleURLSuggestions,
|
|
57
|
+
__experimentalRenderControl: renderControl,
|
|
58
|
+
__experimentalRenderSuggestions: renderSuggestions,
|
|
59
|
+
__experimentalShowInitialSuggestions: showInitialSuggestions = false,
|
|
60
|
+
autocompleteRef,
|
|
61
|
+
className,
|
|
62
|
+
customValidity,
|
|
63
|
+
disableSuggestions,
|
|
64
|
+
disabled = false,
|
|
65
|
+
help = null,
|
|
66
|
+
hideLabelFromVision = false,
|
|
67
|
+
inputRef,
|
|
68
|
+
isFullWidth,
|
|
69
|
+
label = null,
|
|
70
|
+
markWhenOptional,
|
|
71
|
+
onChange,
|
|
72
|
+
onKeyDown,
|
|
73
|
+
onSubmit,
|
|
74
|
+
placeholder = __( 'Paste URL or type to search' ),
|
|
75
|
+
required = true,
|
|
76
|
+
suffix,
|
|
77
|
+
value = '',
|
|
78
|
+
} = props;
|
|
79
|
+
|
|
80
|
+
const instanceId = useInstanceId( URLInput );
|
|
81
|
+
const { getSettings } = useSelect( blockEditorStore );
|
|
82
|
+
const debouncedSpeak = useDebounce( speak, 500 );
|
|
83
|
+
|
|
84
|
+
const [ suggestions, setSuggestions ] = useState( [] );
|
|
85
|
+
const [ suggestionsValue, setSuggestionsValue ] = useState( null );
|
|
86
|
+
const [ selectedSuggestion, setSelectedSuggestion ] = useState( null );
|
|
87
|
+
const [ isSuggestionsListOpen, setIsSuggestionsListOpen ] =
|
|
88
|
+
useState( false );
|
|
89
|
+
const [ isLoading, setIsLoading ] = useState( false );
|
|
90
|
+
|
|
91
|
+
const fallbackInputRef = useRef();
|
|
92
|
+
const suggestionNodesRef = useRef( [] );
|
|
93
|
+
// A fetch Promise can't be aborted. It's mimicked by holding on to the
|
|
94
|
+
// pending request so that responses of superseded requests can be ignored.
|
|
95
|
+
const suggestionsRequestRef = useRef( null );
|
|
96
|
+
|
|
97
|
+
const controlInputRef = inputRef ?? fallbackInputRef;
|
|
98
|
+
|
|
99
|
+
const inputId = `url-input-control-${ instanceId }`;
|
|
100
|
+
const suggestionsListboxId = `block-editor-url-input-suggestions-${ instanceId }`;
|
|
101
|
+
const suggestionOptionIdPrefix = `block-editor-url-input-suggestion-${ instanceId }`;
|
|
102
|
+
|
|
103
|
+
// The suggestions are hidden rather than discarded, so that returning focus
|
|
104
|
+
// to a field that already has results doesn't trigger a new search.
|
|
105
|
+
const showSuggestions =
|
|
106
|
+
isSuggestionsListOpen &&
|
|
107
|
+
disableSuggestions !== true &&
|
|
108
|
+
( showInitialSuggestions || !! value.length );
|
|
109
|
+
|
|
110
|
+
const updateSuggestions = useEvent( ( searchValue = '' ) => {
|
|
111
|
+
const fetchLinkSuggestions = isFunction( fetchLinkSuggestionsProp )
|
|
112
|
+
? fetchLinkSuggestionsProp
|
|
113
|
+
: getSettings().__experimentalFetchLinkSuggestions;
|
|
141
114
|
|
|
142
115
|
if ( ! fetchLinkSuggestions ) {
|
|
143
116
|
return;
|
|
@@ -145,11 +118,11 @@ class URLInput extends Component {
|
|
|
145
118
|
|
|
146
119
|
// Initial suggestions may only show if there is no value
|
|
147
120
|
// (note: this includes whitespace).
|
|
148
|
-
const isInitialSuggestions = !
|
|
121
|
+
const isInitialSuggestions = ! searchValue?.length;
|
|
149
122
|
|
|
150
123
|
// Trim only now we've determined whether or not it originally had a "length"
|
|
151
124
|
// (even if that value was all whitespace).
|
|
152
|
-
|
|
125
|
+
const search = searchValue.trim();
|
|
153
126
|
|
|
154
127
|
// Allow a suggestions request if:
|
|
155
128
|
// - there are at least 2 characters in the search input (except manual searches where
|
|
@@ -157,119 +130,152 @@ class URLInput extends Component {
|
|
|
157
130
|
// - this is a direct entry (eg: a URL)
|
|
158
131
|
if (
|
|
159
132
|
! isInitialSuggestions &&
|
|
160
|
-
(
|
|
133
|
+
( search.length < 2 ||
|
|
134
|
+
( ! handleURLSuggestions && isURL( search ) ) )
|
|
161
135
|
) {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
loading: false,
|
|
171
|
-
} );
|
|
136
|
+
suggestionsRequestRef.current?.cancel?.();
|
|
137
|
+
suggestionsRequestRef.current = null;
|
|
138
|
+
|
|
139
|
+
setSuggestions( [] );
|
|
140
|
+
setIsSuggestionsListOpen( false );
|
|
141
|
+
setSuggestionsValue( search );
|
|
142
|
+
setSelectedSuggestion( null );
|
|
143
|
+
setIsLoading( false );
|
|
172
144
|
|
|
173
145
|
return;
|
|
174
146
|
}
|
|
175
147
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
loading: true,
|
|
179
|
-
} );
|
|
148
|
+
setSelectedSuggestion( null );
|
|
149
|
+
setIsLoading( true );
|
|
180
150
|
|
|
181
|
-
const request = fetchLinkSuggestions(
|
|
151
|
+
const request = fetchLinkSuggestions( search, {
|
|
182
152
|
isInitialSuggestions,
|
|
183
153
|
} );
|
|
154
|
+
suggestionsRequestRef.current = request;
|
|
184
155
|
|
|
185
156
|
request
|
|
186
|
-
.then( (
|
|
187
|
-
|
|
188
|
-
// comparing the request reference in on the instance, which is
|
|
189
|
-
// reset or deleted on subsequent requests or unmounting.
|
|
190
|
-
if ( this.suggestionsRequest !== request ) {
|
|
157
|
+
.then( ( nextSuggestions ) => {
|
|
158
|
+
if ( suggestionsRequestRef.current !== request ) {
|
|
191
159
|
return;
|
|
192
160
|
}
|
|
193
161
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
showSuggestions: !! suggestions.length,
|
|
199
|
-
} );
|
|
162
|
+
setSuggestions( nextSuggestions );
|
|
163
|
+
setSuggestionsValue( search );
|
|
164
|
+
setIsLoading( false );
|
|
165
|
+
setIsSuggestionsListOpen( !! nextSuggestions.length );
|
|
200
166
|
|
|
201
|
-
if (
|
|
202
|
-
|
|
167
|
+
if ( nextSuggestions.length ) {
|
|
168
|
+
debouncedSpeak(
|
|
203
169
|
sprintf(
|
|
204
170
|
/* translators: %d: number of results. */
|
|
205
171
|
_n(
|
|
206
172
|
'%d result found, use up and down arrow keys to navigate.',
|
|
207
173
|
'%d results found, use up and down arrow keys to navigate.',
|
|
208
|
-
|
|
174
|
+
nextSuggestions.length
|
|
209
175
|
),
|
|
210
|
-
|
|
176
|
+
nextSuggestions.length
|
|
211
177
|
),
|
|
212
178
|
'assertive'
|
|
213
179
|
);
|
|
214
180
|
} else {
|
|
215
|
-
|
|
216
|
-
__( 'No results.' ),
|
|
217
|
-
'assertive'
|
|
218
|
-
);
|
|
181
|
+
debouncedSpeak( __( 'No results.' ), 'assertive' );
|
|
219
182
|
}
|
|
220
183
|
} )
|
|
221
184
|
.catch( () => {
|
|
222
|
-
if (
|
|
185
|
+
if ( suggestionsRequestRef.current !== request ) {
|
|
223
186
|
return;
|
|
224
187
|
}
|
|
225
188
|
|
|
226
|
-
|
|
227
|
-
loading: false,
|
|
228
|
-
} );
|
|
189
|
+
setIsLoading( false );
|
|
229
190
|
} )
|
|
230
191
|
.finally( () => {
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
if ( this.suggestionsRequest === request ) {
|
|
234
|
-
this.suggestionsRequest = null;
|
|
192
|
+
if ( suggestionsRequestRef.current === request ) {
|
|
193
|
+
suggestionsRequestRef.current = null;
|
|
235
194
|
}
|
|
236
195
|
} );
|
|
196
|
+
} );
|
|
197
|
+
|
|
198
|
+
const debouncedUpdateSuggestions = useDebounce( updateSuggestions, 200 );
|
|
199
|
+
|
|
200
|
+
// Keep the suggestions in sync with the value being searched for. An empty
|
|
201
|
+
// value requests the initial suggestions, when those are enabled.
|
|
202
|
+
useEffect( () => {
|
|
203
|
+
if (
|
|
204
|
+
! disableSuggestions &&
|
|
205
|
+
( value.length || showInitialSuggestions )
|
|
206
|
+
) {
|
|
207
|
+
debouncedUpdateSuggestions( value );
|
|
208
|
+
}
|
|
209
|
+
}, [
|
|
210
|
+
value,
|
|
211
|
+
disableSuggestions,
|
|
212
|
+
showInitialSuggestions,
|
|
213
|
+
debouncedUpdateSuggestions,
|
|
214
|
+
] );
|
|
215
|
+
|
|
216
|
+
// Persist the hidden state, so that the list can't reappear with stale
|
|
217
|
+
// results once the value or the props allow suggestions again.
|
|
218
|
+
useEffect( () => {
|
|
219
|
+
if ( ! showSuggestions ) {
|
|
220
|
+
setIsSuggestionsListOpen( false );
|
|
221
|
+
}
|
|
222
|
+
}, [ showSuggestions ] );
|
|
223
|
+
|
|
224
|
+
useEffect( () => {
|
|
225
|
+
if ( showSuggestions && selectedSuggestion !== null ) {
|
|
226
|
+
suggestionNodesRef.current[ selectedSuggestion ]?.scrollIntoView( {
|
|
227
|
+
behavior: 'instant',
|
|
228
|
+
block: 'nearest',
|
|
229
|
+
inline: 'nearest',
|
|
230
|
+
} );
|
|
231
|
+
}
|
|
232
|
+
}, [ showSuggestions, selectedSuggestion ] );
|
|
237
233
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
234
|
+
useEffect( () => {
|
|
235
|
+
return () => {
|
|
236
|
+
suggestionsRequestRef.current?.cancel?.();
|
|
237
|
+
suggestionsRequestRef.current = null;
|
|
238
|
+
};
|
|
239
|
+
}, [] );
|
|
240
|
+
|
|
241
|
+
function selectLink( suggestion ) {
|
|
242
|
+
onChange( suggestion.url, suggestion );
|
|
243
|
+
setSelectedSuggestion( null );
|
|
244
|
+
setIsSuggestionsListOpen( false );
|
|
241
245
|
}
|
|
242
246
|
|
|
243
|
-
|
|
244
|
-
|
|
247
|
+
function handleSuggestionClick( suggestion ) {
|
|
248
|
+
selectLink( suggestion );
|
|
249
|
+
// Move focus to the input field when a link suggestion is clicked.
|
|
250
|
+
controlInputRef.current.focus();
|
|
245
251
|
}
|
|
246
252
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
253
|
+
function handleChange( newValue ) {
|
|
254
|
+
// `InputControl` passes an `{ event }` object as its second argument,
|
|
255
|
+
// which callers would mistake for a selected suggestion.
|
|
256
|
+
onChange( newValue );
|
|
257
|
+
}
|
|
250
258
|
|
|
259
|
+
function handleFocus() {
|
|
251
260
|
// 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
|
|
252
261
|
// Don't re-run the suggestions on focus if there are already suggestions present (prevents searching again when tabbing between the input and buttons)
|
|
253
262
|
// or there is already a request in progress.
|
|
254
263
|
if (
|
|
255
264
|
value &&
|
|
256
265
|
! disableSuggestions &&
|
|
257
|
-
!
|
|
258
|
-
|
|
266
|
+
! suggestions.length &&
|
|
267
|
+
suggestionsRequestRef.current === null
|
|
259
268
|
) {
|
|
260
|
-
|
|
261
|
-
this.updateSuggestions( value );
|
|
269
|
+
debouncedUpdateSuggestions( value );
|
|
262
270
|
}
|
|
263
271
|
}
|
|
264
272
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
const { showSuggestions, selectedSuggestion, suggestions, loading } =
|
|
268
|
-
this.state;
|
|
273
|
+
function handleKeyDown( event ) {
|
|
274
|
+
onKeyDown?.( event );
|
|
269
275
|
|
|
270
276
|
// If the suggestions are not shown or loading, we shouldn't handle the arrow keys
|
|
271
277
|
// We shouldn't preventDefault to allow block arrow keys navigation.
|
|
272
|
-
if ( ! showSuggestions || ! suggestions.length ||
|
|
278
|
+
if ( ! showSuggestions || ! suggestions.length || isLoading ) {
|
|
273
279
|
// In the Windows version of Firefox the up and down arrows don't move the caret
|
|
274
280
|
// within an input field like they do for Mac Firefox/Chrome/Safari. This causes
|
|
275
281
|
// a form of focus trapping that is disruptive to the user experience. This disruption
|
|
@@ -290,15 +296,13 @@ class URLInput extends Component {
|
|
|
290
296
|
// When DOWN is pressed, if the caret is not at the end of the text, move it to the
|
|
291
297
|
// last position.
|
|
292
298
|
case DOWN: {
|
|
293
|
-
if (
|
|
294
|
-
this.props.value.length !== event.target.selectionStart
|
|
295
|
-
) {
|
|
299
|
+
if ( value.length !== event.target.selectionStart ) {
|
|
296
300
|
event.preventDefault();
|
|
297
301
|
|
|
298
302
|
// Set the input caret to the last position.
|
|
299
303
|
event.target.setSelectionRange(
|
|
300
|
-
|
|
301
|
-
|
|
304
|
+
value.length,
|
|
305
|
+
value.length
|
|
302
306
|
);
|
|
303
307
|
}
|
|
304
308
|
break;
|
|
@@ -306,9 +310,9 @@ class URLInput extends Component {
|
|
|
306
310
|
|
|
307
311
|
// Submitting while loading should trigger onSubmit.
|
|
308
312
|
case ENTER: {
|
|
309
|
-
if (
|
|
313
|
+
if ( onSubmit ) {
|
|
310
314
|
event.preventDefault();
|
|
311
|
-
|
|
315
|
+
onSubmit( null, event );
|
|
312
316
|
}
|
|
313
317
|
break;
|
|
314
318
|
}
|
|
@@ -317,50 +321,46 @@ class URLInput extends Component {
|
|
|
317
321
|
return;
|
|
318
322
|
}
|
|
319
323
|
|
|
320
|
-
const suggestion =
|
|
321
|
-
this.state.suggestions[ this.state.selectedSuggestion ];
|
|
324
|
+
const suggestion = suggestions[ selectedSuggestion ];
|
|
322
325
|
|
|
323
326
|
switch ( event.keyCode ) {
|
|
324
327
|
case UP: {
|
|
325
328
|
event.preventDefault();
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
} );
|
|
329
|
+
setSelectedSuggestion(
|
|
330
|
+
! selectedSuggestion
|
|
331
|
+
? suggestions.length - 1
|
|
332
|
+
: selectedSuggestion - 1
|
|
333
|
+
);
|
|
332
334
|
break;
|
|
333
335
|
}
|
|
334
336
|
case DOWN: {
|
|
335
337
|
event.preventDefault();
|
|
336
|
-
|
|
338
|
+
setSelectedSuggestion(
|
|
337
339
|
selectedSuggestion === null ||
|
|
338
|
-
|
|
340
|
+
selectedSuggestion === suggestions.length - 1
|
|
339
341
|
? 0
|
|
340
|
-
: selectedSuggestion + 1
|
|
341
|
-
|
|
342
|
-
selectedSuggestion: nextIndex,
|
|
343
|
-
} );
|
|
342
|
+
: selectedSuggestion + 1
|
|
343
|
+
);
|
|
344
344
|
break;
|
|
345
345
|
}
|
|
346
346
|
case TAB: {
|
|
347
|
-
if (
|
|
348
|
-
|
|
347
|
+
if ( selectedSuggestion !== null ) {
|
|
348
|
+
selectLink( suggestion );
|
|
349
349
|
// Announce a link has been selected when tabbing away from the input field.
|
|
350
|
-
|
|
350
|
+
speak( __( 'Link selected.' ) );
|
|
351
351
|
}
|
|
352
352
|
break;
|
|
353
353
|
}
|
|
354
354
|
case ENTER: {
|
|
355
355
|
event.preventDefault();
|
|
356
|
-
if (
|
|
357
|
-
|
|
356
|
+
if ( selectedSuggestion !== null ) {
|
|
357
|
+
selectLink( suggestion );
|
|
358
358
|
|
|
359
|
-
if (
|
|
360
|
-
|
|
359
|
+
if ( onSubmit ) {
|
|
360
|
+
onSubmit( suggestion, event );
|
|
361
361
|
}
|
|
362
|
-
} else if (
|
|
363
|
-
|
|
362
|
+
} else if ( onSubmit ) {
|
|
363
|
+
onSubmit( null, event );
|
|
364
364
|
}
|
|
365
365
|
|
|
366
366
|
break;
|
|
@@ -368,252 +368,184 @@ class URLInput extends Component {
|
|
|
368
368
|
}
|
|
369
369
|
}
|
|
370
370
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
} )
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
371
|
+
const controlProps = {
|
|
372
|
+
id: inputId, // Passes attribute to label for the for attribute
|
|
373
|
+
label,
|
|
374
|
+
className: clsx( 'block-editor-url-input', className, {
|
|
375
|
+
'is-full-width': isFullWidth,
|
|
376
|
+
} ),
|
|
377
|
+
hideLabelFromVision,
|
|
378
|
+
};
|
|
379
|
+
|
|
380
|
+
const inputProps = {
|
|
381
|
+
id: inputId,
|
|
382
|
+
value,
|
|
383
|
+
required,
|
|
384
|
+
type: 'text',
|
|
385
|
+
name: inputId,
|
|
386
|
+
autoComplete: 'off',
|
|
387
|
+
onChange: disabled ? noop : handleChange,
|
|
388
|
+
onFocus: disabled ? noop : handleFocus,
|
|
389
|
+
onKeyDown: disabled ? noop : handleKeyDown,
|
|
390
|
+
placeholder,
|
|
391
|
+
role: 'combobox',
|
|
392
|
+
'aria-label': label ? undefined : __( 'URL' ), // Ensure input always has an accessible label
|
|
393
|
+
'aria-expanded': showSuggestions,
|
|
394
|
+
'aria-autocomplete': 'list',
|
|
395
|
+
'aria-owns': suggestionsListboxId,
|
|
396
|
+
'aria-activedescendant':
|
|
397
|
+
selectedSuggestion !== null
|
|
398
|
+
? `${ suggestionOptionIdPrefix }-${ selectedSuggestion }`
|
|
399
|
+
: undefined,
|
|
400
|
+
ref: controlInputRef,
|
|
401
|
+
disabled,
|
|
402
|
+
suffix,
|
|
403
|
+
help,
|
|
404
|
+
};
|
|
405
|
+
|
|
406
|
+
return (
|
|
407
|
+
<>
|
|
408
|
+
<Control
|
|
409
|
+
controlProps={ controlProps }
|
|
410
|
+
inputProps={ inputProps }
|
|
411
|
+
isLoading={ isLoading }
|
|
412
|
+
customValidity={ customValidity }
|
|
413
|
+
markWhenOptional={ markWhenOptional }
|
|
414
|
+
renderControl={ renderControl }
|
|
415
|
+
/>
|
|
416
|
+
{ showSuggestions && suggestions.length > 0 && (
|
|
417
|
+
<Suggestions
|
|
418
|
+
autocompleteRef={ autocompleteRef }
|
|
419
|
+
className={ className }
|
|
420
|
+
handleSuggestionClick={ handleSuggestionClick }
|
|
421
|
+
isLoading={ isLoading }
|
|
422
|
+
renderSuggestions={ renderSuggestions }
|
|
423
|
+
selectedSuggestion={ selectedSuggestion }
|
|
424
|
+
suggestionNodesRef={ suggestionNodesRef }
|
|
425
|
+
suggestionOptionIdPrefix={ suggestionOptionIdPrefix }
|
|
426
|
+
suggestions={ suggestions }
|
|
427
|
+
suggestionsListboxId={ suggestionsListboxId }
|
|
428
|
+
suggestionsValue={ suggestionsValue }
|
|
429
|
+
/>
|
|
430
|
+
) }
|
|
431
|
+
</>
|
|
432
|
+
);
|
|
433
|
+
}
|
|
405
434
|
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
435
|
+
function Control( {
|
|
436
|
+
controlProps,
|
|
437
|
+
inputProps,
|
|
438
|
+
isLoading,
|
|
439
|
+
customValidity,
|
|
440
|
+
markWhenOptional,
|
|
441
|
+
renderControl,
|
|
442
|
+
} ) {
|
|
443
|
+
// Once a validity has been reported, keep using the validated control, so
|
|
444
|
+
// that clearing the validity doesn't remount (and blur) the input.
|
|
445
|
+
const [ isValidated, setIsValidated ] = useState(
|
|
446
|
+
customValidity !== undefined
|
|
447
|
+
);
|
|
448
|
+
|
|
449
|
+
if ( customValidity !== undefined && ! isValidated ) {
|
|
450
|
+
setIsValidated( true );
|
|
411
451
|
}
|
|
412
452
|
|
|
413
|
-
|
|
414
|
-
return (
|
|
415
|
-
<>
|
|
416
|
-
{ this.renderControl() }
|
|
417
|
-
{ this.renderSuggestions() }
|
|
418
|
-
</>
|
|
419
|
-
);
|
|
453
|
+
if ( renderControl ) {
|
|
454
|
+
return renderControl( controlProps, inputProps, isLoading );
|
|
420
455
|
}
|
|
421
456
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
} = this.state;
|
|
445
|
-
|
|
446
|
-
const inputId = `url-input-control-${ instanceId }`;
|
|
447
|
-
|
|
448
|
-
const controlProps = {
|
|
449
|
-
id: inputId, // Passes attribute to label for the for attribute
|
|
450
|
-
label,
|
|
451
|
-
className: clsx( 'block-editor-url-input', className, {
|
|
452
|
-
'is-full-width': isFullWidth,
|
|
453
|
-
} ),
|
|
454
|
-
hideLabelFromVision,
|
|
455
|
-
};
|
|
456
|
-
|
|
457
|
-
const inputProps = {
|
|
458
|
-
id: inputId,
|
|
459
|
-
value,
|
|
460
|
-
required: this.props.required ?? true,
|
|
461
|
-
type: 'text',
|
|
462
|
-
name: inputId,
|
|
463
|
-
autoComplete: 'off',
|
|
464
|
-
onChange: disabled ? () => {} : this.onChange, // Disable onChange when disabled
|
|
465
|
-
onFocus: disabled ? () => {} : this.onFocus, // Disable onFocus when disabled
|
|
466
|
-
placeholder,
|
|
467
|
-
onKeyDown: disabled ? () => {} : this.onKeyDown, // Disable onKeyDown when disabled
|
|
468
|
-
role: 'combobox',
|
|
469
|
-
'aria-label': label ? undefined : __( 'URL' ), // Ensure input always has an accessible label
|
|
470
|
-
'aria-expanded': showSuggestions,
|
|
471
|
-
'aria-autocomplete': 'list',
|
|
472
|
-
'aria-owns': suggestionsListboxId,
|
|
473
|
-
'aria-activedescendant':
|
|
474
|
-
selectedSuggestion !== null
|
|
475
|
-
? `${ suggestionOptionIdPrefix }-${ selectedSuggestion }`
|
|
476
|
-
: undefined,
|
|
477
|
-
ref: this.inputRef,
|
|
478
|
-
disabled,
|
|
479
|
-
suffix: this.props.suffix,
|
|
480
|
-
help,
|
|
481
|
-
};
|
|
457
|
+
const MaybeValidatedInputControl = isValidated
|
|
458
|
+
? ValidatedInputControl
|
|
459
|
+
: InputControl;
|
|
460
|
+
|
|
461
|
+
return (
|
|
462
|
+
<BaseControl { ...controlProps }>
|
|
463
|
+
<MaybeValidatedInputControl
|
|
464
|
+
{ ...inputProps }
|
|
465
|
+
{ ...( isValidated && {
|
|
466
|
+
customValidity,
|
|
467
|
+
// Suppress the "(Required)" indicator in the label.
|
|
468
|
+
// The field is still required for validation, but the indicator
|
|
469
|
+
// can be hidden when markWhenOptional is set to true.
|
|
470
|
+
...( markWhenOptional !== undefined && {
|
|
471
|
+
markWhenOptional,
|
|
472
|
+
} ),
|
|
473
|
+
} ) }
|
|
474
|
+
/>
|
|
475
|
+
{ isLoading && <Spinner /> }
|
|
476
|
+
</BaseControl>
|
|
477
|
+
);
|
|
478
|
+
}
|
|
482
479
|
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
480
|
+
function Suggestions( {
|
|
481
|
+
autocompleteRef,
|
|
482
|
+
className,
|
|
483
|
+
handleSuggestionClick,
|
|
484
|
+
isLoading,
|
|
485
|
+
renderSuggestions,
|
|
486
|
+
selectedSuggestion,
|
|
487
|
+
suggestionNodesRef,
|
|
488
|
+
suggestionOptionIdPrefix,
|
|
489
|
+
suggestions,
|
|
490
|
+
suggestionsListboxId,
|
|
491
|
+
suggestionsValue,
|
|
492
|
+
} ) {
|
|
493
|
+
const suggestionsListProps = {
|
|
494
|
+
id: suggestionsListboxId,
|
|
495
|
+
ref: autocompleteRef,
|
|
496
|
+
role: 'listbox',
|
|
497
|
+
};
|
|
498
|
+
|
|
499
|
+
const buildSuggestionItemProps = ( suggestion, index ) => {
|
|
500
|
+
return {
|
|
501
|
+
role: 'option',
|
|
502
|
+
tabIndex: '-1',
|
|
503
|
+
id: `${ suggestionOptionIdPrefix }-${ index }`,
|
|
504
|
+
ref: ( node ) => {
|
|
505
|
+
suggestionNodesRef.current[ index ] = node;
|
|
506
|
+
},
|
|
507
|
+
'aria-selected': index === selectedSuggestion ? true : undefined,
|
|
491
508
|
};
|
|
509
|
+
};
|
|
492
510
|
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
// Use ValidatedInputControl if customValidity has ever had a non-undefined value.
|
|
498
|
-
if ( customValidity !== undefined ) {
|
|
499
|
-
this.hasRenderedValidation.current = true;
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
const MaybeValidatedInputControl = this.hasRenderedValidation.current
|
|
503
|
-
? ValidatedInputControl
|
|
504
|
-
: InputControl;
|
|
505
|
-
|
|
506
|
-
return (
|
|
507
|
-
<BaseControl { ...controlProps }>
|
|
508
|
-
<MaybeValidatedInputControl
|
|
509
|
-
{ ...inputProps }
|
|
510
|
-
{ ...( this.hasRenderedValidation.current
|
|
511
|
-
? validationProps
|
|
512
|
-
: {} ) }
|
|
513
|
-
__next40pxDefaultSize
|
|
514
|
-
/>
|
|
515
|
-
{ loading && <Spinner /> }
|
|
516
|
-
</BaseControl>
|
|
517
|
-
);
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
renderSuggestions() {
|
|
521
|
-
const {
|
|
522
|
-
className,
|
|
523
|
-
__experimentalRenderSuggestions: renderSuggestions,
|
|
524
|
-
} = this.props;
|
|
525
|
-
|
|
526
|
-
const {
|
|
527
|
-
showSuggestions,
|
|
511
|
+
if ( isFunction( renderSuggestions ) ) {
|
|
512
|
+
return renderSuggestions( {
|
|
528
513
|
suggestions,
|
|
529
|
-
suggestionsValue,
|
|
530
514
|
selectedSuggestion,
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
const suggestionsListProps = {
|
|
541
|
-
id: suggestionsListboxId,
|
|
542
|
-
ref: this.autocompleteRef,
|
|
543
|
-
role: 'listbox',
|
|
544
|
-
};
|
|
545
|
-
|
|
546
|
-
const buildSuggestionItemProps = ( suggestion, index ) => {
|
|
547
|
-
return {
|
|
548
|
-
role: 'option',
|
|
549
|
-
tabIndex: '-1',
|
|
550
|
-
id: `${ suggestionOptionIdPrefix }-${ index }`,
|
|
551
|
-
ref: this.bindSuggestionNode( index ),
|
|
552
|
-
'aria-selected':
|
|
553
|
-
index === selectedSuggestion ? true : undefined,
|
|
554
|
-
};
|
|
555
|
-
};
|
|
556
|
-
|
|
557
|
-
if ( isFunction( renderSuggestions ) ) {
|
|
558
|
-
return renderSuggestions( {
|
|
559
|
-
suggestions,
|
|
560
|
-
selectedSuggestion,
|
|
561
|
-
suggestionsListProps,
|
|
562
|
-
buildSuggestionItemProps,
|
|
563
|
-
isLoading: loading,
|
|
564
|
-
handleSuggestionClick: this.handleOnClick,
|
|
565
|
-
isInitialSuggestions: ! suggestionsValue?.length,
|
|
566
|
-
currentInputValue: suggestionsValue,
|
|
567
|
-
} );
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
return (
|
|
571
|
-
<Popover placement="bottom" focusOnMount={ false }>
|
|
572
|
-
<div
|
|
573
|
-
{ ...suggestionsListProps }
|
|
574
|
-
className={ clsx( 'block-editor-url-input__suggestions', {
|
|
575
|
-
[ `${ className }__suggestions` ]: className,
|
|
576
|
-
} ) }
|
|
577
|
-
>
|
|
578
|
-
{ suggestions.map( ( suggestion, index ) => (
|
|
579
|
-
<Button
|
|
580
|
-
__next40pxDefaultSize
|
|
581
|
-
{ ...buildSuggestionItemProps( suggestion, index ) }
|
|
582
|
-
key={ suggestion.id }
|
|
583
|
-
className={ clsx(
|
|
584
|
-
'block-editor-url-input__suggestion',
|
|
585
|
-
{
|
|
586
|
-
'is-selected': index === selectedSuggestion,
|
|
587
|
-
}
|
|
588
|
-
) }
|
|
589
|
-
onClick={ () => this.handleOnClick( suggestion ) }
|
|
590
|
-
>
|
|
591
|
-
{ suggestion.title }
|
|
592
|
-
</Button>
|
|
593
|
-
) ) }
|
|
594
|
-
</div>
|
|
595
|
-
</Popover>
|
|
596
|
-
);
|
|
515
|
+
suggestionsListProps,
|
|
516
|
+
buildSuggestionItemProps,
|
|
517
|
+
isLoading,
|
|
518
|
+
handleSuggestionClick,
|
|
519
|
+
isInitialSuggestions: ! suggestionsValue?.length,
|
|
520
|
+
currentInputValue: suggestionsValue,
|
|
521
|
+
} );
|
|
597
522
|
}
|
|
598
|
-
}
|
|
599
523
|
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
)(
|
|
524
|
+
return (
|
|
525
|
+
<Popover placement="bottom" focusOnMount={ false }>
|
|
526
|
+
<div
|
|
527
|
+
{ ...suggestionsListProps }
|
|
528
|
+
className={ clsx( 'block-editor-url-input__suggestions', {
|
|
529
|
+
[ `${ className }__suggestions` ]: className,
|
|
530
|
+
} ) }
|
|
531
|
+
>
|
|
532
|
+
{ suggestions.map( ( suggestion, index ) => (
|
|
533
|
+
<Button
|
|
534
|
+
__next40pxDefaultSize
|
|
535
|
+
{ ...buildSuggestionItemProps( suggestion, index ) }
|
|
536
|
+
key={ suggestion.id }
|
|
537
|
+
className={ clsx(
|
|
538
|
+
'block-editor-url-input__suggestion',
|
|
539
|
+
{
|
|
540
|
+
'is-selected': index === selectedSuggestion,
|
|
541
|
+
}
|
|
542
|
+
) }
|
|
543
|
+
onClick={ () => handleSuggestionClick( suggestion ) }
|
|
544
|
+
>
|
|
545
|
+
{ suggestion.title }
|
|
546
|
+
</Button>
|
|
547
|
+
) ) }
|
|
548
|
+
</div>
|
|
549
|
+
</Popover>
|
|
550
|
+
);
|
|
551
|
+
}
|