@payload-onpage/react 0.2.1 → 0.3.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.
Files changed (217) hide show
  1. package/dist/OnPageClient.d.ts.map +1 -1
  2. package/dist/OnPageClient.js +9 -5
  3. package/dist/OnPageClient.js.map +1 -1
  4. package/dist/annotations.d.ts +1 -1
  5. package/dist/annotations.d.ts.map +1 -1
  6. package/dist/context.d.ts +19 -5
  7. package/dist/context.d.ts.map +1 -1
  8. package/dist/context.js +31 -7
  9. package/dist/context.js.map +1 -1
  10. package/dist/dom.d.ts +16 -12
  11. package/dist/dom.d.ts.map +1 -1
  12. package/dist/dom.js +16 -16
  13. package/dist/dom.js.map +1 -1
  14. package/dist/editor/FieldHost.d.ts.map +1 -1
  15. package/dist/editor/FieldHost.js +18 -11
  16. package/dist/editor/FieldHost.js.map +1 -1
  17. package/dist/editor/OnPageEditor.d.ts +15 -8
  18. package/dist/editor/OnPageEditor.d.ts.map +1 -1
  19. package/dist/editor/OnPageEditor.js +73 -103
  20. package/dist/editor/OnPageEditor.js.map +1 -1
  21. package/dist/editor/editTarget.d.ts +28 -0
  22. package/dist/editor/editTarget.d.ts.map +1 -0
  23. package/dist/editor/editTarget.js +20 -0
  24. package/dist/editor/editTarget.js.map +1 -0
  25. package/dist/editor/useClientDocs.d.ts +4 -0
  26. package/dist/editor/useClientDocs.d.ts.map +1 -0
  27. package/dist/editor/useClientDocs.js +16 -0
  28. package/dist/editor/useClientDocs.js.map +1 -0
  29. package/dist/editor/useEditorSession.d.ts +13 -0
  30. package/dist/editor/useEditorSession.d.ts.map +1 -0
  31. package/dist/editor/useEditorSession.js +30 -0
  32. package/dist/editor/useEditorSession.js.map +1 -0
  33. package/dist/editor/useFlash.d.ts +4 -0
  34. package/dist/editor/useFlash.d.ts.map +1 -0
  35. package/dist/editor/useFlash.js +16 -0
  36. package/dist/editor/useFlash.js.map +1 -0
  37. package/dist/editor/useGhosts.d.ts +5 -7
  38. package/dist/editor/useGhosts.d.ts.map +1 -1
  39. package/dist/editor/useGhosts.js.map +1 -1
  40. package/dist/editor/usePageEvents.d.ts.map +1 -1
  41. package/dist/editor/usePageEvents.js +16 -16
  42. package/dist/editor/usePageEvents.js.map +1 -1
  43. package/dist/editor-kit.d.ts +10 -6
  44. package/dist/editor-kit.d.ts.map +1 -1
  45. package/dist/editor-kit.js +8 -5
  46. package/dist/editor-kit.js.map +1 -1
  47. package/dist/editors/CheckboxEditor.d.ts +6 -1
  48. package/dist/editors/CheckboxEditor.d.ts.map +1 -1
  49. package/dist/editors/CheckboxEditor.js +6 -3
  50. package/dist/editors/CheckboxEditor.js.map +1 -1
  51. package/dist/editors/ColorEditor.d.ts +3 -1
  52. package/dist/editors/ColorEditor.d.ts.map +1 -1
  53. package/dist/editors/ColorEditor.js +9 -15
  54. package/dist/editors/ColorEditor.js.map +1 -1
  55. package/dist/editors/DateEditor.d.ts +3 -1
  56. package/dist/editors/DateEditor.d.ts.map +1 -1
  57. package/dist/editors/DateEditor.js +10 -12
  58. package/dist/editors/DateEditor.js.map +1 -1
  59. package/dist/editors/InlineText.d.ts +2 -2
  60. package/dist/editors/InlineText.d.ts.map +1 -1
  61. package/dist/editors/InlineText.js +2 -3
  62. package/dist/editors/InlineText.js.map +1 -1
  63. package/dist/editors/NumberEditor.d.ts +3 -1
  64. package/dist/editors/NumberEditor.d.ts.map +1 -1
  65. package/dist/editors/NumberEditor.js +9 -17
  66. package/dist/editors/NumberEditor.js.map +1 -1
  67. package/dist/editors/RelationshipEditor.d.ts +7 -1
  68. package/dist/editors/RelationshipEditor.d.ts.map +1 -1
  69. package/dist/editors/RelationshipEditor.js +10 -18
  70. package/dist/editors/RelationshipEditor.js.map +1 -1
  71. package/dist/editors/SelectEditor.d.ts +4 -1
  72. package/dist/editors/SelectEditor.d.ts.map +1 -1
  73. package/dist/editors/SelectEditor.js +8 -13
  74. package/dist/editors/SelectEditor.js.map +1 -1
  75. package/dist/editors/UploadEditor.d.ts +7 -1
  76. package/dist/editors/UploadEditor.d.ts.map +1 -1
  77. package/dist/editors/UploadEditor.js +5 -5
  78. package/dist/editors/UploadEditor.js.map +1 -1
  79. package/dist/editors/index.d.ts +0 -1
  80. package/dist/editors/index.d.ts.map +1 -1
  81. package/dist/editors/index.js +56 -16
  82. package/dist/editors/index.js.map +1 -1
  83. package/dist/editors/kit.d.ts +39 -0
  84. package/dist/editors/kit.d.ts.map +1 -0
  85. package/dist/editors/kit.js +44 -0
  86. package/dist/editors/kit.js.map +1 -0
  87. package/dist/editors/useDebouncedSearch.d.ts +10 -6
  88. package/dist/editors/useDebouncedSearch.d.ts.map +1 -1
  89. package/dist/editors/useDebouncedSearch.js +11 -7
  90. package/dist/editors/useDebouncedSearch.js.map +1 -1
  91. package/dist/geometry.d.ts +49 -0
  92. package/dist/geometry.d.ts.map +1 -0
  93. package/dist/geometry.js +48 -0
  94. package/dist/geometry.js.map +1 -0
  95. package/dist/hub.d.ts +4 -4
  96. package/dist/hub.d.ts.map +1 -1
  97. package/dist/hub.js.map +1 -1
  98. package/dist/index.d.ts +3 -3
  99. package/dist/index.d.ts.map +1 -1
  100. package/dist/index.js +2 -2
  101. package/dist/index.js.map +1 -1
  102. package/dist/layout.d.ts.map +1 -1
  103. package/dist/layout.js +4 -2
  104. package/dist/layout.js.map +1 -1
  105. package/dist/locators.d.ts +0 -2
  106. package/dist/locators.d.ts.map +1 -1
  107. package/dist/locators.js +10 -16
  108. package/dist/locators.js.map +1 -1
  109. package/dist/messages.d.ts +15 -2
  110. package/dist/messages.d.ts.map +1 -1
  111. package/dist/messages.js +5 -0
  112. package/dist/messages.js.map +1 -1
  113. package/dist/registry.d.ts +21 -2
  114. package/dist/registry.d.ts.map +1 -1
  115. package/dist/registry.js +31 -6
  116. package/dist/registry.js.map +1 -1
  117. package/dist/rowOps.d.ts +20 -0
  118. package/dist/rowOps.d.ts.map +1 -0
  119. package/dist/rowOps.js +48 -0
  120. package/dist/rowOps.js.map +1 -0
  121. package/dist/sanitize.d.ts +2 -0
  122. package/dist/sanitize.d.ts.map +1 -1
  123. package/dist/sanitize.js +3 -0
  124. package/dist/sanitize.js.map +1 -1
  125. package/dist/settings.d.ts +10 -0
  126. package/dist/settings.d.ts.map +1 -0
  127. package/dist/settings.js +30 -0
  128. package/dist/settings.js.map +1 -0
  129. package/dist/styles.d.ts +0 -4
  130. package/dist/styles.d.ts.map +1 -1
  131. package/dist/styles.js +2 -1
  132. package/dist/styles.js.map +1 -1
  133. package/dist/targets.d.ts +1 -1
  134. package/dist/targets.d.ts.map +1 -1
  135. package/dist/types.d.ts +71 -46
  136. package/dist/types.d.ts.map +1 -1
  137. package/dist/ui/BlockPicker.d.ts.map +1 -1
  138. package/dist/ui/BlockPicker.js +6 -5
  139. package/dist/ui/BlockPicker.js.map +1 -1
  140. package/dist/ui/Dialog.d.ts +0 -5
  141. package/dist/ui/Dialog.d.ts.map +1 -1
  142. package/dist/ui/Dialog.js +10 -6
  143. package/dist/ui/Dialog.js.map +1 -1
  144. package/dist/ui/Dock.d.ts +1 -6
  145. package/dist/ui/Dock.d.ts.map +1 -1
  146. package/dist/ui/Dock.js +46 -28
  147. package/dist/ui/Dock.js.map +1 -1
  148. package/dist/ui/Overlay.d.ts.map +1 -1
  149. package/dist/ui/Overlay.js +39 -37
  150. package/dist/ui/Overlay.js.map +1 -1
  151. package/dist/ui/PreviewToolbar.d.ts +3 -3
  152. package/dist/ui/PreviewToolbar.d.ts.map +1 -1
  153. package/dist/ui/PreviewToolbar.js +4 -6
  154. package/dist/ui/PreviewToolbar.js.map +1 -1
  155. package/dist/ui/SettingsPanel.d.ts +0 -9
  156. package/dist/ui/SettingsPanel.d.ts.map +1 -1
  157. package/dist/ui/SettingsPanel.js +36 -41
  158. package/dist/ui/SettingsPanel.js.map +1 -1
  159. package/dist/ui/ToolbarButton.d.ts +12 -0
  160. package/dist/ui/ToolbarButton.d.ts.map +1 -0
  161. package/dist/ui/ToolbarButton.js +7 -0
  162. package/dist/ui/ToolbarButton.js.map +1 -0
  163. package/dist/useEvent.d.ts +1 -1
  164. package/dist/useEvent.d.ts.map +1 -1
  165. package/dist/useEvent.js +3 -1
  166. package/dist/useEvent.js.map +1 -1
  167. package/dist/useIsClient.d.ts +5 -0
  168. package/dist/useIsClient.d.ts.map +1 -0
  169. package/dist/useIsClient.js +8 -0
  170. package/dist/useIsClient.js.map +1 -0
  171. package/package.json +2 -2
  172. package/src/OnPageClient.tsx +9 -5
  173. package/src/annotations.ts +1 -1
  174. package/src/context.tsx +53 -10
  175. package/src/dom.ts +17 -18
  176. package/src/editor/FieldHost.tsx +26 -11
  177. package/src/editor/OnPageEditor.tsx +103 -160
  178. package/src/editor/editTarget.ts +41 -0
  179. package/src/editor/useClientDocs.ts +19 -0
  180. package/src/editor/useEditorSession.ts +36 -0
  181. package/src/editor/useFlash.ts +18 -0
  182. package/src/editor/useGhosts.ts +5 -7
  183. package/src/editor/usePageEvents.ts +13 -14
  184. package/src/editor-kit.ts +10 -6
  185. package/src/editors/CheckboxEditor.tsx +11 -12
  186. package/src/editors/ColorEditor.tsx +15 -35
  187. package/src/editors/DateEditor.tsx +18 -37
  188. package/src/editors/InlineText.tsx +4 -5
  189. package/src/editors/NumberEditor.tsx +15 -36
  190. package/src/editors/RelationshipEditor.tsx +18 -36
  191. package/src/editors/SelectEditor.tsx +14 -22
  192. package/src/editors/UploadEditor.tsx +6 -6
  193. package/src/editors/index.ts +56 -16
  194. package/src/editors/kit.tsx +97 -0
  195. package/src/editors/useDebouncedSearch.ts +18 -10
  196. package/src/geometry.ts +62 -0
  197. package/src/hub.ts +5 -5
  198. package/src/index.ts +5 -2
  199. package/src/layout.ts +5 -2
  200. package/src/locators.ts +11 -14
  201. package/src/messages.ts +26 -5
  202. package/src/registry.ts +42 -8
  203. package/src/rowOps.ts +64 -0
  204. package/src/sanitize.ts +6 -0
  205. package/src/settings.ts +49 -0
  206. package/src/styles.ts +2 -1
  207. package/src/targets.ts +1 -1
  208. package/src/types.ts +78 -45
  209. package/src/ui/BlockPicker.tsx +6 -7
  210. package/src/ui/Dialog.tsx +9 -5
  211. package/src/ui/Dock.tsx +67 -50
  212. package/src/ui/Overlay.tsx +75 -102
  213. package/src/ui/PreviewToolbar.tsx +7 -15
  214. package/src/ui/SettingsPanel.tsx +84 -81
  215. package/src/ui/ToolbarButton.tsx +36 -0
  216. package/src/useEvent.ts +4 -2
  217. package/src/useIsClient.ts +15 -0
