@jbpark/live-editor 6.1.0 → 6.1.1
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/README.ko.md +8 -0
- package/README.md +8 -0
- package/dist/{boundary-CySnDUs2.js → boundary-CLMNMZFa.js} +4 -4
- package/dist/boundary-CLMNMZFa.js.map +1 -0
- package/dist/{cn-CAnad2vL.js → compile-BWZ5U59y.js} +135 -2
- package/dist/compile-BWZ5U59y.js.map +1 -0
- package/dist/{context-Bjq0CvBE.js → context-DJxX9epB.js} +2 -3
- package/dist/context-DJxX9epB.js.map +1 -0
- package/dist/{core-BNVuH8-l.js → core-BfJwD8Jx.js} +3 -6
- package/dist/core-BfJwD8Jx.js.map +1 -0
- package/dist/dnd/index.js +2 -2
- package/dist/{dnd-COjjjuD7.js → dnd-DAGjlJ0_.js} +206 -167
- package/dist/dnd-DAGjlJ0_.js.map +1 -0
- package/dist/{document-BkGWDZGc.js → document-MkQGxfry.js} +710 -318
- package/dist/document-MkQGxfry.js.map +1 -0
- package/dist/editor/index.js +2 -2
- package/dist/{editor-CvnzrM9J.js → editor-B_VOz3Q0.js} +2 -2
- package/dist/{editor-CvnzrM9J.js.map → editor-B_VOz3Q0.js.map} +1 -1
- package/dist/editor-caches-DXYXgWeD.js +63 -0
- package/dist/editor-caches-DXYXgWeD.js.map +1 -0
- package/dist/error/index.d.ts +1 -1
- package/dist/error/index.js +2 -2
- package/dist/{error-CFzvnLCf.js → error-E3T5CS3m.js} +13 -12
- package/dist/error-E3T5CS3m.js.map +1 -0
- package/dist/{index-DfIvkht8.d.ts → index-BC9hY7dI.d.ts} +5 -5
- package/dist/{index-CqsS207F.d.ts → index-D-Dfv65w.d.ts} +3 -2
- package/dist/index.d.ts +4 -4
- package/dist/index.js +5 -5
- package/dist/preview/index.d.ts +1 -1
- package/dist/preview/index.js +1 -1
- package/dist/{preview-8ejC5VP2.js → preview-DFb1pOqJ.js} +30 -11
- package/dist/preview-DFb1pOqJ.js.map +1 -0
- package/dist/provider/index.js +1 -1
- package/dist/{sections-rD8NuRI8.js → sections-CnG7OLr8.js} +3 -3
- package/dist/{sections-rD8NuRI8.js.map → sections-CnG7OLr8.js.map} +1 -1
- package/dist/states-Ci1AvoQ9.js.map +1 -1
- package/dist/style.css +74 -74
- package/dist/ui-kit/index.js +2 -2
- package/dist/ui-kit/style.css +22 -6
- package/dist/{update-DyFnX8yE.js → update-DnX2ibYn.js} +162 -43
- package/dist/update-DnX2ibYn.js.map +1 -0
- package/dist/{use-dynamic-tailwind-0nuRN57N.js → use-dynamic-tailwind-CjXeHjsr.js} +107 -25
- package/dist/use-dynamic-tailwind-CjXeHjsr.js.map +1 -0
- package/dist/{use-field-state-iQGW0vc6.js → use-field-state-B1VLhWfr.js} +2 -2
- package/dist/{use-field-state-iQGW0vc6.js.map → use-field-state-B1VLhWfr.js.map} +1 -1
- package/dist/utils/ast/index.js +1 -86
- package/dist/utils/index.js +3 -4
- package/package.json +2 -3
- package/dist/boundary-CySnDUs2.js.map +0 -1
- package/dist/cn-CAnad2vL.js.map +0 -1
- package/dist/compile-my0AjsCw.js +0 -136
- package/dist/compile-my0AjsCw.js.map +0 -1
- package/dist/constants-st9gnJ5g.js +0 -55
- package/dist/constants-st9gnJ5g.js.map +0 -1
- package/dist/context-Bjq0CvBE.js.map +0 -1
- package/dist/core-BNVuH8-l.js.map +0 -1
- package/dist/detect-typescript-CMF0FVKD.js +0 -10
- package/dist/detect-typescript-CMF0FVKD.js.map +0 -1
- package/dist/dnd-COjjjuD7.js.map +0 -1
- package/dist/document-BkGWDZGc.js.map +0 -1
- package/dist/editor-caches-ClMvdcQF.js +0 -23
- package/dist/editor-caches-ClMvdcQF.js.map +0 -1
- package/dist/error-CFzvnLCf.js.map +0 -1
- package/dist/preview-8ejC5VP2.js.map +0 -1
- package/dist/update-DyFnX8yE.js.map +0 -1
- package/dist/use-dynamic-tailwind-0nuRN57N.js.map +0 -1
- package/dist/utils/ast/index.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-dynamic-tailwind-CjXeHjsr.js","names":["ReactComponent"],"sources":["../src/components/frame/auto-height/measure.ts","../src/components/frame/auto-height/animations.ts","../src/components/frame/viewport-units.ts","../src/components/frame/auto-height/auto-height.ts","../src/components/frame/auto-height/use-auto-height.ts","../src/components/frame/iframe/inject-assets.ts","../src/components/frame/style-sync.ts","../src/components/frame/iframe/use-host-style-sync.ts","../src/components/frame/iframe/use-iframe-document.ts","../src/components/frame/iframe/iframe.tsx","../src/components/frame/shadow.tsx","../src/components/frame/use-is-client.ts","../src/components/frame/frame.tsx","../src/components/preview/stable-component.ts","../src/components/preview/defaults.ts","../src/components/preview/use-compiled-module.ts","../src/components/preview/use-dynamic-tailwind.ts"],"sourcesContent":["// The calculations behind `autoHeight`, kept free of the DOM so they can be\n// unit-tested. The DOM reads stay in `auto-height.ts`, which needs a real\n// browser to test.\n\n// The probe height when there's no `[data-frame-container]` scroll\n// container, as when `Frame` is used without `Live.Dnd`. A common mobile\n// viewport height.\nexport const FALLBACK_PROBE_HEIGHT = 812;\n\n// The vertical space an element takes around its content: margin, border\n// and padding, top and bottom. Margin counts (#440), or a `100vh` section in\n// a wrapper with a margin would overflow the container by that margin. A\n// margin that collapses through a parent is counted twice, which errs toward\n// a smaller probe that fits.\nexport const verticalInsets = (\n style: Pick<\n CSSStyleDeclaration,\n | 'marginTop'\n | 'marginBottom'\n | 'borderTopWidth'\n | 'borderBottomWidth'\n | 'paddingTop'\n | 'paddingBottom'\n >,\n): number =>\n [\n style.marginTop,\n style.marginBottom,\n style.borderTopWidth,\n style.borderBottomWidth,\n style.paddingTop,\n style.paddingBottom,\n ].reduce((sum, value) => sum + (parseFloat(value) || 0), 0);\n\n// The height the preview's `cq*` units resolve against: the scroll\n// container's `clientHeight` minus `wrapperInsets`, the space everything\n// between the iframe and the container takes (`verticalInsets`). `null`\n// while the container isn't laid out yet: skip the pass rather than guess a\n// height that nothing would correct later.\nexport const computeProbeHeight = (\n scrollContainerClientHeight: number,\n wrapperInsets: number,\n): number | null => {\n const usable = scrollContainerClientHeight - wrapperInsets;\n\n return usable > 0 ? usable : null;\n};\n\n// Whether an element is hidden but still takes height: `visibility: hidden`\n// or `opacity: 0` (`display: none` already reads 0), such as a closed bottom\n// sheet. A transparent element that `isAnimating` is a fade-in starting, so\n// it's measured (#374). `visibility: hidden` stays hidden even while\n// animating.\nexport const isVisuallyHidden = (\n computed: {\n visibility: string;\n opacity: string;\n },\n isAnimating = false,\n): boolean =>\n computed.visibility === 'hidden' ||\n (computed.opacity === '0' && !isAnimating);\n\n// The `playState`s that mean the element is still changing. Not just\n// `!== 'idle'`: a finished fade-out with `fill-mode: forwards` stays\n// `finished` at `opacity: 0` and is hidden for good. `paused` counts, since\n// a paused fade-in stopped part-way.\nexport const isAnimationActive = (playState: string): boolean =>\n playState === 'running' || playState === 'paused';\n\n// How far down a fixed or absolute element reaches, from its viewport `top`\n// and `bottom` read while the viewport is `probeHeight` tall: its bottom\n// edge, capped at `probeHeight` so one element placed past the viewport can't\n// stretch the whole preview. `null` for an element entirely outside the\n// viewport, such as a closed drawer moved off-screen, which shows nothing.\nexport const positionedElementBottom = (\n top: number,\n bottom: number,\n probeHeight: number,\n): number | null => {\n if (bottom <= 0 || top >= probeHeight) {\n return null;\n }\n\n return Math.min(bottom, probeHeight);\n};\n","import { isAnimationActive } from './measure';\n\n// `getAnimations()` mixes CSS animations, CSS transitions and script\n// animations (`element.animate()`). Only the first two fire a bubbling event\n// when they end, so they're told apart. Both constructors are checked first,\n// since jsdom has neither.\nexport const isCssAnimation = (\n win: Window & typeof globalThis,\n animation: Animation,\n): boolean =>\n typeof win.CSSAnimation === 'function' &&\n animation instanceof win.CSSAnimation;\n\nexport const isCssTransition = (\n win: Window & typeof globalThis,\n animation: Animation,\n): boolean =>\n typeof win.CSSTransition === 'function' &&\n animation instanceof win.CSSTransition;\n\n// Whether the element is animating, which tells a passing `opacity: 0` (the\n// start of a fade-in, so measure it) from a lasting one (a closed overlay, so\n// skip it). Transitions count too: a measurement pass doesn't always freeze\n// them, so a fade-in can read `opacity: 0` with a transition running. Without\n// `getAnimations()` (jsdom, old browsers) this is false, which under-measures\n// a fading-in overlay rather than counting a closed one.\nexport const hasActiveAnimation = (el: HTMLElement): boolean =>\n typeof el.getAnimations === 'function' &&\n el.getAnimations().some(animation => isAnimationActive(animation.playState));\n\n// An animation that repeats forever never resolves `finished`, so it gets no\n// handler. It's always animating, so `hasActiveAnimation` keeps it measured\n// anyway.\nexport const neverFinishes = (animation: Animation): boolean => {\n const timing = animation.effect?.getComputedTiming();\n\n return timing?.iterations === Infinity || timing?.duration === Infinity;\n};\n","// Rewrites viewport units to container units for `autoHeight` (#132). The\n// iframe measures its content against a fixed-height size container\n// (`html { container-type: size; height: <probe>px }`), but a size container\n// doesn't change what `vh` resolves against, so `vh`, `svh`, `lvh`, `dvh`,\n// `vmin` and `vmax` become `cqh`, `cqmin` and `cqmax`. `vw` and `vi` are left\n// alone: they depend on width, which the measurement doesn't touch.\nconst UNIT_MAP: Record<string, string> = {\n vh: 'cqh',\n svh: 'cqh',\n lvh: 'cqh',\n dvh: 'cqh',\n vmin: 'cqmin',\n vmax: 'cqmax',\n};\n\n// Longest-unit-first so `svh`/`lvh`/`dvh` aren't shadowed by a shorter\n// alternative matching a prefix of them first.\nconst UNITS_PATTERN = Object.keys(UNIT_MAP)\n .sort((a, b) => b.length - a.length)\n .join('|');\n\n// A CSS dimension token: optional sign, then digits with an optional\n// fractional part on either side of the decimal point (`100`, `50.5`,\n// `-10`, `.5` all match; a bare `-` or `.` alone does not).\nconst NUMBER_PATTERN = '-?(?:\\\\d+\\\\.?\\\\d*|\\\\.\\\\d+)';\n\n// The number must not follow a letter, digit, `_`, `.` or `-`, so\n// `url(a5vh.png)`, `.a5vh{}` and `.hero-100vh{}` stay as they are: those\n// digits belong to a name. A real negative value (`margin-top: -10vh`) still\n// matches, since a space comes before its `-`. `--my-vh` never matches:\n// there's no number before `vh`.\nconst VIEWPORT_UNIT_RE = new RegExp(\n `(?<![\\\\w.-])(${NUMBER_PATTERN})(${UNITS_PATTERN})(?![a-zA-Z0-9_-])`,\n 'gi',\n);\n\n// Rewrites `vh`, `svh`, `lvh`, `dvh`, `vmin` and `vmax` to `cqh`, `cqmin` and\n// `cqmax` wherever they appear as a dimension, including inside `calc()` and\n// `var()` fallbacks. `vw` stays (see `UNIT_MAP`). A plain text replacement,\n// so a value in a CSS string such as `content: \"100vh\"` is rewritten too,\n// which doesn't affect layout.\nexport const convertViewportUnits = (css: string): string =>\n css.replace(\n VIEWPORT_UNIT_RE,\n (_match, number: string, unit: string) =>\n number + UNIT_MAP[unit.toLowerCase()],\n );\n\n// Rewrites viewport units in the two places `convertViewportUnits` doesn't\n// see: inline `style` attributes (which panel edits produce) and `<style>`\n// elements the preview renders itself. Without this, their `vh` would\n// resolve against the iframe's own height again (#132). Call it from\n// `updateHeight` after the container style is applied, before reading any\n// height.\n//\n// Idempotent: converted values no longer match the selectors, and a node is\n// written only when its text changes. A write would trigger the\n// MutationObserver in `iframe.tsx` again and loop.\nexport const rewriteInlineViewportUnits = (root: HTMLElement): void => {\n // `vh` as a substring already covers `svh`/`lvh`/`dvh`; `vmin`/`vmax` need\n // their own terms. Case-insensitive so `100VH` inline is caught too.\n const inlineTargets = root.querySelectorAll<HTMLElement>(\n '[style*=\"vh\" i], [style*=\"vmin\" i], [style*=\"vmax\" i]',\n );\n\n inlineTargets.forEach(el => {\n const current = el.getAttribute('style');\n\n if (current == null) {\n return;\n }\n\n const converted = convertViewportUnits(current);\n\n if (converted !== current) {\n el.setAttribute('style', converted);\n }\n });\n\n root.querySelectorAll<HTMLStyleElement>('style').forEach(styleEl => {\n const current = styleEl.textContent;\n\n if (!current) {\n return;\n }\n\n const converted = convertViewportUnits(current);\n\n if (converted !== current) {\n styleEl.textContent = converted;\n }\n });\n};\n","import { rewriteInlineViewportUnits } from '../viewport-units';\nimport { hasActiveAnimation } from './animations';\nimport {\n FALLBACK_PROBE_HEIGHT,\n computeProbeHeight,\n isVisuallyHidden,\n positionedElementBottom,\n verticalInsets,\n} from './measure';\n\n// The style that makes `<html>` a size container, found again by this id.\n// Scoped to `html`, so it only affects how `cq*` units resolve.\nexport const CONTAINER_STYLE_ID = 'autoheight-container';\n\n// A style that is off (`media=\"not all\"`) except while a measurement runs.\n// It guards against two things:\n//\n// - Transitions: a transition on what the measurement changes would animate\n// the new probe height, and the read would catch a value part-way there.\n// Only on a pass that changes the probe height (`freezeTransitions`).\n// - Scrollbars: a new probe height can show or hide a scrollbar for that\n// pass, which narrows the content where scrollbars take space (Windows)\n// and changes the height. Only the scrollbar is hidden; scrolling still\n// works.\n//\n// One element that is never added or removed, so toggling it doesn't trigger\n// the MutationObserver that watches the content.\nconst MEASUREMENT_OVERRIDE_STYLE_ID = 'autoheight-measurement-overrides';\n\nexport const SCROLLBAR_OVERRIDE_RULES = [\n 'html, body { scrollbar-width: none !important; }',\n 'html::-webkit-scrollbar, body::-webkit-scrollbar { display: none !important; }',\n];\n\nconst FREEZE_TRANSITIONS_RULE =\n '*, *::before, *::after { transition: none !important; }';\n\n// Measures with the overrides above on. `freezeTransitions` is off unless\n// the pass changes the probe height: `transition: none` cancels a running\n// transition rather than pausing it, and the element jumps to its end state\n// for good. A pass that leaves the probe height alone changes nothing, so it\n// leaves the preview's transitions alone.\nconst withMeasurementOverrides = (\n doc: Document,\n freezeTransitions: boolean,\n measure: () => void,\n) => {\n let styleEl = doc.getElementById(\n MEASUREMENT_OVERRIDE_STYLE_ID,\n ) as HTMLStyleElement | null;\n\n if (!styleEl) {\n styleEl = doc.createElement('style');\n styleEl.id = MEASUREMENT_OVERRIDE_STYLE_ID;\n styleEl.media = 'not all';\n doc.head?.appendChild(styleEl);\n }\n\n const text = (\n freezeTransitions\n ? [FREEZE_TRANSITIONS_RULE, ...SCROLLBAR_OVERRIDE_RULES]\n : SCROLLBAR_OVERRIDE_RULES\n ).join('\\n');\n\n if (styleEl.textContent !== text) {\n styleEl.textContent = text;\n }\n\n styleEl.media = 'all';\n measure();\n styleEl.media = 'not all';\n};\n\n// Hides the iframe document's scrollbar while `autoHeight` sizes it.\n// Rounding can leave the document a fraction taller than the iframe, which\n// would draw a scrollbar in every section. Unlike the measurement override,\n// this stays on. Scrolling still works if the content ever outgrows it.\nexport const HIDE_SCROLLBAR_STYLE_ID = 'autoheight-hide-scrollbar';\n\n// Makes `<html>` a size container with a fixed height, `probeHeight`, the\n// scroll container's available height. Viewport units are rewritten to `cq*`\n// units (`convertViewportUnits`), so they resolve against this height\n// instead of the iframe's own. Against the iframe's own height, `100vh`\n// content would grow the iframe, which grows the content again, and never\n// settle (#132).\nconst ensureContainerStyle = (doc: Document, probeHeight: number) => {\n let styleEl = doc.getElementById(\n CONTAINER_STYLE_ID,\n ) as HTMLStyleElement | null;\n\n if (!styleEl) {\n styleEl = doc.createElement('style');\n styleEl.id = CONTAINER_STYLE_ID;\n doc.head?.appendChild(styleEl);\n }\n\n const text = `html { container-type: size !important; height: ${probeHeight}px !important; }`;\n\n // Written only when it differs, so a pass with the same probe height\n // changes nothing in the document (see `freezeTransitions`).\n if (styleEl.textContent !== text) {\n styleEl.textContent = text;\n }\n};\n\n// The height the preview's `cq*` units resolve against, read from the\n// iframe's scroll container. `null` while the layout isn't ready.\nexport const getProbeHeight = (\n iframe: HTMLIFrameElement,\n win: Window,\n): number | null => {\n const scrollParent = iframe.closest<HTMLElement>('[data-frame-container]');\n\n if (!scrollParent) {\n // No scroll container (`Frame` used without `Live.Dnd`): use a fixed\n // default. See `FALLBACK_PROBE_HEIGHT`.\n return FALLBACK_PROBE_HEIGHT;\n }\n\n // From the iframe up to the scroll container: the iframe's and each\n // wrapper's margin, border and padding take space, and the scroll container\n // adds only its padding, since `clientHeight` leaves out its border and\n // margin (#440).\n const scrollParentStyle = win.getComputedStyle(scrollParent);\n let wrapperInsets =\n (parseFloat(scrollParentStyle.paddingTop) || 0) +\n (parseFloat(scrollParentStyle.paddingBottom) || 0);\n let node: HTMLElement | null = iframe;\n\n while (node && node !== scrollParent) {\n wrapperInsets += verticalInsets(win.getComputedStyle(node));\n node = node.parentElement;\n }\n\n return computeProbeHeight(scrollParent.clientHeight, wrapperInsets);\n};\n\n// The height of the preview's content with the iframe sized to\n// `probeHeight`: the mount node's `scrollHeight`, or the bottom edge of a\n// fixed or absolute element if that reaches further. Runs inside the\n// measurement overrides, and writes the container style and the rewritten\n// viewport units into the document.\nexport const measureContentHeight = (\n mountNode: HTMLElement,\n doc: Document,\n win: Window,\n probeHeight: number,\n freezeTransitions: boolean,\n): number => {\n let contentHeight = 0;\n\n withMeasurementOverrides(doc, freezeTransitions, () => {\n ensureContainerStyle(doc, probeHeight);\n\n // Rewrite viewport units in inline `style` attributes and in-preview\n // `<style>` tags, which the container style can't reach, so they resolve\n // against the probe height too. After `ensureContainerStyle` and before the\n // reads below. It's idempotent, so the observer pass its own rewrites trigger\n // changes nothing.\n rewriteInlineViewportUnits(mountNode);\n\n // No need to fold the iframe to 0px first: `cq*` content is sized against\n // the fixed probe height, so a direct read is stable (#132).\n contentHeight = mountNode.scrollHeight;\n\n // Every descendant, so an overlay nested deep in the tree counts too.\n const descendants = mountNode.querySelectorAll<HTMLElement>('*');\n\n descendants.forEach(el => {\n const style = win.getComputedStyle(el);\n\n // `visibility: hidden` and `opacity: 0` elements, such as a closed bottom\n // sheet, still have an `offsetHeight`, so they're skipped here\n // (`display: none` already reads 0). Animations are checked only for a fully\n // transparent element, to tell a fade-in apart from a hidden one.\n const isAnimating = style.opacity === '0' && hasActiveAnimation(el);\n\n if (isVisuallyHidden(style, isAnimating)) {\n return;\n }\n\n if (\n (style.position === 'fixed' || style.position === 'absolute') &&\n el.offsetHeight > 0\n ) {\n const rect = el.getBoundingClientRect();\n const bottom = positionedElementBottom(\n rect.top,\n rect.bottom,\n probeHeight,\n );\n\n if (bottom !== null) {\n contentHeight = Math.max(contentHeight, bottom);\n }\n }\n });\n });\n\n return contentHeight;\n};\n","import { useCallback, useEffect, useMemo, useRef } from 'react';\nimport type { RefObject } from 'react';\n\nimport {\n useEventListener,\n useMutationObserver,\n useResizeObserver,\n} from '@jbpark/use-hooks';\n\nimport { isCssAnimation, isCssTransition, neverFinishes } from './animations';\nimport {\n CONTAINER_STYLE_ID,\n HIDE_SCROLLBAR_STYLE_ID,\n SCROLLBAR_OVERRIDE_RULES,\n getProbeHeight,\n measureContentHeight,\n} from './auto-height';\nimport { isAnimationActive } from './measure';\n\n// Sizes the iframe to its content while `shouldAutoHeight` is on, and\n// re-measures when anything that can change the height does. Also owns the\n// document styles that only `autoHeight` needs. Call it after the hooks that\n// set up the document: the effects below rely on running after them.\nexport const useAutoHeight = (\n iframeRef: RefObject<HTMLIFrameElement | null>,\n mountNode: HTMLElement | null,\n shouldAutoHeight: boolean,\n) => {\n // Animations that already have a re-measure handler, so a long one gets only\n // one. Weak, so it never keeps an animation or its element alive.\n const trackedAnimationsRef = useRef(new WeakSet<Animation>());\n // Lets those handlers call the current `updateHeight` without it depending\n // on itself.\n const updateHeightRef = useRef<(() => void) | null>(null);\n // The probe height the container style was last built for. A pass that\n // leaves it unchanged changes nothing, so it can measure without freezing\n // transitions.\n const lastProbeHeightRef = useRef<number | undefined>(undefined);\n // Script animations fire no event when they end, so each gets one\n // re-measure when its `finished` promise settles. CSS animations and\n // transitions are skipped: their end events already bubble to the mount node.\n // A cancelled animation rejects `finished` and snaps the element back, which\n // also changes the height, so both outcomes re-measure.\n const trackScriptAnimations = useCallback(\n (doc: Document, win: Window & typeof globalThis) => {\n if (typeof doc.getAnimations !== 'function') {\n return;\n }\n\n doc.getAnimations().forEach(animation => {\n if (\n trackedAnimationsRef.current.has(animation) ||\n !isAnimationActive(animation.playState) ||\n isCssAnimation(win, animation) ||\n isCssTransition(win, animation) ||\n neverFinishes(animation)\n ) {\n return;\n }\n\n trackedAnimationsRef.current.add(animation);\n\n void animation.finished\n .catch(() => undefined)\n .then(() => updateHeightRef.current?.());\n });\n },\n [],\n );\n\n // There is no \"skip when `scrollHeight` and the probe height are unchanged\"\n // check, though setting the iframe's height can trigger the ResizeObserver\n // again. Opening or closing a fixed or absolute overlay often leaves\n // `scrollHeight` unchanged, so such a check would skip exactly the updates\n // the full-subtree walk is for.\n const updateHeight = useCallback(() => {\n if (!shouldAutoHeight || !mountNode || !iframeRef.current) {\n return;\n }\n\n const iframe = iframeRef.current;\n const doc = iframe.contentDocument;\n const win = doc?.defaultView;\n\n if (!doc || !win) {\n return;\n }\n\n const probeHeight = getProbeHeight(iframe, win);\n\n if (probeHeight === null) {\n // Layout isn't ready yet: skip this pass. The observers call this again\n // once it settles.\n return;\n }\n\n // Only the first pass and a pass that moves the probe height change the\n // container, so only they freeze transitions.\n const freezeTransitions = probeHeight !== lastProbeHeightRef.current;\n\n lastProbeHeightRef.current = probeHeight;\n\n // Sizes the iframe's viewport to the probe height for the reads below, so\n // a fixed element's percentage `height` or `top` resolves against what the\n // section shows, not against the iframe's current height, which is itself\n // what is being measured (#565). The final height replaces it before the\n // next paint.\n const previousHeight = iframe.style.height;\n\n iframe.style.height = `${probeHeight}px`;\n\n const contentHeight = measureContentHeight(\n mountNode,\n doc,\n win,\n probeHeight,\n freezeTransitions,\n );\n\n iframe.style.height =\n contentHeight > 0 ? `${Math.ceil(contentHeight)}px` : previousHeight;\n\n // After the measurement window, which can cancel transitions, so this sees\n // the document's real animations.\n trackScriptAnimations(doc, win);\n }, [iframeRef, shouldAutoHeight, mountNode, trackScriptAnimations]);\n\n useEffect(() => {\n updateHeightRef.current = updateHeight;\n }, [updateHeight]);\n\n // Removes the container style when `autoHeight` turns off or a height is\n // given, so `cq*` content goes back to sizing against the viewport.\n useEffect(() => {\n if (shouldAutoHeight) {\n return;\n }\n\n iframeRef.current?.contentDocument\n ?.getElementById(CONTAINER_STYLE_ID)\n ?.remove();\n }, [iframeRef, shouldAutoHeight]);\n\n // A fixed-height iframe scrolls its own document. A copied host\n // `body { overflow: hidden }` would stop that, so an inline value overrides\n // it. With `autoHeight` there's nothing to scroll, so the body is left\n // alone.\n useEffect(() => {\n const body = iframeRef.current?.contentDocument?.body;\n\n if (!body || !mountNode) {\n return;\n }\n\n if (shouldAutoHeight) {\n body.style.removeProperty('overflow-y');\n } else {\n body.style.setProperty('overflow-y', 'auto');\n }\n }, [iframeRef, shouldAutoHeight, mountNode]);\n\n // Depends on `mountNode` so it runs again once the iframe's document\n // exists.\n useEffect(() => {\n const doc = iframeRef.current?.contentDocument;\n\n if (!doc?.head) {\n return;\n }\n\n const existing = doc.getElementById(HIDE_SCROLLBAR_STYLE_ID);\n\n if (!shouldAutoHeight) {\n existing?.remove();\n return;\n }\n\n if (existing) {\n return;\n }\n\n const styleEl = doc.createElement('style');\n styleEl.id = HIDE_SCROLLBAR_STYLE_ID;\n styleEl.textContent = SCROLLBAR_OVERRIDE_RULES.join('\\n');\n doc.head.appendChild(styleEl);\n }, [iframeRef, shouldAutoHeight, mountNode]);\n\n useEffect(() => {\n updateHeight();\n }, [updateHeight]);\n\n const [resizeRef, resizeSize] = useResizeObserver<HTMLElement>();\n\n // Attached by hand, since `mountNode` is created by the portal, not\n // rendered here. Only used as a trigger: `updateHeight`'s own walk measures\n // more accurately than the mount node's size.\n useEffect(() => {\n if (!shouldAutoHeight || !mountNode) {\n return;\n }\n\n resizeRef(mountNode);\n return () => resizeRef(null);\n }, [shouldAutoHeight, mountNode, resizeRef]);\n\n // Runs on each size the resize observer reports. `updateHeight` stays out\n // of the deps: the effect above already runs it whenever it changes.\n useEffect(() => {\n updateHeight();\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [resizeSize]);\n\n useMutationObserver(mountNode, updateHeight, {\n enabled: shouldAutoHeight,\n childList: true,\n subtree: true,\n attributes: true,\n characterData: true,\n });\n\n // Re-measure when an animation or transition ends or is cancelled, which\n // neither observer sees. A fixed or absolute element read part-way through a\n // fade would otherwise keep its height until something else changed (#374).\n // All four events bubble, so one listener each on the mount node covers\n // every descendant.\n //\n // The mount node is wrapped in a ref that changes only with the node, and\n // `enabled` waits until it exists.\n const mountNodeRef = useMemo(() => ({ current: mountNode }), [mountNode]);\n const settleListenerEnabled = shouldAutoHeight && mountNode !== null;\n\n useEventListener('animationend', updateHeight, {\n target: mountNodeRef,\n enabled: settleListenerEnabled,\n });\n\n useEventListener('animationcancel', updateHeight, {\n target: mountNodeRef,\n enabled: settleListenerEnabled,\n });\n\n useEventListener('transitionend', updateHeight, {\n target: mountNodeRef,\n enabled: settleListenerEnabled,\n });\n\n useEventListener('transitioncancel', updateHeight, {\n target: mountNodeRef,\n enabled: settleListenerEnabled,\n });\n};\n","import { convertViewportUnits } from '../viewport-units';\n\n// How many `<style>` and `<link>` elements the last sync left in the\n// document, so a shorter list can remove the extras.\nexport interface InjectedCounts {\n styles: number;\n stylesheets: number;\n}\n\n// Gives each value its own element in the head, found again by\n// `${idPrefix}${index}`, and removes the elements a shorter list leaves\n// behind.\nconst syncInjectedElements = <T extends HTMLElement>(\n doc: Document,\n idPrefix: string,\n values: string[],\n previousCount: number,\n create: () => T,\n update: (el: T, value: string) => void,\n) => {\n values.forEach((value, index) => {\n const id = `${idPrefix}${index}`;\n let el = doc.getElementById(id) as T | null;\n\n if (!el) {\n el = create();\n el.id = id;\n doc.head.appendChild(el);\n }\n\n update(el, value);\n });\n\n for (let index = values.length; index < previousCount; index++) {\n doc.getElementById(`${idPrefix}${index}`)?.remove();\n }\n};\n\n// Writes `styles` (CSS text) and `stylesheets` (URLs) into the document's\n// head and returns the counts for the next call. Writes only what differs.\nexport const syncInjectedAssets = (\n doc: Document,\n styles: string[],\n stylesheets: string[],\n previous: InjectedCounts,\n): InjectedCounts => {\n syncInjectedElements(\n doc,\n 'injected-style-',\n styles,\n previous.styles,\n () => doc.createElement('style'),\n (styleEl, css) => {\n // The main source of `vh` units in a preview: compiled component CSS,\n // such as Tailwind's `h-screen`. See `ensureContainerStyle` in\n // `auto-height.ts` for why they're converted.\n const convertedCss = convertViewportUnits(css);\n\n if (styleEl.textContent !== convertedCss) {\n styleEl.textContent = convertedCss;\n }\n },\n );\n\n syncInjectedElements(\n doc,\n 'injected-stylesheet-',\n stylesheets,\n previous.stylesheets,\n () => {\n const linkEl = doc.createElement('link');\n\n linkEl.rel = 'stylesheet';\n\n return linkEl;\n },\n (linkEl, href) => {\n if (linkEl.href !== href) {\n linkEl.href = href;\n }\n },\n );\n\n return { styles: styles.length, stylesheets: stylesheets.length };\n};\n","const SYNCED_STYLE_ATTR = 'data-live-editor-synced-style';\n\ntype SourceStyle = HTMLLinkElement | HTMLStyleElement;\ntype SyncedStyle = HTMLLinkElement | HTMLStyleElement;\n\n// A source `<style>`'s text, transformed, so the transform runs again only\n// when that text or the transform changes (#719).\ninterface TransformedStyle {\n text: string;\n transform: (content: string) => string;\n output: string;\n}\n\ninterface StyleSyncManager {\n clones: Map<SourceStyle, SyncedStyle>;\n transformed: WeakMap<SourceStyle, TransformedStyle>;\n}\n\nexport const createStyleSyncManager = (): StyleSyncManager => ({\n clones: new Map(),\n transformed: new WeakMap(),\n});\n\nconst isDocument = (target: Document | ShadowRoot): target is Document =>\n target.nodeType === Node.DOCUMENT_NODE;\n\nconst sourceStyles = (document: Document): SourceStyle[] =>\n Array.from(\n document.head.querySelectorAll<HTMLLinkElement | HTMLStyleElement>(\n 'link[rel=\"stylesheet\"], style',\n ),\n );\n\nconst transformedText = (\n source: SourceStyle,\n manager: StyleSyncManager,\n transformStyle: (content: string) => string,\n): string => {\n const text = source.textContent || '';\n const cached = manager.transformed.get(source);\n\n if (cached && cached.text === text && cached.transform === transformStyle) {\n return cached.output;\n }\n\n const output = transformStyle(text);\n\n manager.transformed.set(source, { text, transform: transformStyle, output });\n\n return output;\n};\n\n// Brings `clone` in line with `source`, writing only what differs. Setting a\n// `<style>`'s text, even to the same value, makes the browser parse it again\n// and build a new stylesheet, and every head change reaches every copy in\n// every frame (#719).\nconst copyAttributes = (\n source: SourceStyle,\n clone: SyncedStyle,\n manager: StyleSyncManager,\n transformStyle: (content: string) => string,\n) => {\n Array.from(clone.attributes).forEach(attribute => {\n if (\n attribute.name !== SYNCED_STYLE_ATTR &&\n !source.hasAttribute(attribute.name)\n ) {\n clone.removeAttribute(attribute.name);\n }\n });\n\n Array.from(source.attributes).forEach(attribute => {\n if (clone.getAttribute(attribute.name) !== attribute.value) {\n clone.setAttribute(attribute.name, attribute.value);\n }\n });\n\n if (source.tagName === 'LINK' && clone.tagName === 'LINK') {\n const disabled = (source as HTMLLinkElement).disabled;\n\n if ((clone as HTMLLinkElement).disabled !== disabled) {\n (clone as HTMLLinkElement).disabled = disabled;\n }\n }\n\n const text =\n source.tagName === 'STYLE'\n ? transformedText(source, manager, transformStyle)\n : source.textContent;\n\n if (clone.textContent !== text) {\n clone.textContent = text;\n }\n};\n\nconst createClone = (\n source: SourceStyle,\n target: Document | ShadowRoot,\n manager: StyleSyncManager,\n transformStyle: (content: string) => string,\n): SyncedStyle => {\n const ownerDocument = isDocument(target) ? target : target.ownerDocument;\n const clone = ownerDocument.createElement(\n source.tagName.toLowerCase(),\n ) as SyncedStyle;\n\n clone.setAttribute(SYNCED_STYLE_ATTR, '');\n copyAttributes(source, clone, manager, transformStyle);\n\n return clone;\n};\n\nconst targetContainer = (target: Document | ShadowRoot) =>\n isDocument(target) ? target.head : target;\n\nconst isSyncedStyle = (node: Node): node is SyncedStyle => {\n const element = node as Element;\n\n return (\n (element.tagName === 'LINK' || element.tagName === 'STYLE') &&\n element.hasAttribute(SYNCED_STYLE_ATTR)\n );\n};\n\nconst removeClone = (clone: SyncedStyle) => {\n clone.remove();\n};\n\n// `anchor`, when given, is an element the synced block has to stay in front\n// of. Without it, a first sync appends the block after whatever the target\n// already holds, so a rule the target added for itself could end up before\n// the host's copies and lose to them on order (#441).\nexport const reconcileStyles = (\n sourceDocument: Document,\n target: Document | ShadowRoot,\n manager: StyleSyncManager,\n enabled: boolean,\n transformStyle: (content: string) => string = content => content,\n anchor: Node | null = null,\n) => {\n if (!enabled) {\n manager.clones.forEach(removeClone);\n manager.clones.clear();\n\n return;\n }\n\n const sources = sourceStyles(sourceDocument);\n const sourceSet = new Set(sources);\n const container = targetContainer(target);\n\n manager.clones.forEach((clone, source) => {\n if (!sourceSet.has(source)) {\n removeClone(clone);\n manager.clones.delete(source);\n }\n });\n\n let previous: SyncedStyle | null = null;\n\n sources.forEach(source => {\n let clone = manager.clones.get(source);\n\n if (!clone) {\n clone = createClone(source, target, manager, transformStyle);\n manager.clones.set(source, clone);\n } else {\n copyAttributes(source, clone, manager, transformStyle);\n }\n\n const nextSibling = previous\n ? previous.nextSibling\n : (Array.from(container.childNodes).find(isSyncedStyle) ??\n (anchor?.parentNode === container ? anchor : null));\n\n if (clone !== nextSibling) {\n container.insertBefore(clone, nextSibling);\n }\n\n previous = clone;\n });\n};\n\n// What a root-attribute sync has written onto the target, so a later pass\n// removes exactly that and never what the target set for itself.\ninterface RootAttributeSync {\n attributes: Set<string>;\n classes: Set<string>;\n}\n\nexport const createRootAttributeSync = (): RootAttributeSync => ({\n attributes: new Set(),\n classes: new Set(),\n});\n\nconst isMirroredAttribute = (name: string) => name.startsWith('data-');\n\n// Mirrors the host `<html>`'s classes and `data-*` attributes onto an iframe's\n// `<html>`. A theme is usually switched there (`.dark`, `data-theme=\"dark\"`),\n// and copying the host's stylesheets alone doesn't help an iframe: its\n// selectors look for those on an ancestor, and the iframe document has a root\n// of its own. A shadow root needs none of this, since it sits under the host's\n// `<html>` already (#497).\n//\n// Classes are synced token by token, so a class the preview put on its own\n// root survives. Other attributes (`style`, `lang`, `dir`, ...) are left\n// alone: the frame relies on its own root for sizing and layout.\nexport const reconcileRootAttributes = (\n source: Element,\n target: Element,\n sync: RootAttributeSync,\n enabled: boolean,\n) => {\n const attributes = enabled\n ? Array.from(source.attributes).filter(({ name }) =>\n isMirroredAttribute(name),\n )\n : [];\n const classes = enabled ? Array.from(source.classList) : [];\n const attributeNames = new Set(attributes.map(({ name }) => name));\n const classNames = new Set(classes);\n\n sync.attributes.forEach(name => {\n if (!attributeNames.has(name)) {\n target.removeAttribute(name);\n }\n });\n attributes.forEach(({ name, value }) => {\n if (target.getAttribute(name) !== value) {\n target.setAttribute(name, value);\n }\n });\n\n sync.classes.forEach(name => {\n if (!classNames.has(name)) {\n target.classList.remove(name);\n }\n });\n classes.forEach(name => target.classList.add(name));\n\n if (!target.classList.length) {\n target.removeAttribute('class');\n }\n\n sync.attributes = attributeNames;\n sync.classes = classNames;\n};\n","import { useCallback, useRef } from 'react';\nimport type { RefObject } from 'react';\n\nimport { useDebouncedCallback, useMutationObserver } from '@jbpark/use-hooks';\n\nimport { CONTAINER_STYLE_ID } from '../auto-height/auto-height';\nimport {\n createRootAttributeSync,\n createStyleSyncManager,\n reconcileRootAttributes,\n reconcileStyles,\n} from '../style-sync';\nimport { convertViewportUnits } from '../viewport-units';\n\n// Copies the host page's styles and root attributes into the iframe while\n// `syncStyle` is on, and again whenever the host changes them. Returns\n// `applyStyle`, which the document setup calls once the iframe's document\n// exists.\nexport const useHostStyleSync = (\n iframeRef: RefObject<HTMLIFrameElement | null>,\n syncStyle: boolean,\n) => {\n const styleManagerRef = useRef(createStyleSyncManager());\n const rootAttributeSyncRef = useRef(createRootAttributeSync());\n\n const applyStyle = useCallback(() => {\n const doc = iframeRef.current?.contentDocument;\n\n if (!doc) {\n return;\n }\n\n // The container style goes after every copied host style. Both use\n // `!important`, so a host `html { height: 100% !important }` copied in after\n // it would win and fold the frame to a pixel (#441).\n reconcileStyles(\n document,\n doc,\n styleManagerRef.current,\n syncStyle,\n convertViewportUnits,\n doc.getElementById(CONTAINER_STYLE_ID),\n );\n // The host's theme class or attribute, which the copied styles select on\n // (#497).\n reconcileRootAttributes(\n document.documentElement,\n doc.documentElement,\n rootAttributeSyncRef.current,\n syncStyle,\n );\n }, [iframeRef, syncStyle]);\n\n // Debounced, since a stylesheet swap fires several head mutations in a\n // row.\n const debouncedApplyStyle = useDebouncedCallback(applyStyle, {\n delay: 50,\n autoInvoke: false,\n });\n\n // Evaluated during render: `document` is missing on the server (#716).\n useMutationObserver(\n typeof document === 'undefined' ? null : document.head,\n debouncedApplyStyle,\n {\n enabled: syncStyle,\n childList: true,\n subtree: true,\n attributes: true,\n },\n );\n\n // A theme switch only changes the host's `<html>` attributes, which the head\n // observer above doesn't see.\n useMutationObserver(\n typeof document === 'undefined' ? null : document.documentElement,\n debouncedApplyStyle,\n {\n enabled: syncStyle,\n attributes: true,\n },\n );\n\n return applyStyle;\n};\n","import { useCallback, useEffect, useRef, useState } from 'react';\nimport type { RefObject } from 'react';\n\nimport { useLatest } from '@jbpark/use-hooks';\n\nimport { withScriptBlobs } from '~/utils/scripts';\n\n// Adds each script to the document's head as a `<script>` that loads from a\n// blob URL. Not awaited: a script that fails to load becomes an unhandled\n// rejection on the host page.\nconst injectScripts = (doc: Document, srcs: string[]) => {\n void withScriptBlobs(srcs, blobUrls => {\n if (!doc.head) {\n return;\n }\n\n const fragment = doc.createDocumentFragment();\n blobUrls.forEach(blobUrl => {\n const script = doc.createElement('script');\n script.src = blobUrl;\n fragment.appendChild(script);\n });\n doc.head.appendChild(fragment);\n });\n};\n\ninterface Options {\n scripts: string[];\n onLoaded?: () => void;\n applyStyle: () => void;\n}\n\n// Prepares the iframe's document each time it loads: creates the mount node,\n// applies the host styles, injects the scripts not yet in it and calls\n// `onLoaded`, once per document. Returns the mount node, `null` until the\n// first load.\n//\n// `onLoaded` and `scripts` are read through refs, so an inline callback or\n// array doesn't set the document up again on every render (#715).\nexport const useIframeDocument = (\n iframeRef: RefObject<HTMLIFrameElement | null>,\n { scripts, onLoaded, applyStyle }: Options,\n) => {\n const onLoadedRef = useLatest(onLoaded);\n const scriptsRef = useLatest(scripts);\n\n const [mountNode, setMountNode] = useState<HTMLElement | null>(null);\n // The script srcs already added to this iframe's document, so a src added\n // to `scripts` later still loads.\n const loadedScriptsRef = useRef<Set<string>>(new Set());\n // The document those scripts went into. Moving the iframe, as reordering\n // canvas sections does, reloads it with a new document that has none\n // (#507).\n const loadedScriptsDocRef = useRef<Document | null>(null);\n // The document `onLoaded` was last called for.\n const loadedDocRef = useRef<Document | null>(null);\n\n const injectPendingScripts = useCallback(\n (doc: Document) => {\n const pendingScripts = scriptsRef.current.filter(\n src => !loadedScriptsRef.current.has(src),\n );\n\n if (pendingScripts.length) {\n pendingScripts.forEach(src => loadedScriptsRef.current.add(src));\n injectScripts(doc, pendingScripts);\n }\n },\n [scriptsRef],\n );\n\n useEffect(() => {\n const $iframe = iframeRef.current;\n\n if (!$iframe) {\n return;\n }\n\n const onLoad = () => {\n const doc = $iframe.contentDocument;\n\n if (!doc) {\n return;\n }\n\n if (loadedScriptsDocRef.current !== doc) {\n loadedScriptsDocRef.current = doc;\n loadedScriptsRef.current = new Set();\n }\n\n doc.body.style.overflowX = 'hidden';\n doc.body.style.margin = '0';\n\n let node = doc.getElementById('iframe-root');\n\n if (!node) {\n node = doc.createElement('div');\n node.id = 'iframe-root';\n doc.body.appendChild(node);\n }\n\n setMountNode(node);\n\n applyStyle();\n injectPendingScripts(doc);\n\n // The effect runs again when `applyStyle` changes; that re-applies the\n // styles but isn't a new load.\n if (loadedDocRef.current !== doc) {\n loadedDocRef.current = doc;\n onLoadedRef.current?.();\n }\n };\n\n $iframe.addEventListener('load', onLoad);\n\n if ($iframe.contentDocument?.readyState === 'complete') {\n onLoad();\n }\n\n return () => {\n $iframe.removeEventListener('load', onLoad);\n };\n }, [iframeRef, applyStyle, injectPendingScripts, onLoadedRef]);\n\n // A src added to `scripts` after the document loaded. Only the ones not in\n // it yet are injected, so an inline array with the same entries does\n // nothing.\n useEffect(() => {\n const doc = loadedScriptsDocRef.current;\n\n if (doc && doc === iframeRef.current?.contentDocument) {\n injectPendingScripts(doc);\n }\n }, [iframeRef, scripts, injectPendingScripts]);\n\n return mountNode;\n};\n","import { useEffect, useRef } from 'react';\nimport type { ReactNode } from 'react';\nimport { createPortal } from 'react-dom';\n\nimport { useAutoHeight } from '../auto-height/use-auto-height';\nimport { type InjectedCounts, syncInjectedAssets } from './inject-assets';\nimport { useHostStyleSync } from './use-host-style-sync';\nimport { useIframeDocument } from './use-iframe-document';\n\nexport interface Props {\n title?: string;\n /** Forwarded to the iframe's `sandbox` attribute for DOM/CSS isolation only — not a security boundary, since preview code executes in the host window's realm (see `compileModule` in `~/utils`). */\n sandbox?: string;\n style?: React.CSSProperties;\n scripts?: string[];\n styles?: string[];\n stylesheets?: string[];\n autoHeight?: boolean;\n syncStyle?: boolean;\n children: (container: HTMLElement) => ReactNode;\n onLoaded?: () => void;\n}\n\nconst EMPTY_STRING_ARRAY: string[] = [];\n\nconst IFrame = ({\n title = 'Live Preview',\n sandbox,\n style = {},\n scripts = EMPTY_STRING_ARRAY,\n styles = EMPTY_STRING_ARRAY,\n stylesheets = EMPTY_STRING_ARRAY,\n autoHeight = false,\n syncStyle = false,\n children,\n onLoaded,\n ...props\n}: Props) => {\n const iframeRef = useRef<HTMLIFrameElement>(null);\n const shouldAutoHeight = autoHeight && style.height == null;\n\n const applyStyle = useHostStyleSync(iframeRef, syncStyle);\n const mountNode = useIframeDocument(iframeRef, {\n scripts,\n onLoaded,\n applyStyle,\n });\n const injectedCountsRef = useRef<InjectedCounts>({\n styles: 0,\n stylesheets: 0,\n });\n\n useEffect(() => {\n const doc = iframeRef.current?.contentDocument;\n\n if (!doc?.head) {\n return;\n }\n\n injectedCountsRef.current = syncInjectedAssets(\n doc,\n styles,\n stylesheets,\n injectedCountsRef.current,\n );\n }, [styles, stylesheets]);\n\n useAutoHeight(iframeRef, mountNode, shouldAutoHeight);\n\n const content = mountNode\n ? createPortal(children(mountNode), mountNode)\n : null;\n\n return (\n <iframe\n ref={iframeRef}\n style={{\n // An iframe is inline by default and leaves a gap below it for text\n // descenders, which added up across canvas sections (#439).\n display: 'block',\n width: '100%',\n height: '100%',\n border: 'none',\n ...style,\n }}\n title={title}\n sandbox={sandbox}\n {...props}\n >\n {content}\n </iframe>\n );\n};\n\nexport default IFrame;\n","import { useCallback, useLayoutEffect, useRef, useState } from 'react';\nimport type { ReactNode } from 'react';\nimport { createPortal } from 'react-dom';\n\nimport { useDebouncedCallback, useMutationObserver } from '@jbpark/use-hooks';\n\nimport { createStyleSyncManager, reconcileStyles } from './style-sync';\n\ninterface Props {\n // Copies the host page's stylesheets and `<style>` tags into the shadow\n // root, like `iframe.tsx`'s option of the same name. This brings the host's\n // compiled CSS, its own utilities and theme included. `dynamicTailwind`\n // covers what the host's build never saw, such as a class typed at runtime.\n syncStyle?: boolean;\n // Receives the overlay layer, the `container` to portal a modal or drawer\n // into so it opens inside the preview.\n children: (overlayLayer: HTMLElement | null) => ReactNode;\n}\n\nconst OVERLAY_LAYER_ATTR = 'data-live-editor-overlay-layer';\n\n// The layer lets clicks through to the preview under it. What is portaled\n// into it takes them again.\nconst OVERLAY_LAYER_STYLE = `[${OVERLAY_LAYER_ATTR}] > * { pointer-events: auto; }`;\n\n// The render target and the overlay layer in one grid cell, so the layer\n// covers exactly the preview's box. `min-height: 100%` stretches that box to\n// a parent with a set height, such as `Live.Preview`'s, so an overlay isn't\n// squeezed into short content. The wrapper isn't positioned, so it doesn't\n// change what an `absolute` element in the preview is placed against.\n// `isolation` keeps the layer's stacking inside the preview.\nconst createFrameNodes = () => {\n const wrapper = document.createElement('div');\n\n wrapper.style.display = 'grid';\n wrapper.style.gridTemplateColumns = 'minmax(0, 1fr)';\n wrapper.style.minHeight = '100%';\n wrapper.style.isolation = 'isolate';\n\n const renderTarget = document.createElement('div');\n\n renderTarget.style.gridArea = '1 / 1';\n\n // `contain: layout` makes the layer the containing block of the fixed\n // elements inside it, so an overlay covers the preview and not the page.\n const overlayLayer = document.createElement('div');\n\n overlayLayer.setAttribute(OVERLAY_LAYER_ATTR, '');\n overlayLayer.style.gridArea = '1 / 1';\n overlayLayer.style.zIndex = '1';\n overlayLayer.style.contain = 'layout';\n overlayLayer.style.pointerEvents = 'none';\n\n const style = document.createElement('style');\n\n style.textContent = OVERLAY_LAYER_STYLE;\n wrapper.append(style, renderTarget, overlayLayer);\n\n return { wrapper, renderTarget };\n};\n\n// The overlay layer `createFrameNodes` put next to `renderTarget`.\nconst overlayLayerOf = (renderTarget: HTMLElement) => {\n const layer = renderTarget.nextElementSibling;\n\n return layer instanceof HTMLElement && layer.hasAttribute(OVERLAY_LAYER_ATTR)\n ? layer\n : null;\n};\n\nconst Shadow = ({ syncStyle = false, children }: Props) => {\n const hostRef = useRef<HTMLDivElement>(null);\n const shadowRootRef = useRef<ShadowRoot | null>(null);\n const renderTargetRef = useRef<HTMLDivElement | null>(null);\n const [renderTarget, setRenderTarget] = useState<HTMLDivElement | null>(null);\n\n // Added next to the portal target, not inside it: React owns that subtree\n // and would remove anything added there.\n const styleManagerRef = useRef(createStyleSyncManager());\n\n const applyStyle = useCallback(() => {\n const shadowRoot = shadowRootRef.current;\n\n if (!shadowRoot) {\n return;\n }\n\n reconcileStyles(document, shadowRoot, styleManagerRef.current, syncStyle);\n }, [syncStyle]);\n\n // Debounced, since a stylesheet swap fires several head mutations in a\n // row, as in `iframe.tsx`.\n const debouncedApplyStyle = useDebouncedCallback(applyStyle, {\n delay: 50,\n autoInvoke: false,\n });\n\n useMutationObserver(\n typeof document === 'undefined' ? null : document.head,\n debouncedApplyStyle,\n {\n enabled: syncStyle,\n childList: true,\n subtree: true,\n attributes: true,\n },\n );\n\n useLayoutEffect(() => {\n if (!hostRef.current) {\n return;\n }\n\n let shadowRoot = shadowRootRef.current;\n\n if (!shadowRoot) {\n shadowRoot =\n hostRef.current.shadowRoot ||\n hostRef.current.attachShadow({ mode: 'open' });\n shadowRootRef.current = shadowRoot;\n }\n\n let target = renderTargetRef.current;\n\n if (!target) {\n const nodes = createFrameNodes();\n\n shadowRoot.appendChild(nodes.wrapper);\n target = nodes.renderTarget;\n renderTargetRef.current = target;\n setRenderTarget(target);\n }\n\n applyStyle();\n // `click`, `pointerdown` and `pointerup` already cross the shadow boundary\n // (`composed: true`), so they aren't dispatched again here, which made\n // listeners outside see them twice (#92). `event.composedPath()[0]` gives\n // the element inside.\n }, [applyStyle]);\n\n useLayoutEffect(() => {\n if (renderTargetRef.current && !renderTarget) {\n setRenderTarget(renderTargetRef.current);\n }\n }, [renderTarget]);\n\n return (\n <div ref={hostRef} style={{ display: 'contents' }}>\n {renderTarget &&\n createPortal(children(overlayLayerOf(renderTarget)), renderTarget)}\n </div>\n );\n};\n\nexport default Shadow;\n","import { useSyncExternalStore } from 'react';\n\nconst subscribe = () => () => {};\n\n// `false` on the server and while hydrating, `true` after. The first client\n// render then matches the server's, so hydration finds no mismatch (#716).\nexport const useIsClient = () =>\n useSyncExternalStore(\n subscribe,\n () => true,\n () => false,\n );\n","import { type ReactNode } from 'react';\n\nimport IFrame, { type Props as IframeProps } from './iframe/iframe';\nimport Shadow from './shadow';\nimport { useIsClient } from './use-is-client';\n\nexport interface FrameProps extends Omit<IframeProps, 'children'> {\n mode?: 'iframe' | 'shadow';\n}\n\ninterface Props extends FrameProps {\n children: (container: HTMLElement) => ReactNode;\n}\n\nconst Frame = ({ mode, children, ...restProps }: Props) => {\n const isClient = useIsClient();\n\n // Every mode renders into a node of the document, which the server doesn't\n // have. The preview appears once the page is on the client (#716).\n if (!isClient) {\n return null;\n }\n\n if (!mode) {\n return children(document.body);\n }\n\n if (mode === 'shadow') {\n return (\n <Shadow syncStyle={restProps.syncStyle}>\n {container => children(container || document.body)}\n </Shadow>\n );\n }\n\n return <IFrame {...restProps}>{children}</IFrame>;\n};\n\nexport default Frame;\n","import { Component as ReactComponent, createElement } from 'react';\n\ntype Props = Record<string, unknown>;\ntype Component = React.ComponentType<Props>;\n\n// Every hook call in `code`, in source order: `useState(`, `React.useMemo(`,\n// `use(`. Two compiles of a component with the same list call the same\n// hooks in the same order, which is what React needs to keep its state.\nconst HOOK_CALL = /\\buse(?:[A-Z0-9_]\\w*)?\\s*\\(/g;\n\nexport const hookSignature = (code: string): string =>\n (code.match(HOOK_CALL) ?? [])\n .map(call => call.replace(/\\s*\\($/, ''))\n .join(',');\n\n// A plain function component, which can be called with its props. Class\n// components and the objects `memo`, `forwardRef` and `lazy` return can't.\nconst isFunctionComponent = (\n component: Component,\n): component is React.FunctionComponent<Props> =>\n typeof component === 'function' &&\n !(component.prototype as { isReactComponent?: unknown } | undefined)\n ?.isReactComponent;\n\ninterface StableProps {\n render: React.FunctionComponent<Props>;\n props: Props;\n}\n\n// One component type for every compile, which calls the compiled function\n// itself, so the function's output belongs to this type.\nconst StableComponent = ({ render, props }: StableProps) => render(props);\n\ninterface RemountState {\n error: unknown;\n // Bumped to remount `StableComponent` with fresh state.\n attempt: number;\n}\n\n// Remounts `StableComponent` once when a new compile throws on its first\n// render, then lets the error through. `hookSignature` reads hook calls from\n// the whole document, so moving a hook from one component to another keeps\n// the signature while the root component's hook count changes, and React\n// throws (#724). A remount is all it needs. An error from a compile that has\n// rendered before isn't retried: a remount would hide it and reset the state\n// that caused it.\nclass RemountOnRecompileError extends ReactComponent<\n StableProps,\n RemountState\n> {\n state: RemountState = { error: null, attempt: 0 };\n\n // The last compile that rendered without an error.\n private committedRender: StableProps['render'] | null = null;\n // The compile already retried, so a second error from it goes through.\n private retriedRender: StableProps['render'] | null = null;\n\n static getDerivedStateFromError(error: unknown): Partial<RemountState> {\n return { error };\n }\n\n private canRetry() {\n const { render } = this.props;\n\n return (\n this.committedRender !== null &&\n this.committedRender !== render &&\n this.retriedRender !== render\n );\n }\n\n componentDidCatch() {\n if (this.canRetry()) {\n this.retriedRender = this.props.render;\n this.setState(({ attempt }) => ({ error: null, attempt: attempt + 1 }));\n }\n }\n\n componentDidMount() {\n this.committedRender = this.props.render;\n }\n\n componentDidUpdate() {\n if (this.state.error === null) {\n this.committedRender = this.props.render;\n }\n }\n\n render() {\n const { error, attempt } = this.state;\n\n if (error !== null) {\n // Thrown from here, the error reaches the boundary outside, which\n // shows it as before.\n if (!this.canRetry()) {\n throw error;\n }\n\n return null;\n }\n\n return createElement(StableComponent, { ...this.props, key: attempt });\n }\n}\n\n// Renders `Component`, compiled from `code`, under a component type that\n// survives a recompile. Each compile returns a new function, and React\n// remounts a subtree whose component type changes, so rendering it directly\n// rebuilds a section's whole DOM on every edit. While the hook calls in\n// `code` stay the same, the type and key here stay the same too, and React\n// updates only what the new output changes. When they change, the key\n// changes and the subtree remounts, so React never sees a different hook\n// order. A class, `memo` or `forwardRef` component renders as itself.\nexport const renderStableComponent = (\n Component: Component,\n code: string,\n props: Props,\n): React.ReactElement =>\n isFunctionComponent(Component)\n ? createElement(RemountOnRecompileError, {\n key: hookSignature(code),\n render: Component,\n props,\n })\n : createElement(Component, props);\n","// Stable defaults for optional object props. A `= {}` default is a new\n// object on every render, which re-runs every memo keyed on it — the merged\n// module map, and so `compile()` — even when the caller passed nothing (#348).\nexport const NO_PROPS: Record<string, unknown> = {};\nexport const NO_MODULES: Record<string, unknown> = {};\n","import { useMemo } from 'react';\n\nimport { useStableValue } from '@jbpark/use-hooks';\n\nimport { useLiveMessages } from '~/components/context/messages';\nimport type { Module } from '~/types';\nimport { compile } from '~/utils/compile';\n\nimport { NO_MODULES } from './defaults';\n\n// Compiles code into a component, for `Live.Preview` and each canvas\n// section, so both handle errors the same way (#246). `null` for empty code,\n// so a caller can tell \"nothing to render\" from a failed compile, which\n// comes back as a module with `error`.\nexport const useCompiledModule = (\n code: string,\n _modules?: Record<string, unknown>,\n): Module | null => {\n // An inline `modules` object from `Live.Preview`'s host is new each\n // render; only a changed entry should recompile (#397).\n const modules = useStableValue(_modules);\n const { unknown } = useLiveMessages().errors;\n\n return useMemo(() => {\n if (!code) {\n return null;\n }\n\n try {\n return compile(code, modules ?? NO_MODULES);\n } catch (e) {\n return {\n exports: {},\n error: e instanceof Error ? e.message : unknown,\n };\n }\n }, [code, modules, unknown]);\n};\n","import { useCallback, useEffect, useState } from 'react';\n\nimport { useAsync } from '@jbpark/use-hooks';\n\n// Compiles the Tailwind classes in the rendered DOM into CSS for a `<style>`\n// tag, for `Live.Preview` and each canvas section. Reading the DOM, not the\n// source, finds the classes imported components add too.\n//\n// The element is held in state through a callback ref: in shadow mode it\n// appears a commit later, and an effect reading a plain ref would miss it.\n// The compiler and theme load on first use, since they're large (#332).\n//\n// `css` keeps the last compiled result while the next one runs, while it's\n// off, and after a failure. A failure is logged and returned as `error`.\nexport const useDynamicTailwind = (code: string, enabled: boolean) => {\n const [element, setElement] = useState<HTMLDivElement | null>(null);\n\n const ref = useCallback((node: HTMLDivElement | null) => {\n setElement(node);\n }, []);\n\n const { value: css = '', error } = useAsync(\n async () => {\n const { generateTailwindCSSFromDOM } = await import('~/utils/tailwind');\n\n return generateTailwindCSSFromDOM(element!);\n },\n [code, element],\n { enabled: enabled && !!code && !!element },\n );\n\n useEffect(() => {\n if (error) {\n console.error('Generating the preview Tailwind CSS failed', error);\n }\n }, [error]);\n\n return { ref, css, error };\n};\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { i as BINDING_PROP } from "./editor-caches-DXYXgWeD.js";
|
|
2
2
|
import { n as validateBindingValue } from "./validate-CZz30Pnb.js";
|
|
3
3
|
import { i as useLiveMessages } from "./messages-DrINcRdG.js";
|
|
4
4
|
import { createContext, useContext, useId, useState } from "react";
|
|
@@ -128,4 +128,4 @@ const useFieldState = (binding) => {
|
|
|
128
128
|
//#endregion
|
|
129
129
|
export { isStructuralFieldKind as a, getFieldKind as i, FieldIdsContext as n, FieldError as o, LABELABLE_KINDS as r, fields_module_default as s, useFieldState as t };
|
|
130
130
|
|
|
131
|
-
//# sourceMappingURL=use-field-state-
|
|
131
|
+
//# sourceMappingURL=use-field-state-B1VLhWfr.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-field-state-iQGW0vc6.js","names":["styles"],"sources":["../src/components/dnd/panel/fields/fields.module.css","../src/components/dnd/panel/fields/field-error.tsx","../src/components/dnd/panel/fields/field-kind.ts","../src/components/dnd/panel/fields/field-ids.ts","../src/components/dnd/panel/fields/use-field-state.ts"],"sourcesContent":["@layer components {\n /* The look shared by the built-in native inputs, selects and text areas.\n `appearance` is set because a host reset may turn it off. */\n .control {\n appearance: auto;\n display: block;\n box-sizing: border-box;\n width: 100%;\n margin: 0;\n padding: 0.25rem 0.5rem;\n border: 1px solid var(--live-editor-color-border);\n border-radius: var(--live-editor-radius);\n background: var(--live-editor-color-surface);\n color: var(--live-editor-color-text);\n font: inherit;\n font-size: var(--live-editor-font-size-sm);\n\n &:focus {\n border-color: var(--live-editor-color-focus);\n outline: none;\n }\n\n &[aria-invalid='true'] {\n border-color: var(--live-editor-color-danger);\n }\n }\n\n .mono {\n font-family: var(--live-editor-font-mono);\n font-size: var(--live-editor-font-size-xs);\n }\n\n /* `appearance` is set because a host reset may turn it off. */\n .checkbox {\n appearance: auto;\n width: 1rem;\n height: 1rem;\n margin: 0;\n accent-color: var(--live-editor-color-primary);\n }\n\n /* `margin-bottom: 0` keeps the fields below from jumping when the error\n appears: a host page's `<p>` margin would push them (#409). */\n .error {\n margin: 0.25rem 0 0;\n font-size: var(--live-editor-font-size-xs);\n color: var(--live-editor-color-danger);\n }\n\n .label {\n display: block;\n font-size: var(--live-editor-font-size-xs);\n font-weight: 500;\n color: var(--live-editor-color-text-muted);\n }\n\n .stack {\n display: flex;\n flex-direction: column;\n gap: 0.25rem;\n }\n\n .object {\n display: flex;\n flex-direction: column;\n gap: 0.5rem;\n padding: 0.5rem;\n border: 1px solid var(--live-editor-color-border-subtle);\n border-radius: var(--live-editor-radius);\n background: var(--live-editor-color-surface-muted);\n }\n\n .readonly {\n display: flex;\n flex-direction: column;\n gap: 0.25rem;\n padding: 0.75rem;\n border: 1px dashed var(--live-editor-color-warning-border);\n border-radius: var(--live-editor-radius);\n font-size: var(--live-editor-font-size-xs);\n color: var(--live-editor-color-warning-text);\n }\n\n .readonlyCode {\n display: block;\n overflow-x: auto;\n font-family: var(--live-editor-font-mono);\n }\n\n .color {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n }\n\n .colorSwatch {\n flex-shrink: 0;\n width: 3rem;\n height: 2rem;\n padding: 0.125rem;\n }\n\n .colorSource {\n font-family: var(--live-editor-font-mono);\n font-size: var(--live-editor-font-size-xs);\n color: var(--live-editor-color-text-muted);\n }\n}\n","import styles from './fields.module.css';\n\n// `role=\"alert\"` announces the message when it appears; the control points\n// at it with `aria-describedby` so it's read again on focus (#637).\nconst FieldError = ({ id, message }: { id: string; message: string | null }) =>\n message ? (\n <p id={id} role=\"alert\" className={styles.error}>\n {message}\n </p>\n ) : null;\n\nexport default FieldError;\n","import { BINDING_PROP } from '~/constants';\n\nimport type { PanelBinding } from '../../panel-binding';\n\n// Which built-in control `Field` renders for a binding. Exported so a\n// custom panel can ask the same question, most often whether a binding\n// needs a structural editor.\nexport type FieldKind =\n // `canEditValue: false` — shown as preserved source, not editable.\n | 'readonly'\n // An array literal (`items`/`data` or `type: 'array'`) — the Items editor,\n // or `useDndItems` for your own markup.\n | 'items'\n // Element children given as extracted nodes — the Children editor, or\n // `useDndChildren` for your own markup.\n | 'children'\n | 'richtext'\n | 'jsx'\n | 'html'\n // A plain object value — one nested field per key.\n | 'object'\n | 'boolean'\n | 'select'\n | 'color'\n | 'date'\n | 'time'\n | 'datetime'\n | 'url'\n | 'email'\n | 'tel'\n | 'number'\n | 'text';\n\nexport type FieldKindBinding = Pick<\n PanelBinding,\n 'property' | 'type' | 'value' | 'options' | 'canEditValue'\n>;\n\nconst isColorProperty = (property: string): boolean =>\n property.toLowerCase().includes('color');\n\n// The first match wins. Describes the built-in control only: `renderField`\n// runs first and can replace it.\nexport const getFieldKind = (binding: FieldKindBinding): FieldKind => {\n const { property, type, value } = binding;\n\n if (binding.canEditValue === false) {\n return 'readonly';\n }\n\n if (property === 'items' || property === 'data' || type === 'array') {\n return 'items';\n }\n\n if (type === 'richtext') {\n return 'richtext';\n }\n\n if (property === BINDING_PROP.INNER_HTML) {\n return 'html';\n }\n\n if (type === 'jsx') {\n return 'jsx';\n }\n\n if (property === 'children' && Array.isArray(value)) {\n return 'children';\n }\n\n if (typeof value === 'object' && value !== null && !Array.isArray(value)) {\n return 'object';\n }\n\n if (type === 'boolean' || typeof value === 'boolean') {\n return 'boolean';\n }\n\n if (binding.options && Array.isArray(binding.options)) {\n return 'select';\n }\n\n if (type === 'color' || isColorProperty(property)) {\n return 'color';\n }\n\n if (type === 'date') {\n return 'date';\n }\n\n if (type === 'time') {\n return 'time';\n }\n\n if (type === 'datetime') {\n return 'datetime';\n }\n\n if (type === 'url') {\n return 'url';\n }\n\n if (type === 'email') {\n return 'email';\n }\n\n if (type === 'tel') {\n return 'tel';\n }\n\n if (type === 'number' || typeof value === 'number') {\n return 'number';\n }\n\n return 'text';\n};\n\n// True for the two kinds whose value holds further data-bound elements, so a\n// flat input can't edit them: route these to `Field` or to the matching hook.\nexport const isStructuralFieldKind = (\n kind: FieldKind,\n): kind is 'items' | 'children' => kind === 'items' || kind === 'children';\n","import { createContext, useContext, useId } from 'react';\n\nimport type { FieldKind } from './field-kind';\n\n// The ids a `LabeledField` gives the field inside it: the control's, which\n// the label's `htmlFor` points at, and the label's own, for a control that\n// can't take an id and points back with `aria-labelledby` instead, such as\n// a `renderField` control (#637).\nexport const FieldIdsContext = createContext<{\n controlId: string;\n labelId: string;\n} | null>(null);\n\n// The ids a control carries: its own, for the label's `htmlFor`, its\n// label's, for `aria-labelledby`, and its error message's, for\n// `aria-describedby`. `isLabelled` is false outside a `LabeledField`, such\n// as in a custom panel's own markup, where the control names itself with\n// `aria-label` instead.\nexport const useFieldIds = () => {\n const labelledIds = useContext(FieldIdsContext);\n\n const fallbackId = useId();\n\n const controlId = labelledIds?.controlId ?? fallbackId;\n\n return {\n controlId,\n labelId: labelledIds?.labelId,\n errorId: `${controlId}-error`,\n isLabelled: labelledIds !== null,\n };\n};\n\n// The built-in controls that take the label's `htmlFor`: every native\n// input, select and text area. The structural and source editors have no\n// single control to point at.\nexport const LABELABLE_KINDS: ReadonlySet<FieldKind> = new Set([\n 'text',\n 'number',\n 'url',\n 'email',\n 'tel',\n 'time',\n 'date',\n 'datetime',\n 'boolean',\n 'select',\n 'color',\n 'richtext',\n]);\n","import { useState } from 'react';\n\nimport { useLiveMessages } from '~/components/context/messages';\nimport { validateBindingValue } from '~/utils/ast/validate';\n\nimport type { PanelBinding } from '../../panel-binding';\nimport { useFieldIds } from './field-ids';\n\n// What a field control needs besides the binding: the text being typed,\n// the validation error, the ids and aria props that tie the control to its\n// label and error, and the two ways to commit. Shared by the built-in\n// controls and the ui-kit ones (`@jbpark/live-editor/ui-kit`).\nexport const useFieldState = (binding: PanelBinding) => {\n const messages = useLiveMessages();\n // `value` is the JS value; `rawValue` is the source text, for the text\n // controls and the source editors (#238).\n const { value, rawValue, onChange } = binding;\n\n const [validationError, setValidationError] = useState<string | null>(null);\n const { controlId, labelId, errorId, isLabelled } = useFieldIds();\n\n // What the user is typing, for the inputs that commit on blur (url,\n // number, text area). Reset during render when `rawValue` changes from\n // outside, such as undo, so the old text isn't committed on blur (#284).\n const [text, setText] = useState(rawValue);\n const [prevRawValue, setPrevRawValue] = useState(rawValue);\n\n if (rawValue !== prevRawValue) {\n setPrevRawValue(rawValue);\n setText(rawValue);\n }\n\n // For the controls that take an id: the id the label points at, a name\n // of their own outside a `LabeledField`, and the error once there is one.\n const controlProps = {\n id: controlId,\n 'aria-label': isLabelled ? undefined : binding.label,\n 'aria-invalid': validationError ? true : undefined,\n 'aria-describedby': validationError ? errorId : undefined,\n };\n // For the controls that take no id: the label they point at inside a\n // `LabeledField`, or a name of their own outside one.\n const labelledByProps = {\n 'aria-label': labelId ? undefined : binding.label,\n 'aria-labelledby': labelId,\n };\n\n // Skips a commit that doesn't change anything, comparing a string with\n // `rawValue` and anything else with `value`.\n const commitIfChanged = (next: unknown) => {\n const current = typeof next === 'string' ? rawValue : value;\n\n if (next !== current) {\n onChange(next);\n }\n };\n\n // Commits `next` if it passes the binding's validation, else shows why\n // not.\n const validateAndCommit = (next: unknown) => {\n const result = validateBindingValue(binding, next, {\n messages: messages.validation,\n });\n\n if (!result.valid) {\n setValidationError(result.message ?? messages.panel.invalidValue);\n\n return;\n }\n\n setValidationError(null);\n commitIfChanged(next);\n };\n\n return {\n text,\n setText,\n validationError,\n errorId,\n controlProps,\n labelledByProps,\n commitIfChanged,\n validateAndCommit,\n };\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"use-field-state-B1VLhWfr.js","names":["styles"],"sources":["../src/components/dnd/panel/fields/fields.module.css","../src/components/dnd/panel/fields/field-error.tsx","../src/components/dnd/panel/fields/field-kind.ts","../src/components/dnd/panel/fields/field-ids.ts","../src/components/dnd/panel/fields/use-field-state.ts"],"sourcesContent":["@layer components {\n /* The look shared by the built-in native inputs, selects and text areas.\n `appearance` is set because a host reset may turn it off. */\n .control {\n appearance: auto;\n display: block;\n box-sizing: border-box;\n width: 100%;\n margin: 0;\n padding: 0.25rem 0.5rem;\n border: 1px solid var(--live-editor-color-border);\n border-radius: var(--live-editor-radius);\n background: var(--live-editor-color-surface);\n color: var(--live-editor-color-text);\n font: inherit;\n font-size: var(--live-editor-font-size-sm);\n\n &:focus {\n border-color: var(--live-editor-color-focus);\n outline: none;\n }\n\n &[aria-invalid='true'] {\n border-color: var(--live-editor-color-danger);\n }\n }\n\n .mono {\n font-family: var(--live-editor-font-mono);\n font-size: var(--live-editor-font-size-xs);\n }\n\n /* `appearance` is set because a host reset may turn it off. */\n .checkbox {\n appearance: auto;\n width: 1rem;\n height: 1rem;\n margin: 0;\n accent-color: var(--live-editor-color-primary);\n }\n\n /* `margin-bottom: 0` keeps the fields below from jumping when the error\n appears: a host page's `<p>` margin would push them (#409). */\n .error {\n margin: 0.25rem 0 0;\n font-size: var(--live-editor-font-size-xs);\n color: var(--live-editor-color-danger);\n }\n\n .label {\n display: block;\n font-size: var(--live-editor-font-size-xs);\n font-weight: 500;\n color: var(--live-editor-color-text-muted);\n }\n\n .stack {\n display: flex;\n flex-direction: column;\n gap: 0.25rem;\n }\n\n .object {\n display: flex;\n flex-direction: column;\n gap: 0.5rem;\n padding: 0.5rem;\n border: 1px solid var(--live-editor-color-border-subtle);\n border-radius: var(--live-editor-radius);\n background: var(--live-editor-color-surface-muted);\n }\n\n .readonly {\n display: flex;\n flex-direction: column;\n gap: 0.25rem;\n padding: 0.75rem;\n border: 1px dashed var(--live-editor-color-warning-border);\n border-radius: var(--live-editor-radius);\n font-size: var(--live-editor-font-size-xs);\n color: var(--live-editor-color-warning-text);\n }\n\n .readonlyCode {\n display: block;\n overflow-x: auto;\n font-family: var(--live-editor-font-mono);\n }\n\n .color {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n }\n\n .colorSwatch {\n flex-shrink: 0;\n width: 3rem;\n height: 2rem;\n padding: 0.125rem;\n }\n\n .colorSource {\n font-family: var(--live-editor-font-mono);\n font-size: var(--live-editor-font-size-xs);\n color: var(--live-editor-color-text-muted);\n }\n}\n","import styles from './fields.module.css';\n\n// `role=\"alert\"` announces the message when it appears; the control points\n// at it with `aria-describedby` so it's read again on focus (#637).\nconst FieldError = ({ id, message }: { id: string; message: string | null }) =>\n message ? (\n <p id={id} role=\"alert\" className={styles.error}>\n {message}\n </p>\n ) : null;\n\nexport default FieldError;\n","import { BINDING_PROP } from '~/constants';\n\nimport type { PanelBinding } from '../../panel-binding';\n\n// Which built-in control `Field` renders for a binding. Exported so a\n// custom panel can ask the same question, most often whether a binding\n// needs a structural editor.\nexport type FieldKind =\n // `canEditValue: false` — shown as preserved source, not editable.\n | 'readonly'\n // An array literal (`items`/`data` or `type: 'array'`) — the Items editor,\n // or `useDndItems` for your own markup.\n | 'items'\n // Element children given as extracted nodes — the Children editor, or\n // `useDndChildren` for your own markup.\n | 'children'\n | 'richtext'\n | 'jsx'\n | 'html'\n // A plain object value — one nested field per key.\n | 'object'\n | 'boolean'\n | 'select'\n | 'color'\n | 'date'\n | 'time'\n | 'datetime'\n | 'url'\n | 'email'\n | 'tel'\n | 'number'\n | 'text';\n\nexport type FieldKindBinding = Pick<\n PanelBinding,\n 'property' | 'type' | 'value' | 'options' | 'canEditValue'\n>;\n\nconst isColorProperty = (property: string): boolean =>\n property.toLowerCase().includes('color');\n\n// The first match wins. Describes the built-in control only: `renderField`\n// runs first and can replace it.\nexport const getFieldKind = (binding: FieldKindBinding): FieldKind => {\n const { property, type, value } = binding;\n\n if (binding.canEditValue === false) {\n return 'readonly';\n }\n\n if (property === 'items' || property === 'data' || type === 'array') {\n return 'items';\n }\n\n if (type === 'richtext') {\n return 'richtext';\n }\n\n if (property === BINDING_PROP.INNER_HTML) {\n return 'html';\n }\n\n if (type === 'jsx') {\n return 'jsx';\n }\n\n if (property === 'children' && Array.isArray(value)) {\n return 'children';\n }\n\n if (typeof value === 'object' && value !== null && !Array.isArray(value)) {\n return 'object';\n }\n\n if (type === 'boolean' || typeof value === 'boolean') {\n return 'boolean';\n }\n\n if (binding.options && Array.isArray(binding.options)) {\n return 'select';\n }\n\n if (type === 'color' || isColorProperty(property)) {\n return 'color';\n }\n\n if (type === 'date') {\n return 'date';\n }\n\n if (type === 'time') {\n return 'time';\n }\n\n if (type === 'datetime') {\n return 'datetime';\n }\n\n if (type === 'url') {\n return 'url';\n }\n\n if (type === 'email') {\n return 'email';\n }\n\n if (type === 'tel') {\n return 'tel';\n }\n\n if (type === 'number' || typeof value === 'number') {\n return 'number';\n }\n\n return 'text';\n};\n\n// True for the two kinds whose value holds further data-bound elements, so a\n// flat input can't edit them: route these to `Field` or to the matching hook.\nexport const isStructuralFieldKind = (\n kind: FieldKind,\n): kind is 'items' | 'children' => kind === 'items' || kind === 'children';\n","import { createContext, useContext, useId } from 'react';\n\nimport type { FieldKind } from './field-kind';\n\n// The ids a `LabeledField` gives the field inside it: the control's, which\n// the label's `htmlFor` points at, and the label's own, for a control that\n// can't take an id and points back with `aria-labelledby` instead, such as\n// a `renderField` control (#637).\nexport const FieldIdsContext = createContext<{\n controlId: string;\n labelId: string;\n} | null>(null);\n\n// The ids a control carries: its own, for the label's `htmlFor`, its\n// label's, for `aria-labelledby`, and its error message's, for\n// `aria-describedby`. `isLabelled` is false outside a `LabeledField`, such\n// as in a custom panel's own markup, where the control names itself with\n// `aria-label` instead.\nexport const useFieldIds = () => {\n const labelledIds = useContext(FieldIdsContext);\n\n const fallbackId = useId();\n\n const controlId = labelledIds?.controlId ?? fallbackId;\n\n return {\n controlId,\n labelId: labelledIds?.labelId,\n errorId: `${controlId}-error`,\n isLabelled: labelledIds !== null,\n };\n};\n\n// The built-in controls that take the label's `htmlFor`: every native\n// input, select and text area. The structural and source editors have no\n// single control to point at.\nexport const LABELABLE_KINDS: ReadonlySet<FieldKind> = new Set([\n 'text',\n 'number',\n 'url',\n 'email',\n 'tel',\n 'time',\n 'date',\n 'datetime',\n 'boolean',\n 'select',\n 'color',\n 'richtext',\n]);\n","import { useState } from 'react';\n\nimport { useLiveMessages } from '~/components/context/messages';\nimport { validateBindingValue } from '~/utils/ast/validate';\n\nimport type { PanelBinding } from '../../panel-binding';\nimport { useFieldIds } from './field-ids';\n\n// What a field control needs besides the binding: the text being typed,\n// the validation error, the ids and aria props that tie the control to its\n// label and error, and the two ways to commit. Shared by the built-in\n// controls and the ui-kit ones (`@jbpark/live-editor/ui-kit`).\nexport const useFieldState = (binding: PanelBinding) => {\n const messages = useLiveMessages();\n // `value` is the JS value; `rawValue` is the source text, for the text\n // controls and the source editors (#238).\n const { value, rawValue, onChange } = binding;\n\n const [validationError, setValidationError] = useState<string | null>(null);\n const { controlId, labelId, errorId, isLabelled } = useFieldIds();\n\n // What the user is typing, for the inputs that commit on blur (url,\n // number, text area). Reset during render when `rawValue` changes from\n // outside, such as undo, so the old text isn't committed on blur (#284).\n const [text, setText] = useState(rawValue);\n const [prevRawValue, setPrevRawValue] = useState(rawValue);\n\n if (rawValue !== prevRawValue) {\n setPrevRawValue(rawValue);\n setText(rawValue);\n }\n\n // For the controls that take an id: the id the label points at, a name\n // of their own outside a `LabeledField`, and the error once there is one.\n const controlProps = {\n id: controlId,\n 'aria-label': isLabelled ? undefined : binding.label,\n 'aria-invalid': validationError ? true : undefined,\n 'aria-describedby': validationError ? errorId : undefined,\n };\n // For the controls that take no id: the label they point at inside a\n // `LabeledField`, or a name of their own outside one.\n const labelledByProps = {\n 'aria-label': labelId ? undefined : binding.label,\n 'aria-labelledby': labelId,\n };\n\n // Skips a commit that doesn't change anything, comparing a string with\n // `rawValue` and anything else with `value`.\n const commitIfChanged = (next: unknown) => {\n const current = typeof next === 'string' ? rawValue : value;\n\n if (next !== current) {\n onChange(next);\n }\n };\n\n // Commits `next` if it passes the binding's validation, else shows why\n // not.\n const validateAndCommit = (next: unknown) => {\n const result = validateBindingValue(binding, next, {\n messages: messages.validation,\n });\n\n if (!result.valid) {\n setValidationError(result.message ?? messages.panel.invalidValue);\n\n return;\n }\n\n setValidationError(null);\n commitIfChanged(next);\n };\n\n return {\n text,\n setText,\n validationError,\n errorId,\n controlProps,\n labelledByProps,\n commitIfChanged,\n validateAndCommit,\n };\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;ACIA,MAAM,cAAc,EAAE,IAAI,cACxB,UACE,oBAAC,KAAD;CAAO;CAAI,MAAK;CAAQ,WAAWA,sBAAO;CACvC,UAAA;AACA,CAAA,IACD;;;AC6BN,MAAM,mBAAmB,aACvB,SAAS,YAAY,CAAC,CAAC,SAAS,OAAO;AAIzC,MAAa,gBAAgB,YAAyC;CACpE,MAAM,EAAE,UAAU,MAAM,UAAU;CAElC,IAAI,QAAQ,iBAAiB,OAC3B,OAAO;CAGT,IAAI,aAAa,WAAW,aAAa,UAAU,SAAS,SAC1D,OAAO;CAGT,IAAI,SAAS,YACX,OAAO;CAGT,IAAI,aAAa,aAAa,YAC5B,OAAO;CAGT,IAAI,SAAS,OACX,OAAO;CAGT,IAAI,aAAa,cAAc,MAAM,QAAQ,KAAK,GAChD,OAAO;CAGT,IAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK,GACrE,OAAO;CAGT,IAAI,SAAS,aAAa,OAAO,UAAU,WACzC,OAAO;CAGT,IAAI,QAAQ,WAAW,MAAM,QAAQ,QAAQ,OAAO,GAClD,OAAO;CAGT,IAAI,SAAS,WAAW,gBAAgB,QAAQ,GAC9C,OAAO;CAGT,IAAI,SAAS,QACX,OAAO;CAGT,IAAI,SAAS,QACX,OAAO;CAGT,IAAI,SAAS,YACX,OAAO;CAGT,IAAI,SAAS,OACX,OAAO;CAGT,IAAI,SAAS,SACX,OAAO;CAGT,IAAI,SAAS,OACX,OAAO;CAGT,IAAI,SAAS,YAAY,OAAO,UAAU,UACxC,OAAO;CAGT,OAAO;AACT;AAIA,MAAa,yBACX,SACiC,SAAS,WAAW,SAAS;;;ACjHhE,MAAa,kBAAkB,cAGrB,IAAI;AAOd,MAAa,oBAAoB;CAC/B,MAAM,cAAc,WAAW,eAAe;CAE9C,MAAM,aAAa,MAAM;CAEzB,MAAM,YAAY,aAAa,aAAa;CAE5C,OAAO;EACL;EACA,SAAS,aAAa;EACtB,SAAS,GAAG,UAAU;EACtB,YAAY,gBAAgB;CAC9B;AACF;AAKA,MAAa,kCAA0C,IAAI,IAAI;CAC7D;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;;;ACrCD,MAAa,iBAAiB,YAA0B;CACtD,MAAM,WAAW,gBAAgB;CAGjC,MAAM,EAAE,OAAO,UAAU,aAAa;CAEtC,MAAM,CAAC,iBAAiB,sBAAsB,SAAwB,IAAI;CAC1E,MAAM,EAAE,WAAW,SAAS,SAAS,eAAe,YAAY;CAKhE,MAAM,CAAC,MAAM,WAAW,SAAS,QAAQ;CACzC,MAAM,CAAC,cAAc,mBAAmB,SAAS,QAAQ;CAEzD,IAAI,aAAa,cAAc;EAC7B,gBAAgB,QAAQ;EACxB,QAAQ,QAAQ;CAClB;CAIA,MAAM,eAAe;EACnB,IAAI;EACJ,cAAc,aAAa,KAAA,IAAY,QAAQ;EAC/C,gBAAgB,kBAAkB,OAAO,KAAA;EACzC,oBAAoB,kBAAkB,UAAU,KAAA;CAClD;CAGA,MAAM,kBAAkB;EACtB,cAAc,UAAU,KAAA,IAAY,QAAQ;EAC5C,mBAAmB;CACrB;CAIA,MAAM,mBAAmB,SAAkB;EAGzC,IAAI,UAFY,OAAO,SAAS,WAAW,WAAW,QAGpD,SAAS,IAAI;CAEjB;CAIA,MAAM,qBAAqB,SAAkB;EAC3C,MAAM,SAAS,qBAAqB,SAAS,MAAM,EACjD,UAAU,SAAS,WACrB,CAAC;EAED,IAAI,CAAC,OAAO,OAAO;GACjB,mBAAmB,OAAO,WAAW,SAAS,MAAM,YAAY;GAEhE;EACF;EAEA,mBAAmB,IAAI;EACvB,gBAAgB,IAAI;CACtB;CAEA,OAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF;AACF"}
|
package/dist/utils/ast/index.js
CHANGED
|
@@ -1,88 +1,3 @@
|
|
|
1
|
-
import { C as isJSXElement, E as isJSXFragment, H as isTemplateLiteral, I as isSpreadElement, L as isStringLiteral, M as isNumericLiteral, N as isObjectExpression, P as isObjectProperty, U as isUnaryExpression, dt as parseExpression, v as isArrayExpression, x as isIdentifier, y as isBooleanLiteral } from "../../document-BkGWDZGc.js";
|
|
2
1
|
import { n as validateBindingValue, t as defaultValidationMessages } from "../../validate-CZz30Pnb.js";
|
|
3
|
-
import { a as getChildrenSignatures, l as findEditableChildren, n as updateAll, o as extract,
|
|
4
|
-
//#region src/utils/ast/editable-value.ts
|
|
5
|
-
const MAX_FLATTEN_DEPTH = 20;
|
|
6
|
-
const isEditablePrimitive = (value) => typeof value === "string" || typeof value === "number" || typeof value === "boolean";
|
|
7
|
-
const flattenEditableValue = (value) => {
|
|
8
|
-
const parsed = parseValue(value);
|
|
9
|
-
if (typeof parsed !== "object" || parsed === null || Object.keys(parsed).length === 0) return null;
|
|
10
|
-
const entries = [];
|
|
11
|
-
const walk = (node, path, depth) => {
|
|
12
|
-
if (depth > MAX_FLATTEN_DEPTH) return;
|
|
13
|
-
if (isEditablePrimitive(node)) {
|
|
14
|
-
entries.push({
|
|
15
|
-
path,
|
|
16
|
-
value: node
|
|
17
|
-
});
|
|
18
|
-
return;
|
|
19
|
-
}
|
|
20
|
-
if (Array.isArray(node)) {
|
|
21
|
-
node.forEach((item, index) => walk(item, [...path, index], depth + 1));
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
|
-
if (typeof node === "object" && node !== null) Object.entries(node).forEach(([key, item]) => walk(item, [...path, key], depth + 1));
|
|
25
|
-
};
|
|
26
|
-
walk(parsed, [], 0);
|
|
27
|
-
return entries.length ? entries : null;
|
|
28
|
-
};
|
|
29
|
-
const findEditableLeaf = (root, path) => {
|
|
30
|
-
let node = unwrapExpression(root);
|
|
31
|
-
for (const segment of path) {
|
|
32
|
-
if (isArrayExpression(node)) {
|
|
33
|
-
if (typeof segment !== "number") return null;
|
|
34
|
-
const elements = node.elements.slice(0, segment + 1);
|
|
35
|
-
const element = node.elements[segment];
|
|
36
|
-
if (!element || elements.some((item) => isSpreadElement(item))) return null;
|
|
37
|
-
node = unwrapExpression(element);
|
|
38
|
-
continue;
|
|
39
|
-
}
|
|
40
|
-
if (isObjectExpression(node)) {
|
|
41
|
-
const key = String(segment);
|
|
42
|
-
const matches = (property) => isObjectProperty(property) && !property.computed && (isIdentifier(property.key) && property.key.name === key || isStringLiteral(property.key) && property.key.value === key || isNumericLiteral(property.key) && String(property.key.value) === key);
|
|
43
|
-
let index = node.properties.length - 1;
|
|
44
|
-
while (index >= 0 && !matches(node.properties[index])) index--;
|
|
45
|
-
const property = node.properties[index];
|
|
46
|
-
if (!property || !isObjectProperty(property) || node.properties.slice(index + 1).some((item) => isSpreadElement(item))) return null;
|
|
47
|
-
node = unwrapExpression(property.value);
|
|
48
|
-
continue;
|
|
49
|
-
}
|
|
50
|
-
return null;
|
|
51
|
-
}
|
|
52
|
-
return node;
|
|
53
|
-
};
|
|
54
|
-
const isEditableLeaf = (node) => isStringLiteral(node) || isNumericLiteral(node) || isBooleanLiteral(node) || isUnaryExpression(node) && node.operator === "-" && isNumericLiteral(node.argument) || isTemplateLiteral(node) && node.expressions.length === 0 || isJSXElement(node) || isJSXFragment(node);
|
|
55
|
-
const quoteString = (text, quote) => {
|
|
56
|
-
if (quote === "`") return `\`${text.replace(/[\\`]|\$\{/g, (match) => `\\${match}`)}\``;
|
|
57
|
-
const escaped = JSON.stringify(text).slice(1, -1);
|
|
58
|
-
return quote === "\"" ? `"${escaped}"` : `'${escaped.replace(/\\"/g, "\"").replace(/'/g, "\\'")}'`;
|
|
59
|
-
};
|
|
60
|
-
const isJSXSource = (text) => {
|
|
61
|
-
try {
|
|
62
|
-
const expression = parseExpression(text, { plugins: ["jsx"] });
|
|
63
|
-
return isJSXElement(expression) || isJSXFragment(expression);
|
|
64
|
-
} catch {
|
|
65
|
-
return false;
|
|
66
|
-
}
|
|
67
|
-
};
|
|
68
|
-
const leafSource = (value, leaf, next) => {
|
|
69
|
-
if (typeof next !== "string") return String(next);
|
|
70
|
-
if ((isJSXElement(leaf) || isJSXFragment(leaf)) && isJSXSource(next)) return next.trim();
|
|
71
|
-
const quote = isStringLiteral(leaf) || isTemplateLiteral(leaf) ? value[leaf.start] : "'";
|
|
72
|
-
return quoteString(next, quote);
|
|
73
|
-
};
|
|
74
|
-
const setEditableValue = (value, path, next) => {
|
|
75
|
-
if (!path.length) return value;
|
|
76
|
-
let leaf;
|
|
77
|
-
try {
|
|
78
|
-
leaf = findEditableLeaf(parseExpression(value, { plugins: ["jsx", "typescript"] }), path);
|
|
79
|
-
} catch {
|
|
80
|
-
return value;
|
|
81
|
-
}
|
|
82
|
-
if (!leaf || !isEditableLeaf(leaf) || leaf.start == null || leaf.end == null) return value;
|
|
83
|
-
return `${value.slice(0, leaf.start)}${leafSource(value, leaf, next)}${value.slice(leaf.end)}`;
|
|
84
|
-
};
|
|
85
|
-
//#endregion
|
|
2
|
+
import { C as parseValue, _ as setEditableValue, a as getChildrenSignatures, l as findEditableChildren, n as updateAll, o as extract, p as flattenEditableValue, t as update } from "../../update-DnX2ibYn.js";
|
|
86
3
|
export { defaultValidationMessages, extract, findEditableChildren, flattenEditableValue, getChildrenSignatures, parseValue, setEditableValue, update, updateAll, validateBindingValue };
|
|
87
|
-
|
|
88
|
-
//# sourceMappingURL=index.js.map
|
package/dist/utils/index.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { t as clearEditorCaches } from "../editor-caches-
|
|
2
|
-
import {
|
|
3
|
-
import { n as
|
|
4
|
-
import { a as replaceSections, n as createDocument, r as extractSections, t as checkDocument } from "../sections-rD8NuRI8.js";
|
|
1
|
+
import { t as clearEditorCaches } from "../editor-caches-DXYXgWeD.js";
|
|
2
|
+
import { a as cn, n as compile, r as preloadScripts, t as clearCompilationCache } from "../compile-BWZ5U59y.js";
|
|
3
|
+
import { a as replaceSections, n as createDocument, r as extractSections, t as checkDocument } from "../sections-CnG7OLr8.js";
|
|
5
4
|
export { checkDocument, clearCompilationCache, clearEditorCaches, cn, compile, createDocument, extractSections, preloadScripts, replaceSections };
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
7
|
-
"version": "6.1.
|
|
7
|
+
"version": "6.1.1",
|
|
8
8
|
"type": "module",
|
|
9
9
|
"module": "./dist/index.js",
|
|
10
10
|
"types": "./dist/index.d.ts",
|
|
@@ -119,8 +119,7 @@
|
|
|
119
119
|
"@uiw/react-codemirror": "^4.25.11",
|
|
120
120
|
"codemirror": "^6.0.0",
|
|
121
121
|
"lucide-react": "^1.31.0",
|
|
122
|
-
"tailwindcss": "^4.3.3"
|
|
123
|
-
"zod": "^4.4.3"
|
|
122
|
+
"tailwindcss": "^4.3.3"
|
|
124
123
|
},
|
|
125
124
|
"peerDependencies": {
|
|
126
125
|
"@jbpark/ui-kit": "^10.5.0",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"boundary-CySnDUs2.js","names":[],"sources":["../src/components/error/error.tsx","../src/components/error/boundary.tsx"],"sourcesContent":["import { useLiveMessages } from '~/components/context/messages';\nimport { cn } from '~/utils/cn';\n\nexport interface Props extends React.ComponentPropsWithRef<'div'> {\n message?: string | null;\n title?: string;\n onReset?: () => void;\n}\n\nconst Error = ({ message, title, className, style, onReset }: Props) => {\n const messages = useLiveMessages();\n\n if (!message) {\n return null;\n }\n\n return (\n <div\n className={cn(className)}\n style={{\n padding: '16px',\n backgroundColor: '#fee2e2',\n border: '1px solid #fca5a5',\n borderRadius: '6px',\n color: '#7f1d1d',\n ...style,\n }}\n >\n <h3 style={{ fontSize: '14px', fontWeight: 'bold', margin: 0 }}>\n {title ?? messages.errors.default}\n </h3>\n <pre\n style={{\n marginTop: '8px',\n fontSize: '12px',\n whiteSpace: 'pre-wrap',\n margin: '8px 0 0 0',\n }}\n >\n {message}\n </pre>\n {onReset && (\n <button\n style={{\n marginTop: '8px',\n padding: '4px 12px',\n fontSize: '12px',\n color: 'white',\n backgroundColor: '#dc2626',\n border: 'none',\n borderRadius: '4px',\n cursor: 'pointer',\n }}\n onClick={onReset}\n >\n {messages.errors.tryAgain}\n </button>\n )}\n </div>\n );\n};\n\nexport default Error;\n","import React from 'react';\n\nimport {\n type LiveMessages,\n MessagesContext,\n} from '~/components/context/messages';\n\nimport Error from './error';\n\ninterface Props {\n children: React.ReactNode;\n // `reset` clears the caught error and renders `children` again, the same\n // thing the built-in error's reset button does.\n fallback?: (message?: string, reset?: () => void) => React.ReactNode;\n onError?: (e: Error, info: React.ErrorInfo) => void;\n // When any of these values changes (compared one by one, by identity), a\n // caught error resets and `children` render again, such as when the code\n // that failed is edited.\n resetKeys?: readonly unknown[];\n}\n\ninterface State {\n hasError: boolean;\n error?: Error;\n // The resetKeys the last render saw, so getDerivedStateFromProps can tell\n // when they change.\n resetKeys?: readonly unknown[];\n}\n\nconst keysChanged = (\n prev: readonly unknown[] = [],\n next: readonly unknown[] = [],\n) => prev.length !== next.length || next.some((key, i) => key !== prev[i]);\n\nclass ErrorBoundary extends React.Component<Props, State> {\n static contextType = MessagesContext;\n declare context: LiveMessages;\n\n constructor(props: Props) {\n super(props);\n this.state = { hasError: false, resetKeys: props.resetKeys };\n }\n\n static getDerivedStateFromError(error: Error): Partial<State> {\n return { hasError: true, error };\n }\n\n componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {\n console.error('🚨 [Boundary] Rendering error:', error, errorInfo);\n this.props.onError?.(error, errorInfo);\n }\n\n // Clears a caught error in the same render that sees new resetKeys, so\n // `children` get their next chance right away and the fallback doesn't\n // render (and run its effects) once more first (#442). If `children` throw\n // again with the new keys, getDerivedStateFromError catches that in this\n // same render pass.\n static getDerivedStateFromProps(\n props: Props,\n state: State,\n ): Partial<State> | null {\n if (!keysChanged(state.resetKeys, props.resetKeys)) {\n return null;\n }\n\n return state.hasError\n ? { hasError: false, error: undefined, resetKeys: props.resetKeys }\n : { resetKeys: props.resetKeys };\n }\n\n reset = () => this.setState({ hasError: false, error: undefined });\n\n render() {\n if (this.state.hasError) {\n return this.props.fallback ? (\n <>{this.props.fallback(this.state.error?.message, this.reset)}</>\n ) : (\n <Error\n message={this.state.error?.message}\n onReset={this.reset}\n title={this.context.errors.rendering}\n />\n );\n }\n\n return this.props.children;\n }\n}\n\nexport default ErrorBoundary;\n"],"mappings":";;;;;AASA,MAAM,SAAS,EAAE,SAAS,OAAO,WAAW,OAAO,cAAqB;CACtE,MAAM,WAAW,gBAAgB;CAEjC,IAAI,CAAC,SACH,OAAO;CAGT,OACE,qBAAC,OAAD;EACE,WAAW,GAAG,SAAS;EACvB,OAAO;GACL,SAAS;GACT,iBAAiB;GACjB,QAAQ;GACR,cAAc;GACd,OAAO;GACP,GAAG;EACL;EATF,UAAA;GAWE,oBAAC,MAAD;IAAI,OAAO;KAAE,UAAU;KAAQ,YAAY;KAAQ,QAAQ;IAAE;IAC1D,UAAA,SAAS,SAAS,OAAO;GACxB,CAAA;GACJ,oBAAC,OAAD;IACE,OAAO;KACL,WAAW;KACX,UAAU;KACV,YAAY;KACZ,QAAQ;IACV;IAEC,UAAA;GACE,CAAA;GACJ,WACC,oBAAC,UAAD;IACE,OAAO;KACL,WAAW;KACX,SAAS;KACT,UAAU;KACV,OAAO;KACP,iBAAiB;KACjB,QAAQ;KACR,cAAc;KACd,QAAQ;IACV;IACA,SAAS;IAER,UAAA,SAAS,OAAO;GACX,CAAA;EAEP;;AAET;;;AC/BA,MAAM,eACJ,OAA2B,CAAC,GAC5B,OAA2B,CAAC,MACzB,KAAK,WAAW,KAAK,UAAU,KAAK,MAAM,KAAK,MAAM,QAAQ,KAAK,EAAE;AAEzE,IAAM,gBAAN,cAA4B,MAAM,UAAwB;CACxD,OAAO,cAAc;CAGrB,YAAY,OAAc;EACxB,MAAM,KAAK;EACX,KAAK,QAAQ;GAAE,UAAU;GAAO,WAAW,MAAM;EAAU;CAC7D;CAEA,OAAO,yBAAyB,OAA8B;EAC5D,OAAO;GAAE,UAAU;GAAM;EAAM;CACjC;CAEA,kBAAkB,OAAc,WAA4B;EAC1D,QAAQ,MAAM,kCAAkC,OAAO,SAAS;EAChE,KAAK,MAAM,UAAU,OAAO,SAAS;CACvC;CAOA,OAAO,yBACL,OACA,OACuB;EACvB,IAAI,CAAC,YAAY,MAAM,WAAW,MAAM,SAAS,GAC/C,OAAO;EAGT,OAAO,MAAM,WACT;GAAE,UAAU;GAAO,OAAO,KAAA;GAAW,WAAW,MAAM;EAAU,IAChE,EAAE,WAAW,MAAM,UAAU;CACnC;CAEA,cAAc,KAAK,SAAS;EAAE,UAAU;EAAO,OAAO,KAAA;CAAU,CAAC;CAEjE,SAAS;EACP,IAAI,KAAK,MAAM,UACb,OAAO,KAAK,MAAM,WAChB,oBAAA,UAAA,EAAA,UAAG,KAAK,MAAM,SAAS,KAAK,MAAM,OAAO,SAAS,KAAK,KAAK,EAAI,CAAA,IAEhE,oBAAC,OAAD;GACE,SAAS,KAAK,MAAM,OAAO;GAC3B,SAAS,KAAK;GACd,OAAO,KAAK,QAAQ,OAAO;EAC5B,CAAA;EAIL,OAAO,KAAK,MAAM;CACpB;AACF"}
|