Skip to content

Instantly share code, notes, and snippets.

@dlh01
Last active July 7, 2024 04:28
Show Gist options
  • Save dlh01/1b18743cb03e26b5e094ec470aca2690 to your computer and use it in GitHub Desktop.
Save dlh01/1b18743cb03e26b5e094ec470aca2690 to your computer and use it in GitHub Desktop.
Block editor

Block editor things to remember.

import { store as blocksStore } from '@wordpress/blocks';
const blockType = useSelect((select) => select(blocksStore).getBlockType(name), [name]);
const map = blockType.attributes.myAttribute.enum.map((e) => e);
// https://l.alley.dev/2551c5bf3d
// Global dependencies.
import useSWR from 'swr';
// WordPress dependencies.
import apiFetch from '@wordpress/api-fetch';
import { ComboboxControl } from '@wordpress/components';
import { useDebouncedInput } from '@wordpress/compose';
import { __, sprintf } from '@wordpress/i18n';
import { addQueryArgs } from '@wordpress/url';
// Local interfaces.
interface BlockAttributes {
id?: string,
}
/**
* Internal component for displaying the setup state, including the currently selected item.
*/
function Setup({
attributes,
setAttributes,
}: {
attributes: BlockAttributes,
setAttributes: (next: any) => void,
}) {
/**
* Using the currently selected item as the search term has the effect of causing that item to
* be searched for when the component loads, which populates the options list with that item.
*/
const [searchTerm, setSearchTerm, debouncedSearchTerm] = useDebouncedInput(attributes.id);
/**
* TODO: The matching item should be captured so that it's always available to use as an option,
* since the label shown in the input is drawn from the options list.
*/
const { data, isLoading }: { data: any, isLoading: boolean } = useSWR(
addQueryArgs('alley/v1/blocks/block-name/search', { search: debouncedSearchTerm }),
(key) => apiFetch({ path: key }),
);
return (
<ComboboxControl
label={__('Search for items', 'alley')}
value={attributes.item}
onChange={(next) => setAttributes({ item: next || '' })}
onFilterValueChange={(next) => setSearchTerm(next)}
options={isLoading
// Small hack. See https://github.com/WordPress/gutenberg/issues/25798.
? [{
/* translators: %s: search term */
label: searchTerm ? sprintf(__('Searching for "%s"…', 'alley'), searchTerm) : __('Loading…', 'alley'),
value: '',
disabled: true,
}]
: data.items.map((item: any) => ({
label: item.title,
value: item.id,
}))}
// @ts-ignore
expandOnFocus={attributes.item === ''}
/>
);
}
// Global dependencies.
import { useState } from 'react';
// WordPress dependencies.
import { useBlockProps } from '@wordpress/block-editor';
import { BlockEditProps, store as blocksStore } from '@wordpress/blocks';
import {
Button,
Placeholder,
TextControl,
} from '@wordpress/components';
import { useSelect } from '@wordpress/data';
import { format as formattedDate } from '@wordpress/date';
import { View } from '@wordpress/primitives';
interface EditProps extends BlockEditProps<any> {
name: string;
}
const dateFormattedForInput = (date: any) => formattedDate('Y-m-d\\TH:i', date);
const dateFormattedForAttribute = (date: any) => formattedDate('Y-m-d\\TH:i:sP', date);
/**
* @return {WPElement} Element to render.
*/
export default function Edit({
name,
isSelected,
attributes,
setAttributes,
}: EditProps) {
const {
date: dateAttribute,
} = attributes;
// @ts-ignore
const blockType = useSelect((select) => select(blocksStore).getBlockType(name), [name]);
const [showSetup, setShowSetup] = useState(true);
const [dateInput, setDateInput] = useState(dateFormattedForInput(new Date()));
return (
<View {...useBlockProps()}>
{showSetup ? (
<Placeholder
label={blockType.title}
icon={blockType?.icon?.src}
instructions={blockType.description}
>
<form
className="setup-form"
onSubmit={(event) => {
event.preventDefault();
setAttributes({
date: dateFormattedForAttribute(dateInput),
});
setShowSetup(false);
}}
>
<TextControl
label="End Date"
value={dateInput}
// @ts-ignore
type="datetime-local"
onChange={(value) => setDateInput(value)}
required
/>
<div>
<Button
variant="primary"
type="submit"
>
Embed
</Button>
</div>
</form>
</Placeholder>
) : (
<>
{isSelected ? (
<div className="controls">
<div>
<TextControl
label="End Date"
value={dateFormattedForInput(dateAttribute)}
// @ts-ignore
type="datetime-local"
onChange={(value) => setAttributes({ date: dateFormattedForAttribute(value) })}
/>
</div>
</div>
) : null}
</>
)}
</View>
);
}
/*
attributes
clientId
context
insertBlocksAfter (fn)
isSelected
isSelectionEnabled
mergeBlocks (fn)
name
onRemove (fn)
onReplace (fn)
setAttributes
toggleSelection (fn)
*/
interface EditProps extends BlockEditProps<any> {
name: string;
}
// Take a string of HTML, create blocks out of it, and inject the result as inner blocks.
import { useBlockProps, InnerBlocks } from '@wordpress/block-editor';
import { rawHandler } from '@wordpress/blocks';
import { useDispatch } from '@wordpress/data';
const Edit = ({ clientId }) => {
const { replaceInnerBlocks } = useDispatch('core/block-editor');
const blockProps = useBlockProps();
const rawHtml = '<p>Foo bar</p>';
// When the block is mounted, convert the raw meta HTML to inner blocks of this block.
useEffect(() => {
const blocks = rawHandler({ HTML: rawHtml });
replaceInnerBlocks(clientId, blocks);
}, []);
return (
<div {...blockProps}>
<InnerBlocks />
</div>
);
};
import { useBlockProps } from '@wordpress/block-editor';
import { BlockEditProps, store as blocksStore } from '@wordpress/blocks';
import { Placeholder } from '@wordpress/components';
import { useSelect } from '@wordpress/data';
import './index.scss';
interface EditProps extends BlockEditProps<any> {
context: object;
name: string;
}
/**
* The block edit function.
*
* @return {WPElement} Element to render.
*/
export default function Edit({ name }: EditProps) {
// @ts-ignore
const blockType = useSelect((select) => select(blocksStore).getBlockType(name), [name]);
return (
<div {...(useBlockProps())}>
<Placeholder
label={blockType.title}
icon={blockType?.icon?.src}
instructions={blockType.description}
/>
</div>
);
}
import { serialize } from '@wordpress/blocks';
import { useSelect } from '@wordpress/data';
export default function Edit({ clientId }) => {
const blockObj = useSelect((select) => select('core/block-editor').getBlock(clientId));
const innerBlockHtml = serialize(blockObj.innerBlocks);
}

Designing a [block] is a small tractable task that can be finished and called done. You’re creating a perfect, beautiful, reusable jewel. All engineers really want to do is write [blocks]. The top-down approach doesn’t have this nice property; the product is forever incomplete. Yes, top-down can be less appealing to some people. Do it anyway.

Adapted

import { getSettings as dateSettings } from '@wordpress/date';
const { timezone } = dateSettings();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment