Skip to content

Commit

Permalink
feat(selectors): add getActiveStyle and isActiveStyle
Browse files Browse the repository at this point in the history
  • Loading branch information
christianhg committed Dec 13, 2024
1 parent 8c548f3 commit 37ba256
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 26 deletions.
34 changes: 8 additions & 26 deletions apps/playground/src/portable-text-toolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
} from '@portabletext/editor'
import * as selectors from '@portabletext/editor/selectors'
import {SquareDashedMousePointerIcon} from 'lucide-react'
import {isValidElement, useMemo} from 'react'
import {isValidElement} from 'react'
import {Group, TooltipTrigger} from 'react-aria-components'
import {isValidElementType} from 'react-is'
import {Button} from './components/button'
Expand All @@ -29,12 +29,7 @@ export function PortableTextToolbar(props: {

return (
<Toolbar aria-label="Text formatting">
<StyleSelector
schemaDefinition={props.schemaDefinition}
editor={editor}
editorInstance={editorInstance}
selection={selection}
/>
<StyleSelector schemaDefinition={props.schemaDefinition} />
<Separator orientation="vertical" />
<Group aria-label="Decorators" className="contents">
{props.schemaDefinition.decorators?.map((decorator) => (
Expand Down Expand Up @@ -98,32 +93,19 @@ export function PortableTextToolbar(props: {
)
}

function StyleSelector(props: {
schemaDefinition: SchemaDefinition
editor: Editor
editorInstance: PortableTextEditor
selection: EditorSelection
}) {
const focusBlock = PortableTextEditor.focusBlock(props.editorInstance)
const activeStyle = useMemo(
() =>
focusBlock
? (props.schemaDefinition.styles.find((style) =>
PortableTextEditor.hasBlockStyle(props.editorInstance, style.name),
)?.name ?? null)
: null,
[props.editorInstance, focusBlock, props.schemaDefinition],
)
function StyleSelector(props: {schemaDefinition: SchemaDefinition}) {
const editor = useEditor()
const activeStyle = useEditorSelector(editor, selectors.getActiveStyle)

return (
<Select
placeholder="Select style"
aria-label="Style"
selectedKey={activeStyle}
selectedKey={activeStyle ?? null}
onSelectionChange={(style) => {
if (typeof style === 'string') {
props.editor.send({type: 'style.toggle', style})
props.editor.send({type: 'focus'})
editor.send({type: 'style.toggle', style})
editor.send({type: 'focus'})
}
}}
>
Expand Down
2 changes: 2 additions & 0 deletions packages/editor/src/selectors/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ export type {

export type {EditorSchema} from '../editor/define-schema'
export {getActiveListItem} from './selector.get-active-list-item'
export {getActiveStyle} from './selector.get-active-style'
export {getSelectedSpans} from './selector.get-selected-spans'
export {getSelectionText} from './selector.get-selection-text'
export {getBlockTextBefore} from './selector.get-text-before'
export {isActiveStyle} from './selector.is-active-style'
export {isDecoratorActive} from './selector.is-decorator-active'
export * from './selectors'
37 changes: 37 additions & 0 deletions packages/editor/src/selectors/selector.get-active-style.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import type {PortableTextTextBlock} from '@sanity/types'
import {createGuards} from '../behavior-actions/behavior.guards'
import type {EditorSelector} from '../editor/editor-selector'
import {getSelectedBlocks} from './selectors'

/**
* @alpha
*/
export const getActiveStyle: EditorSelector<PortableTextTextBlock['style']> = ({
context,
}) => {
if (!context.selection) {
return undefined
}

const guards = createGuards(context)
const selectedBlocks = getSelectedBlocks({context}).map((block) => block.node)
const selectedTextBlocks = selectedBlocks.filter(guards.isTextBlock)

const firstTextBlock = selectedTextBlocks.at(0)

if (!firstTextBlock) {
return undefined
}

const firstStyle = firstTextBlock.style

if (!firstStyle) {
return undefined
}

if (selectedTextBlocks.every((block) => block.style === firstStyle)) {
return firstStyle
}

return undefined
}
13 changes: 13 additions & 0 deletions packages/editor/src/selectors/selector.is-active-style.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import type {EditorSelector} from '../editor/editor-selector'
import {getActiveStyle} from './selector.get-active-style'

/**
* @alpha
*/
export function isActiveStyle(style: string): EditorSelector<boolean> {
return (snapshot) => {
const activeStyle = getActiveStyle(snapshot)

return activeStyle === style
}
}

0 comments on commit 37ba256

Please sign in to comment.