@@ -6,40 +6,29 @@
6
6
  * spacing…), checkboxes, numbers, dates, relationships, URLs and other plain
7
7
  * text, images that aren't on the page. Changes apply immediately.
8
8
  */
9
- import { fieldAtPath, getAt, relationTitle, toText, type SchemaField } from '@payload-onpage/core'
9
+ import {
10
+ fieldAtPath,
11
+ getAt,
12
+ relationTitle,
13
+ rowFieldsOf,
14
+ toText,
15
+ isListField,
16
+ type ChoiceField,
17
+ type NumberField,
18
+ type ScalarField,
19
+ type SchemaField,
20
+ isReady,
21
+ type RefKey,
22
+ } from '@payload-onpage/core'
10
23
  import { useId, useRef, useState, type KeyboardEvent, type RefObject } from 'react'
11
24
  import { useOnPage } from '../context.tsx'
12
25
  import type { Translate } from '../messages.ts'
13
- import type { TargetIndex } from '../types.ts'
26
+ import { checkedOf } from '../editors/CheckboxEditor.tsx'
27
+ import { selectedValues } from '../editors/SelectEditor.tsx'
28
+ import { canEdit } from '../registry.ts'
29
+ import { settable } from '../settings.ts'
14
30
  import { Dialog } from './Dialog.tsx'
