diff --git a/build/toggle-block/index.asset.php b/build/toggle-block/index.asset.php index 1925798..5f7be9b 100644 --- a/build/toggle-block/index.asset.php +++ b/build/toggle-block/index.asset.php @@ -1 +1 @@ - array('wp-block-editor', 'wp-blocks', 'wp-components', 'wp-hooks', 'wp-i18n'), 'version' => 'a5c3c55e8feccd19dc5a'); + array('wp-block-editor', 'wp-blocks', 'wp-components', 'wp-data', 'wp-element', 'wp-hooks', 'wp-i18n'), 'version' => '622cdfd76a7fc92b80d4'); diff --git a/build/toggle-block/index.css b/build/toggle-block/index.css index e353493..009660d 100644 --- a/build/toggle-block/index.css +++ b/build/toggle-block/index.css @@ -1,3 +1,146 @@ .editor-styles-wrapper .wp-block-happyprime-toggle-block { display: inline-block; } + +.toggle-block-selector__current { + background: #f0f0f0; + border-radius: 4px; + padding: 12px; + margin-bottom: 12px; +} + +.toggle-block-selector__current--orphan { + background: transparent; + padding: 0; +} + +.toggle-block-selector__current-header { + display: flex; + align-items: center; + gap: 6px; + margin-bottom: 6px; +} + +.toggle-block-selector__current-title { + font-weight: 600; +} + +.toggle-block-selector__current-anchor { + display: inline-block; + font-size: 12px; + background: #fff; + padding: 2px 6px; + border-radius: 3px; + word-break: break-all; +} + +.toggle-block-selector__current-preview { + margin: 8px 0 0; + font-size: 12px; + color: #555; + line-height: 1.4; +} + +.toggle-block-selector__empty { + margin: 0 0 12px; + font-size: 12px; + color: #555; +} + +.toggle-block-selector__actions { + display: flex; + gap: 8px; + margin-bottom: 8px; +} + +.toggle-block-selector__manual-toggle { + padding: 0; + font-size: 12px; +} + +.toggle-block-selector__modal { + max-width: 560px; +} + +.toggle-block-selector__list { + list-style: none; + margin: 12px 0 0; + padding: 0; + max-height: 60vh; + overflow-y: auto; +} + +.toggle-block-selector__list-item { + margin: 0; + padding: 0; +} + +.toggle-block-selector__list-item + .toggle-block-selector__list-item { + border-top: 1px solid #f0f0f0; +} + +.toggle-block-selector__list-item.is-selected .toggle-block-selector__list-button { + background: #f0f6fc; +} + +.toggle-block-selector__list-button { + display: flex; + align-items: flex-start; + gap: 12px; + width: 100%; + padding: 10px 12px; + background: transparent; + border: 0; + border-radius: 4px; + cursor: pointer; + text-align: left; +} + +.toggle-block-selector__list-button:hover, +.toggle-block-selector__list-button:focus { + background: #f0f0f0; +} + +.toggle-block-selector__list-icon { + flex: 0 0 auto; + display: flex; + align-items: center; + justify-content: center; + width: 24px; + height: 24px; + margin-top: 2px; +} + +.toggle-block-selector__list-body { + display: flex; + flex-direction: column; + gap: 2px; + flex: 1 1 auto; + min-width: 0; +} + +.toggle-block-selector__list-title { + font-weight: 600; + font-size: 13px; +} + +.toggle-block-selector__list-preview { + font-size: 12px; + color: #555; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.toggle-block-selector__list-anchor { + font-size: 11px; + color: #777; + font-family: Menlo, Consolas, monospace; + word-break: break-all; +} + +.toggle-block-selector__no-results { + margin: 16px 0; + color: #555; + font-size: 13px; +} diff --git a/build/toggle-block/index.js b/build/toggle-block/index.js index 390db60..19d9cc4 100644 --- a/build/toggle-block/index.js +++ b/build/toggle-block/index.js @@ -1 +1 @@ -(()=>{"use strict";function e(){return e=Object.assign?Object.assign.bind():function(e){for(var t=1;t{const{attributes:{bodyClass:o,buttonText:a,controlsId:n,defaultToggle:r,labelText:s}}=l;return React.createElement("button",e({},t.useBlockProps.save(),{"aria-label":s,"aria-controls":n},o&&{"data-body-class":o},r&&{"data-default-toggle":"true"}),React.createElement("span",null,a))}},{attributes:{bodyClass:{type:"string",default:""},buttonText:{type:"string",default:""},controlsId:{type:"string",default:""},labelText:{type:"string",default:""}},save:l=>{const{attributes:{bodyClass:o,buttonText:a,controlsId:n,labelText:r}}=l;return React.createElement("button",e({},t.useBlockProps.save(),{"aria-label":r,"aria-controls":n},o&&{"data-body-class":o}),React.createElement("span",null,a))}},{attributes:{bodyClass:{type:"string"},buttonText:{type:"string"},controlsId:{type:"string"},labelText:{type:"string"}},save:l=>{const{attributes:{bodyClass:o,buttonText:a,controlsId:n,labelText:r}}=l;return React.createElement("button",e({},t.useBlockProps.save(),{"aria-label":r,"aria-controls":n},o&&{"data-body-class":o}),a)}}];(0,window.wp.hooks.addFilter)("blocks.registerBlockType","happyprime/add-toggle-to-navigation",function(e,t){if("core/navigation"!==t)return e;const l=e.allowedBlocks||[];return{...e,allowedBlocks:[...l,"happyprime/toggle-block"]}});const s={from:[{type:"block",blocks:["core/navigation-link"],transform:(e,t)=>(0,l.createBlock)("happyprime/toggle-block",{buttonText:e.label||e.title||""},t)}],to:[{type:"block",blocks:["core/navigation-link"],transform:(e,t)=>(0,l.createBlock)("core/navigation-link",{label:e.buttonText||"",url:"#"},t)}]};(0,l.registerBlockType)(n,{edit:l=>{const{attributes:{bodyClass:n,buttonText:r,controlsId:s,defaultToggle:i,labelText:c},setAttributes:g}=l;return React.createElement(React.Fragment,null,React.createElement(t.InspectorControls,null,React.createElement(o.PanelBody,{title:(0,a.__)("Toggle settings","toggle-block")},React.createElement(o.TextControl,{label:(0,a.__)("Controls ID","toggle-block"),description:(0,a.__)("Enter the HTML anchor ID of the element this toggle controls.","toggle-block"),value:s,onChange:e=>g({controlsId:e})}),React.createElement(o.TextControl,{label:(0,a.__)("Screen reader text","toggle-block"),description:(0,a.__)("Enter a description of what this toggle controls.","toggle-block"),value:c,onChange:e=>g({labelText:e})}),React.createElement(o.TextControl,{label:(0,a.__)("Body class","toggle-block"),description:(0,a.__)("Enter a class to add to the body when the toggle is active.","toggle-block"),value:n,onChange:e=>g({bodyClass:e})}),React.createElement(o.ToggleControl,{label:(0,a.__)("Default toggle","toggle-block"),help:(0,a.__)("When inside a toggle group, this toggle will be active by default.","toggle-block"),checked:i,onChange:e=>g({defaultToggle:e})}))),React.createElement(t.RichText,e({},(0,t.useBlockProps)(),{tagName:"span",label:(0,a.__)("Button text","toggle-block"),placeholder:(0,a.__)("Button text","toggle-block"),value:r,onChange:e=>{g({buttonText:e})}})))},save:l=>{const{attributes:{bodyClass:o,buttonText:a,controlsId:n,defaultToggle:r,labelText:s}}=l;return React.createElement("button",e({},t.useBlockProps.save(),s&&{"aria-label":s},{"aria-controls":n},o&&{"data-body-class":o},r&&{"data-default-toggle":"true"}),React.createElement("span",null,a))},transforms:s,deprecated:r})})(); \ No newline at end of file +(()=>{"use strict";function e(){return e=Object.assign?Object.assign.bind():function(e){for(var t=1;t{const{attributes:{bodyClass:o,buttonText:a,controlsId:c,defaultToggle:n,labelText:r}}=l;return React.createElement("button",e({},t.useBlockProps.save(),{"aria-label":r,"aria-controls":c},o&&{"data-body-class":o},n&&{"data-default-toggle":"true"}),React.createElement("span",null,a))}},{attributes:{bodyClass:{type:"string",default:""},buttonText:{type:"string",default:""},controlsId:{type:"string",default:""},labelText:{type:"string",default:""}},save:l=>{const{attributes:{bodyClass:o,buttonText:a,controlsId:c,labelText:n}}=l;return React.createElement("button",e({},t.useBlockProps.save(),{"aria-label":n,"aria-controls":c},o&&{"data-body-class":o}),React.createElement("span",null,a))}},{attributes:{bodyClass:{type:"string"},buttonText:{type:"string"},controlsId:{type:"string"},labelText:{type:"string"}},save:l=>{const{attributes:{bodyClass:o,buttonText:a,controlsId:c,labelText:n}}=l;return React.createElement("button",e({},t.useBlockProps.save(),{"aria-label":n,"aria-controls":c},o&&{"data-body-class":o}),a)}}],r=window.wp.data,s=window.wp.element,i=e=>{const t=[];for(const l of e)t.push(l),l.innerBlocks?.length&&t.push(...i(l.innerBlocks));return t},g=e=>{const t=[e.attributes?.content,e.attributes?.text,e.attributes?.title,e.attributes?.label,e.attributes?.value,e.attributes?.citation,e.attributes?.caption];for(const e of t)if("string"==typeof e&&e.trim()){const t=e.replace(/<[^>]+>/g,"").trim();if(t)return t.length>80?t.slice(0,80)+"…":t}return""},u=({clientId:e,controlsId:c,setAttributes:n})=>{const[u,b]=(0,s.useState)(!1),[d,m]=(0,s.useState)(""),[p,k]=(0,s.useState)(!1),{updateBlockAttributes:_}=(0,r.useDispatch)("core/block-editor"),h=(0,r.useSelect)(e=>i(e("core/block-editor").getBlocks()),[]),y=(0,s.useMemo)(()=>{const e=new Set;for(const t of h)t.attributes?.anchor&&e.add(t.attributes.anchor);return e},[h]),f=(0,s.useMemo)(()=>c&&h.find(e=>e.attributes?.anchor===c)||null,[h,c]),w=(0,s.useMemo)(()=>h.filter(t=>t.clientId!==e&&"happyprime/toggle-block"!==t.name),[h,e]),E=(0,s.useMemo)(()=>{if(!d.trim())return w;const e=d.toLowerCase();return w.filter(t=>{const o=(0,l.getBlockType)(t.name),a=(o?.title||t.name).toLowerCase(),c=(t.attributes?.anchor||"").toLowerCase(),n=g(t).toLowerCase();return a.includes(e)||c.includes(e)||n.includes(e)})},[w,d]),T=f?(0,l.getBlockType)(f.name):null;return React.createElement("div",{className:"toggle-block-selector"},f?React.createElement("div",{className:"toggle-block-selector__current"},React.createElement("div",{className:"toggle-block-selector__current-header"},T?.icon&&React.createElement(t.BlockIcon,{icon:T.icon}),React.createElement("span",{className:"toggle-block-selector__current-title"},T?.title||f.name)),React.createElement("code",{className:"toggle-block-selector__current-anchor"},"#",c),g(f)&&React.createElement("p",{className:"toggle-block-selector__current-preview"},g(f))):c?React.createElement("div",{className:"toggle-block-selector__current toggle-block-selector__current--orphan"},React.createElement(o.Notice,{status:"warning",isDismissible:!1},(0,a.sprintf)((0,a.__)('No block with anchor "%s" was found in this post. The toggle will still target this ID at runtime.',"toggle-block"),c))):React.createElement("p",{className:"toggle-block-selector__empty"},(0,a.__)("No block selected. Browse available blocks to choose which one this toggle controls.","toggle-block")),React.createElement("div",{className:"toggle-block-selector__actions"},React.createElement(o.Button,{variant:"secondary",onClick:()=>b(!0)},f||c?(0,a.__)("Change block","toggle-block"):(0,a.__)("Browse blocks","toggle-block")),c&&React.createElement(o.Button,{variant:"tertiary",isDestructive:!0,onClick:()=>{n({controlsId:""})}},(0,a.__)("Clear","toggle-block"))),React.createElement(o.Button,{variant:"link",className:"toggle-block-selector__manual-toggle",onClick:()=>k(e=>!e)},p?(0,a.__)("Hide manual ID entry","toggle-block"):(0,a.__)("Enter ID manually","toggle-block")),p&&React.createElement(o.TextControl,{label:(0,a.__)("Controls ID","toggle-block"),help:(0,a.__)("Enter the HTML anchor ID of the element this toggle controls. Useful when the target lives outside this post.","toggle-block"),value:c,onChange:e=>n({controlsId:e})}),u&&React.createElement(o.Modal,{title:(0,a.__)("Select a block to toggle","toggle-block"),onRequestClose:()=>{b(!1),m("")},className:"toggle-block-selector__modal"},React.createElement(o.SearchControl,{value:d,onChange:m,label:(0,a.__)("Search blocks","toggle-block"),placeholder:(0,a.__)("Search by block type, content, or anchor…","toggle-block")}),0===E.length?React.createElement("p",{className:"toggle-block-selector__no-results"},0===w.length?(0,a.__)("No other blocks in this post yet. Add the content you want to toggle first.","toggle-block"):(0,a.__)("No blocks match your search.","toggle-block")):React.createElement("ul",{className:"toggle-block-selector__list"},E.map(e=>{const o=(0,l.getBlockType)(e.name),r=g(e),s=e.attributes?.anchor,i=s&&s===c;return React.createElement("li",{key:e.clientId,className:"toggle-block-selector__list-item"+(i?" is-selected":"")},React.createElement("button",{type:"button",className:"toggle-block-selector__list-button",onClick:()=>(e=>{let t=e.attributes?.anchor;t||(t=((e,t)=>{const l=(e.name||"block").replace(/.*\//,"");let o;do{o=`${l}-${Math.random().toString(36).slice(2,8)}`}while(t.has(o));return o})(e,y),_(e.clientId,{anchor:t})),n({controlsId:t}),b(!1),m("")})(e)},React.createElement("span",{className:"toggle-block-selector__list-icon"},o?.icon&&React.createElement(t.BlockIcon,{icon:o.icon})),React.createElement("span",{className:"toggle-block-selector__list-body"},React.createElement("span",{className:"toggle-block-selector__list-title"},o?.title||e.name),r&&React.createElement("span",{className:"toggle-block-selector__list-preview"},r),React.createElement("span",{className:"toggle-block-selector__list-anchor"},s?`#${s}`:(0,a.__)("No anchor set — one will be generated.","toggle-block")))))}))))};(0,window.wp.hooks.addFilter)("blocks.registerBlockType","happyprime/add-toggle-to-navigation",function(e,t){if("core/navigation"!==t)return e;const l=e.allowedBlocks||[];return{...e,allowedBlocks:[...l,"happyprime/toggle-block"]}});const b={from:[{type:"block",blocks:["core/navigation-link"],transform:(e,t)=>(0,l.createBlock)("happyprime/toggle-block",{buttonText:e.label||e.title||""},t)}],to:[{type:"block",blocks:["core/navigation-link"],transform:(e,t)=>(0,l.createBlock)("core/navigation-link",{label:e.buttonText||"",url:"#"},t)}]};(0,l.registerBlockType)(c,{edit:l=>{const{attributes:{bodyClass:c,buttonText:n,controlsId:r,defaultToggle:s,labelText:i},clientId:g,setAttributes:b}=l;return React.createElement(React.Fragment,null,React.createElement(t.InspectorControls,null,React.createElement(o.PanelBody,{title:(0,a.__)("Toggle settings","toggle-block")},React.createElement(u,{clientId:g,controlsId:r,setAttributes:b}),React.createElement(o.TextControl,{label:(0,a.__)("Screen reader text","toggle-block"),description:(0,a.__)("Enter a description of what this toggle controls.","toggle-block"),value:i,onChange:e=>b({labelText:e})}),React.createElement(o.TextControl,{label:(0,a.__)("Body class","toggle-block"),description:(0,a.__)("Enter a class to add to the body when the toggle is active.","toggle-block"),value:c,onChange:e=>b({bodyClass:e})}),React.createElement(o.ToggleControl,{label:(0,a.__)("Default toggle","toggle-block"),help:(0,a.__)("When inside a toggle group, this toggle will be active by default.","toggle-block"),checked:s,onChange:e=>b({defaultToggle:e})}))),React.createElement(t.RichText,e({},(0,t.useBlockProps)(),{tagName:"span",label:(0,a.__)("Button text","toggle-block"),placeholder:(0,a.__)("Button text","toggle-block"),value:n,onChange:e=>{b({buttonText:e})}})))},save:l=>{const{attributes:{bodyClass:o,buttonText:a,controlsId:c,defaultToggle:n,labelText:r}}=l;return React.createElement("button",e({},t.useBlockProps.save(),r&&{"aria-label":r},{"aria-controls":c},o&&{"data-body-class":o},n&&{"data-default-toggle":"true"}),React.createElement("span",null,a))},transforms:b,deprecated:n})})(); \ No newline at end of file diff --git a/src/toggle-block/block-selector.js b/src/toggle-block/block-selector.js new file mode 100644 index 0000000..1a5aad7 --- /dev/null +++ b/src/toggle-block/block-selector.js @@ -0,0 +1,336 @@ +// WordPress dependencies. +import { BlockIcon } from '@wordpress/block-editor'; +import { getBlockType } from '@wordpress/blocks'; +import { + Button, + Modal, + Notice, + SearchControl, + TextControl, +} from '@wordpress/components'; +import { useDispatch, useSelect } from '@wordpress/data'; +import { useMemo, useState } from '@wordpress/element'; +import { __, sprintf } from '@wordpress/i18n'; + +/** + * Recursively flatten a tree of blocks into a single array. + * + * @param {Array} blocks Blocks from the block-editor store. + * @returns {Array} Flat list in document order. + */ +const flattenBlocks = (blocks) => { + const result = []; + for (const block of blocks) { + result.push(block); + if (block.innerBlocks?.length) { + result.push(...flattenBlocks(block.innerBlocks)); + } + } + return result; +}; + +/** + * Pull a short preview string out of a block's attributes. + * + * @param {object} block A block object from the block-editor store. + * @returns {string} A short, plain-text preview, or an empty string. + */ +const getBlockPreview = (block) => { + const candidates = [ + block.attributes?.content, + block.attributes?.text, + block.attributes?.title, + block.attributes?.label, + block.attributes?.value, + block.attributes?.citation, + block.attributes?.caption, + ]; + + for (const candidate of candidates) { + if (typeof candidate === 'string' && candidate.trim()) { + const stripped = candidate.replace(/<[^>]+>/g, '').trim(); + if (stripped) { + return stripped.length > 80 + ? stripped.slice(0, 80) + '…' + : stripped; + } + } + } + + return ''; +}; + +/** + * Produce a reasonably unique anchor for a block that doesn't yet have one. + * + * @param {object} block A block object from the block-editor store. + * @param {Set} existing A set of anchor IDs that are already in use. + * @returns {string} A unique anchor string. + */ +const generateAnchor = (block, existing) => { + const base = (block.name || 'block').replace(/.*\//, ''); + let attempt; + do { + attempt = `${base}-${Math.random().toString(36).slice(2, 8)}`; + } while (existing.has(attempt)); + return attempt; +}; + +const BlockSelector = ({ clientId, controlsId, setAttributes }) => { + const [isOpen, setIsOpen] = useState(false); + const [search, setSearch] = useState(''); + const [showManual, setShowManual] = useState(false); + + const { updateBlockAttributes } = useDispatch('core/block-editor'); + + const allBlocks = useSelect( + (select) => flattenBlocks(select('core/block-editor').getBlocks()), + [] + ); + + const existingAnchors = useMemo(() => { + const set = new Set(); + for (const block of allBlocks) { + if (block.attributes?.anchor) { + set.add(block.attributes.anchor); + } + } + return set; + }, [allBlocks]); + + const selectedBlock = useMemo(() => { + if (!controlsId) { + return null; + } + return ( + allBlocks.find( + (block) => block.attributes?.anchor === controlsId + ) || null + ); + }, [allBlocks, controlsId]); + + const candidates = useMemo(() => { + return allBlocks.filter((block) => { + if (block.clientId === clientId) { + return false; + } + if (block.name === 'happyprime/toggle-block') { + return false; + } + return true; + }); + }, [allBlocks, clientId]); + + const filtered = useMemo(() => { + if (!search.trim()) { + return candidates; + } + const needle = search.toLowerCase(); + return candidates.filter((block) => { + const blockType = getBlockType(block.name); + const title = (blockType?.title || block.name).toLowerCase(); + const anchor = (block.attributes?.anchor || '').toLowerCase(); + const preview = getBlockPreview(block).toLowerCase(); + return ( + title.includes(needle) || + anchor.includes(needle) || + preview.includes(needle) + ); + }); + }, [candidates, search]); + + const selectBlock = (block) => { + let anchor = block.attributes?.anchor; + + if (!anchor) { + anchor = generateAnchor(block, existingAnchors); + updateBlockAttributes(block.clientId, { anchor }); + } + + setAttributes({ controlsId: anchor }); + setIsOpen(false); + setSearch(''); + }; + + const clearSelection = () => { + setAttributes({ controlsId: '' }); + }; + + const selectedBlockType = selectedBlock + ? getBlockType(selectedBlock.name) + : null; + + return ( +
+ {selectedBlock ? ( +
+
+ {selectedBlockType?.icon && ( + + )} + + {selectedBlockType?.title || selectedBlock.name} + +
+ + #{controlsId} + + {getBlockPreview(selectedBlock) && ( +

+ {getBlockPreview(selectedBlock)} +

+ )} +
+ ) : controlsId ? ( +
+ + {sprintf( + /* translators: %s: anchor id */ + __( + 'No block with anchor "%s" was found in this post. The toggle will still target this ID at runtime.', + 'toggle-block' + ), + controlsId + )} + +
+ ) : ( +

+ {__( + 'No block selected. Browse available blocks to choose which one this toggle controls.', + 'toggle-block' + )} +

+ )} + +
+ + {controlsId && ( + + )} +
+ + + + {showManual && ( + setAttributes({ controlsId: value })} + /> + )} + + {isOpen && ( + { + setIsOpen(false); + setSearch(''); + }} + className="toggle-block-selector__modal" + > + + + {filtered.length === 0 ? ( +

+ {candidates.length === 0 + ? __( + 'No other blocks in this post yet. Add the content you want to toggle first.', + 'toggle-block' + ) + : __( + 'No blocks match your search.', + 'toggle-block' + )} +

+ ) : ( +
    + {filtered.map((block) => { + const blockType = getBlockType(block.name); + const preview = getBlockPreview(block); + const anchor = block.attributes?.anchor; + const isSelected = + anchor && anchor === controlsId; + + return ( +
  • + +
  • + ); + })} +
+ )} +
+ )} +
+ ); +}; + +export default BlockSelector; diff --git a/src/toggle-block/index.css b/src/toggle-block/index.css index e353493..009660d 100644 --- a/src/toggle-block/index.css +++ b/src/toggle-block/index.css @@ -1,3 +1,146 @@ .editor-styles-wrapper .wp-block-happyprime-toggle-block { display: inline-block; } + +.toggle-block-selector__current { + background: #f0f0f0; + border-radius: 4px; + padding: 12px; + margin-bottom: 12px; +} + +.toggle-block-selector__current--orphan { + background: transparent; + padding: 0; +} + +.toggle-block-selector__current-header { + display: flex; + align-items: center; + gap: 6px; + margin-bottom: 6px; +} + +.toggle-block-selector__current-title { + font-weight: 600; +} + +.toggle-block-selector__current-anchor { + display: inline-block; + font-size: 12px; + background: #fff; + padding: 2px 6px; + border-radius: 3px; + word-break: break-all; +} + +.toggle-block-selector__current-preview { + margin: 8px 0 0; + font-size: 12px; + color: #555; + line-height: 1.4; +} + +.toggle-block-selector__empty { + margin: 0 0 12px; + font-size: 12px; + color: #555; +} + +.toggle-block-selector__actions { + display: flex; + gap: 8px; + margin-bottom: 8px; +} + +.toggle-block-selector__manual-toggle { + padding: 0; + font-size: 12px; +} + +.toggle-block-selector__modal { + max-width: 560px; +} + +.toggle-block-selector__list { + list-style: none; + margin: 12px 0 0; + padding: 0; + max-height: 60vh; + overflow-y: auto; +} + +.toggle-block-selector__list-item { + margin: 0; + padding: 0; +} + +.toggle-block-selector__list-item + .toggle-block-selector__list-item { + border-top: 1px solid #f0f0f0; +} + +.toggle-block-selector__list-item.is-selected .toggle-block-selector__list-button { + background: #f0f6fc; +} + +.toggle-block-selector__list-button { + display: flex; + align-items: flex-start; + gap: 12px; + width: 100%; + padding: 10px 12px; + background: transparent; + border: 0; + border-radius: 4px; + cursor: pointer; + text-align: left; +} + +.toggle-block-selector__list-button:hover, +.toggle-block-selector__list-button:focus { + background: #f0f0f0; +} + +.toggle-block-selector__list-icon { + flex: 0 0 auto; + display: flex; + align-items: center; + justify-content: center; + width: 24px; + height: 24px; + margin-top: 2px; +} + +.toggle-block-selector__list-body { + display: flex; + flex-direction: column; + gap: 2px; + flex: 1 1 auto; + min-width: 0; +} + +.toggle-block-selector__list-title { + font-weight: 600; + font-size: 13px; +} + +.toggle-block-selector__list-preview { + font-size: 12px; + color: #555; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.toggle-block-selector__list-anchor { + font-size: 11px; + color: #777; + font-family: Menlo, Consolas, monospace; + word-break: break-all; +} + +.toggle-block-selector__no-results { + margin: 16px 0; + color: #555; + font-size: 13px; +} diff --git a/src/toggle-block/index.js b/src/toggle-block/index.js index c2b04aa..a467ed9 100644 --- a/src/toggle-block/index.js +++ b/src/toggle-block/index.js @@ -11,6 +11,7 @@ import { __ } from '@wordpress/i18n'; // Internal dependencies. import metadata from './block.json'; import deprecated from './deprecated'; +import BlockSelector from './block-selector'; // Extend the navigation block to allow the toggle block. import './extend-navigation-block'; @@ -24,6 +25,7 @@ const Edit = (props) => { defaultToggle, labelText, }, + clientId, setAttributes, } = props; @@ -31,16 +33,10 @@ const Edit = (props) => { <> - - setAttributes({ controlsId: value }) - } +