15
31
 
16
- const SKIP = new Set(['id', 'blockType', 'blockName'])
17
- const TEXTUAL = new Set(['text', 'textarea', 'email', 'number', 'date', 'relationship', 'upload'])
18
- const INPUT_TYPES = new Set(['text', 'textarea', 'email', 'number'])
19
-
20
- function settable(field: SchemaField, path: string, key: string, targets: TargetIndex): boolean {
21
- if (SKIP.has(field.name)) return false
22
- const full = `${path}.${field.name}`
23
- switch (field.type) {
24
- case 'select':
25
- case 'radio':
26
- case 'checkbox':
27
- return true
28
- case 'group':
29
- return (field.fields ?? []).some((f) => settable(f, full, key, targets))
30
- default:
31
- // Shown on the page? Edit it there.
32
- return TEXTUAL.has(field.type) && !targets.find(key, full)
33
- }
34
- }
35
-
36
- export const hasSettings = (
37
- fields: SchemaField[],
38
- path: string,
39
- key: string,
40
- targets: TargetIndex,
41
- ): boolean => fields.some((f) => settable(f, path, key, targets))
42
-
43
32
  const display = (v: unknown, t: Translate): string => {
44
33
  if (v == null || v === '') return t('common.none')
45
34
  if (Array.isArray(v))
@@ -53,9 +42,9 @@ function TextInput({
53
42
  field,
54
43
  id,
55
44
  }: {
56
- docKey: string
45
+ docKey: RefKey
57
46
  path: string
58
- field: SchemaField
47
+ field: ScalarField | NumberField
59
48
  id: string
60
49
  }) {
61
50
  const { session, snap } = useOnPage()
@@ -92,14 +81,15 @@ function Segmented({
92
81
  field,
93
82
  labelId,
94
83
  }: {
95
- docKey: string
84
+ docKey: RefKey
96
85
  path: string
97
- field: SchemaField
86
+ field: ChoiceField
98
87
  labelId: string
99
88
  }) {
100
89
  const { session, snap } = useOnPage()
101
- const options = field.options ?? []
102
- const current = getAt(snap.docs[docKey]?.doc, path) ?? field.defaultValue ?? ''
90
+ const options = field.options
91
+ const current =
92
+ selectedValues(getAt(snap.docs[docKey]?.doc, path) ?? field.defaultValue, field)[0] ?? ''
103
93
  const index = Math.max(
104
94
  0,
105
95
  options.findIndex((o) => o.value === current),
@@ -148,54 +138,17 @@ function Control({
148
138
  id,
149
139
  labelId,
150
140
  }: {
151
- docKey: string
141
+ docKey: RefKey
152
142
  path: string
153
143
  field: SchemaField
154
144
  anchor: RefObject<HTMLDivElement | null>
155
145
  id: string
156
146
  labelId: string
157
147
  }) {
158
- const { session, snap, edit, t } = useOnPage()
148
+ const { session, snap, edit, editors, t } = useOnPage()
159
149
  const value = getAt(snap.docs[docKey]?.doc, path)
160
150
  const set = (v: unknown) => session.setValue(docKey, path, v)
161
- if ((field.type === 'select' || field.type === 'radio') && !field.hasMany) {
162
- const options = field.options ?? []
163
- if (options.length <= 5)
164
- return <Segmented docKey={docKey} path={path} field={field} labelId={labelId} />
165
- return (
166
- <select
167
- id={id}
168
- className="po-select"
169
- value={toText(value ?? field.defaultValue)}
170
- onChange={(e) => set(e.target.value || null)}
171
- data-testid={`onpage-setting-${path}`}
172
- >
173
- {!field.required && <option value="">{t('common.none')}</option>}
174
- {options.map((o) => (
175
- <option key={o.value} value={o.value}>
176
- {o.label}
177
- </option>
178
- ))}
179
- </select>
180
- )
181
- }
182
- if (field.type === 'checkbox') {
183
- return (
184
- <label className="po-check">
185
- <input
186
- type="checkbox"
187
- checked={Boolean(value ?? field.defaultValue)}
188
- onChange={(e) => set(e.target.checked)}
189
- data-testid={`onpage-setting-${path}`}
190
- />
191
- {field.label}
192
- </label>
193
- )
194
- }
195
- if (INPUT_TYPES.has(field.type))
196
- return <TextInput docKey={docKey} path={path} field={field} id={id} />
197
- // Everything else opens its field editor, anchored to the panel row.
198
- return (
151
+ const openEditor = (
199
152
  <button
200
153
  id={id}
201
154
  type="button"
@@ -208,9 +161,59 @@ function Control({
208
161
  : display(value, t)}
209
162
  </button>
210
163
  )
164
+ // A field with its own editor (e.g. `editor: 'color'`) opens it, whatever its type.
165
+ if (field.editor && canEdit(editors[field.editor], field)) return openEditor
166
+ switch (field.type) {
167
+ case 'select':
168
+ case 'radio': {
169
+ if (field.hasMany) return openEditor
170
+ if (field.options.length <= 5)
171
+ return <Segmented docKey={docKey} path={path} field={field} labelId={labelId} />
172
+ return (
173
+ <select
174
+ id={id}
175
+ className="po-select"
176
+ value={selectedValues(value ?? field.defaultValue, field)[0] ?? ''}
177
+ onChange={(e) => set(e.target.value || null)}
178
+ data-testid={`onpage-setting-${path}`}
179
+ >
180
+ {!field.required && <option value="">{t('common.none')}</option>}
181
+ {field.options.map((o) => (
182
+ <option key={o.value} value={o.value}>
183
+ {o.label}
184
+ </option>
185
+ ))}
186
+ </select>
187
+ )
188
+ }
189
+ case 'checkbox':
190
+ return (
191
+ <label className="po-check">
192
+ <input
193
+ type="checkbox"
194
+ checked={checkedOf(value, field)}
195
+ onChange={(e) => set(e.target.checked)}
196
+ data-testid={`onpage-setting-${path}`}
197
+ />
198
+ {field.label}
199
+ </label>
200
+ )
201
+ case 'text':
202
+ case 'textarea':
203
+ case 'email':
204
+ case 'number':
205
+ return 'hasMany' in field && field.hasMany ? (
206
+ openEditor
207
+ ) : (
208
+ <TextInput docKey={docKey} path={path} field={field} id={id} />
209
+ )
210
+ default:
211
+ // Dates, relationships, images…: open the field editor, anchored to the panel row.
212
+ return openEditor
213
+ }
211
214
  }
212
215
 
213
- function Row({ docKey, path, field }: { docKey: string; path: string; field: SchemaField }) {
216
+ function Row({ docKey, path, field }: { docKey: RefKey; path: string; field: SchemaField }) {
214
217
  const anchor = useRef<HTMLDivElement>(null)
215
218
  const id = useId()
216
219
  const labelId = `${id}-label`
@@ -234,7 +237,7 @@ function Row({ docKey, path, field }: { docKey: string; path: string; field: Sch
234
237
  )
235
238
  }
236
239
 
237
- function Fields({ docKey, fields, path }: { docKey: string; fields: SchemaField[]; path: string }) {
240
+ function Fields({ docKey, fields, path }: { docKey: RefKey; fields: SchemaField[]; path: string }) {
238
241
  const { targets } = useOnPage()
239
242
  return (
240
243
  <>
@@ -246,7 +249,7 @@ function Fields({ docKey, fields, path }: { docKey: string; fields: SchemaField[
246
249
  return (
247
250
  <fieldset key={f.name} className="po-group">
248
251
  <legend className="po-h4">{f.label}</legend>
249
- <Fields docKey={docKey} fields={f.fields ?? []} path={full} />
252
+ <Fields docKey={docKey} fields={f.fields} path={full} />
250
253
  </fieldset>
251
254
  )
252
255
  }
@@ -260,11 +263,11 @@ export function SettingsPanel() {
260
263
  const { panel, setPanel, snap, t } = useOnPage()
261
264
  if (panel?.kind !== 'settings') return null
262
265
  const state = snap.docs[panel.key]
263
- if (!state?.schema || !state.doc) return null
264
- const owner = fieldAtPath(state.schema.fields, state.doc, panel.path)
266
+ if (!isReady(state)) return null
267
+ const owner = fieldAtPath(state.schema.fields, state.doc, panel.path)?.field
265
268
  // Row fields: the block's fields for a blocks row, the item fields for an array row.
266
269
  const fields =
267
- owner?.field.type === 'blocks' ? (owner.block?.fields ?? []) : (owner?.field.fields ?? [])
270
+ owner && isListField(owner) ? (rowFieldsOf(owner, getAt(state.doc, panel.path)) ?? []) : []
268
271
  return (
269
272
  <Dialog
270
273
  variant="panel"
@@ -0,0 +1,36 @@
1
+ 'use client'
2
+
3
+ import type { ReactNode } from 'react'
4
+
5
+ /** An icon button in a toolbar: labelled for assistive tech, with the label (or a hint) as its tooltip. */
6
+ export function ToolbarButton({
7
+ label,
8
+ hint,
9
+ icon,
10
+ onClick,
11
+ disabled,
12
+ danger,
13
+ testId,
14
+ }: {
15
+ label: string
16
+ hint?: string
17
+ icon: ReactNode
18
+ onClick: () => void
19
+ disabled?: boolean
20
+ danger?: boolean
21
+ testId: string
22
+ }) {
23
+ return (
24
+ <button
25
+ type="button"
26
+ className={`po-btn${danger ? ' is-danger' : ''}`}
27
+ disabled={disabled}
28
+ onClick={onClick}
29
+ aria-label={label}
30
+ title={hint ?? label}
31
+ data-testid={testId}
32
+ >
33
+ {icon}
34
+ </button>
35
+ )
36
+ }
package/src/useEvent.ts CHANGED
@@ -7,10 +7,12 @@ import { useCallback, useInsertionEffect, useRef } from 'react'
7
7
  * pattern). Our own, because the React bundled with Next.js 15 doesn't export
8
8
  * `useEffectEvent`. Call it from effects and event handlers, not during render.
9
9
  */
10
- export function useEvent<A extends unknown[], R>(fn: (...args: A) => R): (...args: A) => R {
10
+ export function useEvent<F extends (...args: never[]) => unknown>(fn: F): F {
11
11
  const latest = useRef(fn)
12
12
  useInsertionEffect(() => {
13
13
  latest.current = fn
14
14
  })
15
- return useCallback((...args: A) => latest.current(...args), [])
15
+ const stable = useCallback((...args: Parameters<F>) => latest.current(...args), [])
16
+ // The wrapper has F's exact signature: it forwards its arguments and result unchanged.
17
+ return stable as F
16
18
  }
@@ -0,0 +1,15 @@
1
+ import { useEffect, useSyncExternalStore } from 'react'
2
+ import { injectStyles } from './styles.ts'
3
+
4
+ const noopSubscribe = () => () => {}
5
+
6
+ /** False during SSR and hydration, true after: for UI that only exists in the browser. */
7
+ export const useIsClient = (): boolean =>
8
+ useSyncExternalStore(
9
+ noopSubscribe,
10
+ () => true,
11
+ () => false,
12
+ )
13
+
14
+ /** Adds the editor's stylesheet to the page while mounted. */
15
+ export const useEditorStyles = (): void => useEffect(() => injectStyles(), [])