@gabvdl/ui 0.0.14
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.md +95 -0
- package/dist/components/button/button.d.ts +39 -0
- package/dist/components/button/button.d.ts.map +1 -0
- package/dist/components/button/button.js +72 -0
- package/dist/components/button/button.js.map +1 -0
- package/dist/components/button/index.d.ts +5 -0
- package/dist/components/button/index.d.ts.map +1 -0
- package/dist/components/button/tooltip.d.ts +31 -0
- package/dist/components/button/tooltip.d.ts.map +1 -0
- package/dist/components/button/tooltip.js +83 -0
- package/dist/components/button/tooltip.js.map +1 -0
- package/dist/components/changelog/changelog.d.ts +52 -0
- package/dist/components/changelog/changelog.d.ts.map +1 -0
- package/dist/components/changelog/changelog.js +164 -0
- package/dist/components/changelog/changelog.js.map +1 -0
- package/dist/components/changelog/index.d.ts +3 -0
- package/dist/components/changelog/index.d.ts.map +1 -0
- package/dist/components/copy-button/copy-button.d.ts +16 -0
- package/dist/components/copy-button/copy-button.d.ts.map +1 -0
- package/dist/components/copy-button/copy-button.js +40 -0
- package/dist/components/copy-button/copy-button.js.map +1 -0
- package/dist/components/copy-button/index.d.ts +3 -0
- package/dist/components/copy-button/index.d.ts.map +1 -0
- package/dist/components/drop-zone/drop-zone.d.ts +20 -0
- package/dist/components/drop-zone/drop-zone.d.ts.map +1 -0
- package/dist/components/drop-zone/drop-zone.js +54 -0
- package/dist/components/drop-zone/drop-zone.js.map +1 -0
- package/dist/components/drop-zone/index.d.ts +5 -0
- package/dist/components/drop-zone/index.d.ts.map +1 -0
- package/dist/components/drop-zone/use-file-drop.d.ts +45 -0
- package/dist/components/drop-zone/use-file-drop.d.ts.map +1 -0
- package/dist/components/drop-zone/use-file-drop.js +97 -0
- package/dist/components/drop-zone/use-file-drop.js.map +1 -0
- package/dist/components/element-picker/element-picker-field.d.ts +30 -0
- package/dist/components/element-picker/element-picker-field.d.ts.map +1 -0
- package/dist/components/element-picker/element-picker-field.js +273 -0
- package/dist/components/element-picker/element-picker-field.js.map +1 -0
- package/dist/components/element-picker/element-picker.d.ts +45 -0
- package/dist/components/element-picker/element-picker.d.ts.map +1 -0
- package/dist/components/element-picker/element-picker.js +179 -0
- package/dist/components/element-picker/element-picker.js.map +1 -0
- package/dist/components/element-picker/element-preview.d.ts +23 -0
- package/dist/components/element-picker/element-preview.d.ts.map +1 -0
- package/dist/components/element-picker/element-preview.js +72 -0
- package/dist/components/element-picker/element-preview.js.map +1 -0
- package/dist/components/element-picker/index.d.ts +12 -0
- package/dist/components/element-picker/index.d.ts.map +1 -0
- package/dist/components/element-picker/parse.d.ts +32 -0
- package/dist/components/element-picker/parse.d.ts.map +1 -0
- package/dist/components/element-picker/parse.js +230 -0
- package/dist/components/element-picker/parse.js.map +1 -0
- package/dist/components/element-picker/types.d.ts +72 -0
- package/dist/components/element-picker/types.d.ts.map +1 -0
- package/dist/components/element-picker/use-element-picker.d.ts +56 -0
- package/dist/components/element-picker/use-element-picker.d.ts.map +1 -0
- package/dist/components/element-picker/use-element-picker.js +201 -0
- package/dist/components/element-picker/use-element-picker.js.map +1 -0
- package/dist/components/empty-state/empty-state.d.ts +16 -0
- package/dist/components/empty-state/empty-state.d.ts.map +1 -0
- package/dist/components/empty-state/empty-state.js +41 -0
- package/dist/components/empty-state/empty-state.js.map +1 -0
- package/dist/components/empty-state/index.d.ts +3 -0
- package/dist/components/empty-state/index.d.ts.map +1 -0
- package/dist/components/floating-panel/dock-context.d.ts +66 -0
- package/dist/components/floating-panel/dock-context.d.ts.map +1 -0
- package/dist/components/floating-panel/dock-context.js +96 -0
- package/dist/components/floating-panel/dock-context.js.map +1 -0
- package/dist/components/floating-panel/dock.d.ts +26 -0
- package/dist/components/floating-panel/dock.d.ts.map +1 -0
- package/dist/components/floating-panel/dock.js +46 -0
- package/dist/components/floating-panel/dock.js.map +1 -0
- package/dist/components/floating-panel/floating-panel.d.ts +69 -0
- package/dist/components/floating-panel/floating-panel.d.ts.map +1 -0
- package/dist/components/floating-panel/floating-panel.js +285 -0
- package/dist/components/floating-panel/floating-panel.js.map +1 -0
- package/dist/components/floating-panel/index.d.ts +7 -0
- package/dist/components/floating-panel/index.d.ts.map +1 -0
- package/dist/components/fuzzy-list/fuzzy-list.d.ts +96 -0
- package/dist/components/fuzzy-list/fuzzy-list.d.ts.map +1 -0
- package/dist/components/fuzzy-list/fuzzy-list.js +127 -0
- package/dist/components/fuzzy-list/fuzzy-list.js.map +1 -0
- package/dist/components/fuzzy-list/index.d.ts +4 -0
- package/dist/components/fuzzy-list/index.d.ts.map +1 -0
- package/dist/components/global-search/global-search.d.ts +78 -0
- package/dist/components/global-search/global-search.d.ts.map +1 -0
- package/dist/components/global-search/global-search.js +181 -0
- package/dist/components/global-search/global-search.js.map +1 -0
- package/dist/components/global-search/hotkey.d.ts +23 -0
- package/dist/components/global-search/hotkey.d.ts.map +1 -0
- package/dist/components/global-search/hotkey.js +41 -0
- package/dist/components/global-search/hotkey.js.map +1 -0
- package/dist/components/global-search/index.d.ts +5 -0
- package/dist/components/global-search/index.d.ts.map +1 -0
- package/dist/components/iframe-preview/iframe-preview.d.ts +64 -0
- package/dist/components/iframe-preview/iframe-preview.d.ts.map +1 -0
- package/dist/components/iframe-preview/iframe-preview.js +257 -0
- package/dist/components/iframe-preview/iframe-preview.js.map +1 -0
- package/dist/components/iframe-preview/index.d.ts +3 -0
- package/dist/components/iframe-preview/index.d.ts.map +1 -0
- package/dist/components/image-viewer/image-viewer.d.ts +30 -0
- package/dist/components/image-viewer/image-viewer.d.ts.map +1 -0
- package/dist/components/image-viewer/image-viewer.js +226 -0
- package/dist/components/image-viewer/image-viewer.js.map +1 -0
- package/dist/components/image-viewer/index.d.ts +4 -0
- package/dist/components/image-viewer/index.d.ts.map +1 -0
- package/dist/components/image-viewer/progressive-image.d.ts +20 -0
- package/dist/components/image-viewer/progressive-image.d.ts.map +1 -0
- package/dist/components/image-viewer/progressive-image.js +62 -0
- package/dist/components/image-viewer/progressive-image.js.map +1 -0
- package/dist/components/image-viewer/viewable-image.d.ts +17 -0
- package/dist/components/image-viewer/viewable-image.d.ts.map +1 -0
- package/dist/components/image-viewer/viewable-image.js +38 -0
- package/dist/components/image-viewer/viewable-image.js.map +1 -0
- package/dist/components/input/index.d.ts +3 -0
- package/dist/components/input/index.d.ts.map +1 -0
- package/dist/components/input/input.d.ts +15 -0
- package/dist/components/input/input.d.ts.map +1 -0
- package/dist/components/input/input.js +56 -0
- package/dist/components/input/input.js.map +1 -0
- package/dist/components/modal/index.d.ts +5 -0
- package/dist/components/modal/index.d.ts.map +1 -0
- package/dist/components/modal/modal-context.d.ts +37 -0
- package/dist/components/modal/modal-context.d.ts.map +1 -0
- package/dist/components/modal/modal-context.js +81 -0
- package/dist/components/modal/modal-context.js.map +1 -0
- package/dist/components/modal/modal.d.ts +28 -0
- package/dist/components/modal/modal.d.ts.map +1 -0
- package/dist/components/modal/modal.js +94 -0
- package/dist/components/modal/modal.js.map +1 -0
- package/dist/components/nav-2d/index.d.ts +3 -0
- package/dist/components/nav-2d/index.d.ts.map +1 -0
- package/dist/components/nav-2d/nav-2d.d.ts +59 -0
- package/dist/components/nav-2d/nav-2d.d.ts.map +1 -0
- package/dist/components/nav-2d/nav-2d.js +290 -0
- package/dist/components/nav-2d/nav-2d.js.map +1 -0
- package/dist/components/phone-preview/index.d.ts +3 -0
- package/dist/components/phone-preview/index.d.ts.map +1 -0
- package/dist/components/phone-preview/phone-preview.d.ts +45 -0
- package/dist/components/phone-preview/phone-preview.d.ts.map +1 -0
- package/dist/components/phone-preview/phone-preview.js +117 -0
- package/dist/components/phone-preview/phone-preview.js.map +1 -0
- package/dist/components/progressive-bash/index.d.ts +7 -0
- package/dist/components/progressive-bash/index.d.ts.map +1 -0
- package/dist/components/progressive-bash/parse.d.ts +88 -0
- package/dist/components/progressive-bash/parse.d.ts.map +1 -0
- package/dist/components/progressive-bash/parse.js +120 -0
- package/dist/components/progressive-bash/parse.js.map +1 -0
- package/dist/components/progressive-bash/playback.d.ts +50 -0
- package/dist/components/progressive-bash/playback.d.ts.map +1 -0
- package/dist/components/progressive-bash/playback.js +19 -0
- package/dist/components/progressive-bash/playback.js.map +1 -0
- package/dist/components/progressive-bash/progressive-bash.d.ts +97 -0
- package/dist/components/progressive-bash/progressive-bash.d.ts.map +1 -0
- package/dist/components/progressive-bash/progressive-bash.js +377 -0
- package/dist/components/progressive-bash/progressive-bash.js.map +1 -0
- package/dist/components/progressive-list/index.d.ts +3 -0
- package/dist/components/progressive-list/index.d.ts.map +1 -0
- package/dist/components/progressive-list/progressive-list.d.ts +55 -0
- package/dist/components/progressive-list/progressive-list.d.ts.map +1 -0
- package/dist/components/progressive-list/progressive-list.js +83 -0
- package/dist/components/progressive-list/progressive-list.js.map +1 -0
- package/dist/components/progressive-table/index.d.ts +3 -0
- package/dist/components/progressive-table/index.d.ts.map +1 -0
- package/dist/components/progressive-table/progressive-table.d.ts +58 -0
- package/dist/components/progressive-table/progressive-table.d.ts.map +1 -0
- package/dist/components/progressive-table/progressive-table.js +86 -0
- package/dist/components/progressive-table/progressive-table.js.map +1 -0
- package/dist/components/progressive-text/index.d.ts +3 -0
- package/dist/components/progressive-text/index.d.ts.map +1 -0
- package/dist/components/progressive-text/progressive-text.d.ts +54 -0
- package/dist/components/progressive-text/progressive-text.d.ts.map +1 -0
- package/dist/components/progressive-text/progressive-text.js +97 -0
- package/dist/components/progressive-text/progressive-text.js.map +1 -0
- package/dist/components/progressive-timeline/index.d.ts +3 -0
- package/dist/components/progressive-timeline/index.d.ts.map +1 -0
- package/dist/components/progressive-timeline/progressive-timeline.d.ts +46 -0
- package/dist/components/progressive-timeline/progressive-timeline.d.ts.map +1 -0
- package/dist/components/progressive-timeline/progressive-timeline.js +62 -0
- package/dist/components/progressive-timeline/progressive-timeline.js.map +1 -0
- package/dist/components/relative-time/index.d.ts +3 -0
- package/dist/components/relative-time/index.d.ts.map +1 -0
- package/dist/components/relative-time/relative-time.d.ts +14 -0
- package/dist/components/relative-time/relative-time.d.ts.map +1 -0
- package/dist/components/relative-time/relative-time.js +15 -0
- package/dist/components/relative-time/relative-time.js.map +1 -0
- package/dist/components/resizable-layout/index.d.ts +3 -0
- package/dist/components/resizable-layout/index.d.ts.map +1 -0
- package/dist/components/resizable-layout/resizable-layout.d.ts +113 -0
- package/dist/components/resizable-layout/resizable-layout.d.ts.map +1 -0
- package/dist/components/resizable-layout/resizable-layout.js +398 -0
- package/dist/components/resizable-layout/resizable-layout.js.map +1 -0
- package/dist/components/rich-input/compose.d.ts +13 -0
- package/dist/components/rich-input/compose.d.ts.map +1 -0
- package/dist/components/rich-input/compose.js +28 -0
- package/dist/components/rich-input/compose.js.map +1 -0
- package/dist/components/rich-input/index.d.ts +5 -0
- package/dist/components/rich-input/index.d.ts.map +1 -0
- package/dist/components/rich-input/parts.d.ts +53 -0
- package/dist/components/rich-input/parts.d.ts.map +1 -0
- package/dist/components/rich-input/parts.js +285 -0
- package/dist/components/rich-input/parts.js.map +1 -0
- package/dist/components/rich-input/rich-input.d.ts +64 -0
- package/dist/components/rich-input/rich-input.d.ts.map +1 -0
- package/dist/components/rich-input/rich-input.js +351 -0
- package/dist/components/rich-input/rich-input.js.map +1 -0
- package/dist/components/rich-input/types.d.ts +92 -0
- package/dist/components/rich-input/types.d.ts.map +1 -0
- package/dist/components/rich-input/use-draft.d.ts +9 -0
- package/dist/components/rich-input/use-draft.d.ts.map +1 -0
- package/dist/components/rich-input/use-draft.js +47 -0
- package/dist/components/rich-input/use-draft.js.map +1 -0
- package/dist/components/rich-input/use-file-upload.d.ts +18 -0
- package/dist/components/rich-input/use-file-upload.d.ts.map +1 -0
- package/dist/components/rich-input/use-file-upload.js +88 -0
- package/dist/components/rich-input/use-file-upload.js.map +1 -0
- package/dist/components/rich-input/use-guidelines.d.ts +16 -0
- package/dist/components/rich-input/use-guidelines.d.ts.map +1 -0
- package/dist/components/rich-input/use-guidelines.js +85 -0
- package/dist/components/rich-input/use-guidelines.js.map +1 -0
- package/dist/components/rich-input/use-input-history.d.ts +22 -0
- package/dist/components/rich-input/use-input-history.d.ts.map +1 -0
- package/dist/components/rich-input/use-input-history.js +61 -0
- package/dist/components/rich-input/use-input-history.js.map +1 -0
- package/dist/components/rich-input/use-mention.d.ts +24 -0
- package/dist/components/rich-input/use-mention.d.ts.map +1 -0
- package/dist/components/rich-input/use-mention.js +54 -0
- package/dist/components/rich-input/use-mention.js.map +1 -0
- package/dist/components/search-input/index.d.ts +3 -0
- package/dist/components/search-input/index.d.ts.map +1 -0
- package/dist/components/search-input/search-input.d.ts +14 -0
- package/dist/components/search-input/search-input.d.ts.map +1 -0
- package/dist/components/search-input/search-input.js +63 -0
- package/dist/components/search-input/search-input.js.map +1 -0
- package/dist/components/skeleton/index.d.ts +3 -0
- package/dist/components/skeleton/index.d.ts.map +1 -0
- package/dist/components/skeleton/skeleton.d.ts +19 -0
- package/dist/components/skeleton/skeleton.d.ts.map +1 -0
- package/dist/components/skeleton/skeleton.js +29 -0
- package/dist/components/skeleton/skeleton.js.map +1 -0
- package/dist/components/spinner/index.d.ts +3 -0
- package/dist/components/spinner/index.d.ts.map +1 -0
- package/dist/components/spinner/spinner.d.ts +11 -0
- package/dist/components/spinner/spinner.d.ts.map +1 -0
- package/dist/components/spinner/spinner.js +32 -0
- package/dist/components/spinner/spinner.js.map +1 -0
- package/dist/components/status-badge/index.d.ts +3 -0
- package/dist/components/status-badge/index.d.ts.map +1 -0
- package/dist/components/status-badge/status-badge.d.ts +31 -0
- package/dist/components/status-badge/status-badge.d.ts.map +1 -0
- package/dist/components/status-badge/status-badge.js +53 -0
- package/dist/components/status-badge/status-badge.js.map +1 -0
- package/dist/components/theme/index.d.ts +5 -0
- package/dist/components/theme/index.d.ts.map +1 -0
- package/dist/components/theme/theme-toggle.d.ts +11 -0
- package/dist/components/theme/theme-toggle.d.ts.map +1 -0
- package/dist/components/theme/theme-toggle.js +57 -0
- package/dist/components/theme/theme-toggle.js.map +1 -0
- package/dist/components/theme/theme.d.ts +29 -0
- package/dist/components/theme/theme.d.ts.map +1 -0
- package/dist/components/theme/theme.js +70 -0
- package/dist/components/theme/theme.js.map +1 -0
- package/dist/components/toast/index.d.ts +3 -0
- package/dist/components/toast/index.d.ts.map +1 -0
- package/dist/components/toast/toast.d.ts +64 -0
- package/dist/components/toast/toast.d.ts.map +1 -0
- package/dist/components/toast/toast.js +155 -0
- package/dist/components/toast/toast.js.map +1 -0
- package/dist/components/virtual-list/index.d.ts +3 -0
- package/dist/components/virtual-list/index.d.ts.map +1 -0
- package/dist/components/virtual-list/virtual-list.d.ts +71 -0
- package/dist/components/virtual-list/virtual-list.d.ts.map +1 -0
- package/dist/components/virtual-list/virtual-list.js +65 -0
- package/dist/components/virtual-list/virtual-list.js.map +1 -0
- package/dist/hooks/index.d.ts +12 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/use-copy-to-clipboard.d.ts +26 -0
- package/dist/hooks/use-copy-to-clipboard.d.ts.map +1 -0
- package/dist/hooks/use-copy-to-clipboard.js +36 -0
- package/dist/hooks/use-copy-to-clipboard.js.map +1 -0
- package/dist/hooks/use-debounced-value.d.ts +23 -0
- package/dist/hooks/use-debounced-value.d.ts.map +1 -0
- package/dist/hooks/use-debounced-value.js +19 -0
- package/dist/hooks/use-debounced-value.js.map +1 -0
- package/dist/hooks/use-intersection.d.ts +25 -0
- package/dist/hooks/use-intersection.d.ts.map +1 -0
- package/dist/hooks/use-intersection.js +38 -0
- package/dist/hooks/use-intersection.js.map +1 -0
- package/dist/hooks/use-local-storage.d.ts +12 -0
- package/dist/hooks/use-local-storage.d.ts.map +1 -0
- package/dist/hooks/use-local-storage.js +43 -0
- package/dist/hooks/use-local-storage.js.map +1 -0
- package/dist/hooks/use-long-press.d.ts +37 -0
- package/dist/hooks/use-long-press.d.ts.map +1 -0
- package/dist/hooks/use-long-press.js +39 -0
- package/dist/hooks/use-long-press.js.map +1 -0
- package/dist/hooks/use-media-query.d.ts +17 -0
- package/dist/hooks/use-media-query.d.ts.map +1 -0
- package/dist/hooks/use-media-query.js +37 -0
- package/dist/hooks/use-media-query.js.map +1 -0
- package/dist/hooks/use-overlay.d.ts +20 -0
- package/dist/hooks/use-overlay.d.ts.map +1 -0
- package/dist/hooks/use-overlay.js +42 -0
- package/dist/hooks/use-overlay.js.map +1 -0
- package/dist/image-viewer.css +152 -0
- package/dist/index.d.ts +67 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +161 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/format.d.ts +19 -0
- package/dist/lib/format.d.ts.map +1 -0
- package/dist/lib/format.js +62 -0
- package/dist/lib/format.js.map +1 -0
- package/dist/lib/highlight.d.ts +15 -0
- package/dist/lib/highlight.d.ts.map +1 -0
- package/dist/lib/highlight.js +39 -0
- package/dist/lib/highlight.js.map +1 -0
- package/dist/lib/search.d.ts +40 -0
- package/dist/lib/search.d.ts.map +1 -0
- package/dist/lib/search.js +91 -0
- package/dist/lib/search.js.map +1 -0
- package/dist/lib/utils.d.ts +4 -0
- package/dist/lib/utils.d.ts.map +1 -0
- package/dist/lib/utils.js +9 -0
- package/dist/lib/utils.js.map +1 -0
- package/dist/overlay.css +78 -0
- package/dist/styles.css +6 -0
- package/dist/theme.css +81 -0
- package/dist/tooltip.css +53 -0
- package/dist/virtual-list.css +29 -0
- package/package.json +77 -0
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
import { jsxs as r, jsx as e } from "react/jsx-runtime";
|
|
2
|
+
import * as p from "react";
|
|
3
|
+
import { createPortal as j } from "react-dom";
|
|
4
|
+
import { Trash2 as O, MousePointerClick as M, Crosshair as v, ChevronDown as E, X as z } from "lucide-react";
|
|
5
|
+
import { cn as b } from "../../lib/utils.js";
|
|
6
|
+
import { ElementPickerOverlay as H } from "./element-picker.js";
|
|
7
|
+
import { ElementPreview as k } from "./element-preview.js";
|
|
8
|
+
import { formatHtml as R, STYLE_GROUPS as T } from "./parse.js";
|
|
9
|
+
import { useElementPicker as L } from "./use-element-picker.js";
|
|
10
|
+
import { Button as w } from "../button/button.js";
|
|
11
|
+
import { Badge as y } from "../status-badge/status-badge.js";
|
|
12
|
+
import { CopyButton as F } from "../copy-button/copy-button.js";
|
|
13
|
+
const C = {
|
|
14
|
+
input: "violet",
|
|
15
|
+
button: "sky",
|
|
16
|
+
link: "sky",
|
|
17
|
+
media: "amber",
|
|
18
|
+
heading: "emerald",
|
|
19
|
+
text: "emerald",
|
|
20
|
+
list: "neutral",
|
|
21
|
+
table: "neutral",
|
|
22
|
+
form: "violet",
|
|
23
|
+
landmark: "rose",
|
|
24
|
+
container: "neutral"
|
|
25
|
+
}, $ = [
|
|
26
|
+
{ id: "preview", label: "Preview" },
|
|
27
|
+
{ id: "html", label: "HTML" },
|
|
28
|
+
{ id: "parsed", label: "Parsed" }
|
|
29
|
+
];
|
|
30
|
+
function A({ element: t }) {
|
|
31
|
+
const [o, m] = p.useState(null);
|
|
32
|
+
return p.useEffect(() => {
|
|
33
|
+
if (!t.isConnected) return;
|
|
34
|
+
t.scrollIntoView({ block: "nearest", behavior: "smooth" });
|
|
35
|
+
let l = 0;
|
|
36
|
+
const n = () => {
|
|
37
|
+
const s = t.getBoundingClientRect();
|
|
38
|
+
m(
|
|
39
|
+
(d) => d && d.x === s.x && d.y === s.y && d.width === s.width && d.height === s.height ? d : s
|
|
40
|
+
), l = requestAnimationFrame(n);
|
|
41
|
+
};
|
|
42
|
+
return l = requestAnimationFrame(n), () => cancelAnimationFrame(l);
|
|
43
|
+
}, [t]), o ? j(
|
|
44
|
+
/* @__PURE__ */ e(
|
|
45
|
+
"div",
|
|
46
|
+
{
|
|
47
|
+
"aria-hidden": !0,
|
|
48
|
+
style: { position: "fixed", left: o.x - 2, top: o.y - 2, width: o.width + 4, height: o.height + 4 },
|
|
49
|
+
className: "pointer-events-none z-[9997] rounded-[3px] bg-sky-500/10 outline-2 outline-dashed outline-sky-500"
|
|
50
|
+
}
|
|
51
|
+
),
|
|
52
|
+
document.body
|
|
53
|
+
) : null;
|
|
54
|
+
}
|
|
55
|
+
const a = ({ label: t, children: o }) => /* @__PURE__ */ r("div", { className: "grid grid-cols-[7rem_1fr] gap-2 py-1 text-xs", children: [
|
|
56
|
+
/* @__PURE__ */ e("dt", { className: "text-muted-foreground", children: t }),
|
|
57
|
+
/* @__PURE__ */ e("dd", { className: "min-w-0 break-words font-mono text-foreground", children: o })
|
|
58
|
+
] });
|
|
59
|
+
function B({ info: t }) {
|
|
60
|
+
const o = Object.entries(T).map(([l, n]) => [l, n.filter((s) => t.styles[s] !== void 0)]).filter(([, l]) => l.length > 0), m = Object.entries(t.attributes).filter(([l]) => l !== "class" && l !== "id" && !l.startsWith("data-"));
|
|
61
|
+
return /* @__PURE__ */ r("div", { className: "space-y-4", children: [
|
|
62
|
+
/* @__PURE__ */ r("section", { children: [
|
|
63
|
+
/* @__PURE__ */ e("h4", { className: "mb-1 text-[11px] font-semibold tracking-wide text-muted-foreground uppercase", children: "Identity" }),
|
|
64
|
+
/* @__PURE__ */ r("dl", { children: [
|
|
65
|
+
/* @__PURE__ */ e(a, { label: "type", children: /* @__PURE__ */ e(y, { tone: C[t.kind], children: t.kind }) }),
|
|
66
|
+
/* @__PURE__ */ r(a, { label: "tag", children: [
|
|
67
|
+
"<",
|
|
68
|
+
t.tag,
|
|
69
|
+
">"
|
|
70
|
+
] }),
|
|
71
|
+
t.id && /* @__PURE__ */ r(a, { label: "id", children: [
|
|
72
|
+
"#",
|
|
73
|
+
t.id
|
|
74
|
+
] }),
|
|
75
|
+
t.classes.length > 0 && /* @__PURE__ */ e(a, { label: "classes", children: t.classes.map((l) => `.${l}`).join(" ") }),
|
|
76
|
+
t.role && /* @__PURE__ */ e(a, { label: "role", children: t.role }),
|
|
77
|
+
t.label && /* @__PURE__ */ e(a, { label: "label", children: t.label }),
|
|
78
|
+
/* @__PURE__ */ e(a, { label: "selector", children: t.selector })
|
|
79
|
+
] })
|
|
80
|
+
] }),
|
|
81
|
+
/* @__PURE__ */ r("section", { children: [
|
|
82
|
+
/* @__PURE__ */ e("h4", { className: "mb-1 text-[11px] font-semibold tracking-wide text-muted-foreground uppercase", children: "Hierarchy" }),
|
|
83
|
+
/* @__PURE__ */ r("dl", { children: [
|
|
84
|
+
/* @__PURE__ */ e(a, { label: "path", children: /* @__PURE__ */ e("span", { className: "text-muted-foreground", children: t.path }) }),
|
|
85
|
+
/* @__PURE__ */ e(a, { label: "depth", children: t.depth }),
|
|
86
|
+
/* @__PURE__ */ r(a, { label: "position", children: [
|
|
87
|
+
t.index,
|
|
88
|
+
" of ",
|
|
89
|
+
t.siblings
|
|
90
|
+
] }),
|
|
91
|
+
/* @__PURE__ */ e(a, { label: "children", children: t.children }),
|
|
92
|
+
/* @__PURE__ */ r(a, { label: "size", children: [
|
|
93
|
+
Math.round(t.rect.width),
|
|
94
|
+
" × ",
|
|
95
|
+
Math.round(t.rect.height)
|
|
96
|
+
] })
|
|
97
|
+
] })
|
|
98
|
+
] }),
|
|
99
|
+
/* @__PURE__ */ r("section", { children: [
|
|
100
|
+
/* @__PURE__ */ e("h4", { className: "mb-1 text-[11px] font-semibold tracking-wide text-muted-foreground uppercase", children: "Content" }),
|
|
101
|
+
t.text ? /* @__PURE__ */ e("p", { className: "rounded-md bg-muted/50 p-2 text-xs whitespace-pre-wrap text-foreground", children: t.text }) : /* @__PURE__ */ e("p", { className: "text-xs text-muted-foreground italic", children: "No text" })
|
|
102
|
+
] }),
|
|
103
|
+
t.field && /* @__PURE__ */ r("section", { children: [
|
|
104
|
+
/* @__PURE__ */ e("h4", { className: "mb-1 text-[11px] font-semibold tracking-wide text-muted-foreground uppercase", children: "Field" }),
|
|
105
|
+
/* @__PURE__ */ e("dl", { children: Object.entries(t.field).map(
|
|
106
|
+
([l, n]) => n === void 0 ? null : /* @__PURE__ */ e(a, { label: l, children: l === "options" ? n.map(([s]) => s).join(", ") : String(n) || /* @__PURE__ */ e("span", { className: "text-muted-foreground", children: "(empty)" }) }, l)
|
|
107
|
+
) })
|
|
108
|
+
] }),
|
|
109
|
+
(m.length > 0 || Object.keys(t.dataset).length > 0) && /* @__PURE__ */ r("section", { children: [
|
|
110
|
+
/* @__PURE__ */ e("h4", { className: "mb-1 text-[11px] font-semibold tracking-wide text-muted-foreground uppercase", children: "Attributes" }),
|
|
111
|
+
/* @__PURE__ */ r("dl", { children: [
|
|
112
|
+
m.map(([l, n]) => /* @__PURE__ */ e(a, { label: l, children: n || /* @__PURE__ */ e("span", { className: "text-muted-foreground", children: "(empty)" }) }, l)),
|
|
113
|
+
Object.entries(t.dataset).map(([l, n]) => /* @__PURE__ */ e(a, { label: `data.${l}`, children: n }, l))
|
|
114
|
+
] })
|
|
115
|
+
] }),
|
|
116
|
+
/* @__PURE__ */ r("section", { children: [
|
|
117
|
+
/* @__PURE__ */ e("h4", { className: "mb-1 text-[11px] font-semibold tracking-wide text-muted-foreground uppercase", children: "Styles" }),
|
|
118
|
+
o.map(([l, n]) => /* @__PURE__ */ r("div", { className: "mb-2", children: [
|
|
119
|
+
/* @__PURE__ */ e("p", { className: "mb-0.5 text-[11px] font-medium text-foreground/70", children: l }),
|
|
120
|
+
/* @__PURE__ */ e("dl", { children: n.map((s) => /* @__PURE__ */ e(a, { label: s, children: t.styles[s] }, s)) })
|
|
121
|
+
] }, l))
|
|
122
|
+
] })
|
|
123
|
+
] });
|
|
124
|
+
}
|
|
125
|
+
function I({
|
|
126
|
+
entry: t,
|
|
127
|
+
index: o,
|
|
128
|
+
defaultView: m,
|
|
129
|
+
onRemove: l
|
|
130
|
+
}) {
|
|
131
|
+
const [n, s] = p.useState(!1), [d, g] = p.useState(m), [c, x] = p.useState(!1), { info: i, element: h } = t, f = p.useMemo(() => R(i.outerHTML), [i.outerHTML]);
|
|
132
|
+
return /* @__PURE__ */ r(
|
|
133
|
+
"li",
|
|
134
|
+
{
|
|
135
|
+
onMouseEnter: () => x(!0),
|
|
136
|
+
onMouseLeave: () => x(!1),
|
|
137
|
+
className: "overflow-hidden rounded-xl border border-border bg-card",
|
|
138
|
+
children: [
|
|
139
|
+
c && h.isConnected && /* @__PURE__ */ e(A, { element: h }),
|
|
140
|
+
/* @__PURE__ */ r("div", { className: "flex items-center gap-3 p-2", children: [
|
|
141
|
+
/* @__PURE__ */ r("div", { className: "relative shrink-0", children: [
|
|
142
|
+
/* @__PURE__ */ e(k, { element: h, height: 56, minScale: 0.34, className: "w-24" }),
|
|
143
|
+
/* @__PURE__ */ e("span", { className: "absolute -bottom-1 -left-1 flex size-4 items-center justify-center rounded-full bg-sky-500 text-[9px] font-bold text-white", children: o + 1 })
|
|
144
|
+
] }),
|
|
145
|
+
/* @__PURE__ */ r(
|
|
146
|
+
"button",
|
|
147
|
+
{
|
|
148
|
+
type: "button",
|
|
149
|
+
onClick: () => s((u) => !u),
|
|
150
|
+
className: "flex min-w-0 flex-1 items-center gap-2 text-left",
|
|
151
|
+
"aria-expanded": n,
|
|
152
|
+
children: [
|
|
153
|
+
/* @__PURE__ */ r("div", { className: "min-w-0 flex-1", children: [
|
|
154
|
+
/* @__PURE__ */ r("div", { className: "flex items-center gap-1.5", children: [
|
|
155
|
+
/* @__PURE__ */ r("code", { className: "truncate text-xs font-medium text-foreground", children: [
|
|
156
|
+
i.tag,
|
|
157
|
+
i.id ? `#${i.id}` : i.classes[0] ? `.${i.classes[0]}` : ""
|
|
158
|
+
] }),
|
|
159
|
+
/* @__PURE__ */ e(y, { tone: C[i.kind], children: i.kind })
|
|
160
|
+
] }),
|
|
161
|
+
/* @__PURE__ */ e("p", { className: "mt-0.5 truncate text-[11px] text-muted-foreground", children: i.text || i.label || i.path })
|
|
162
|
+
] }),
|
|
163
|
+
/* @__PURE__ */ e(E, { className: b("size-4 shrink-0 text-muted-foreground transition-transform", n && "rotate-180") })
|
|
164
|
+
]
|
|
165
|
+
}
|
|
166
|
+
),
|
|
167
|
+
/* @__PURE__ */ e(
|
|
168
|
+
"button",
|
|
169
|
+
{
|
|
170
|
+
type: "button",
|
|
171
|
+
onClick: l,
|
|
172
|
+
"aria-label": `Remove ${i.tag}`,
|
|
173
|
+
className: "shrink-0 rounded-md p-1.5 text-muted-foreground transition-colors hover:bg-destructive/10 hover:text-destructive",
|
|
174
|
+
children: /* @__PURE__ */ e(z, { className: "size-4" })
|
|
175
|
+
}
|
|
176
|
+
)
|
|
177
|
+
] }),
|
|
178
|
+
n && /* @__PURE__ */ r("div", { className: "border-t border-border", children: [
|
|
179
|
+
/* @__PURE__ */ r("div", { className: "flex items-center justify-between gap-2 px-2 py-1.5", children: [
|
|
180
|
+
/* @__PURE__ */ e("div", { className: "flex gap-0.5 rounded-lg bg-muted p-0.5", children: $.map((u) => /* @__PURE__ */ e(
|
|
181
|
+
"button",
|
|
182
|
+
{
|
|
183
|
+
type: "button",
|
|
184
|
+
onClick: () => g(u.id),
|
|
185
|
+
className: b(
|
|
186
|
+
"rounded-md px-2.5 py-1 text-xs font-medium transition-colors",
|
|
187
|
+
d === u.id ? "bg-background text-foreground shadow-sm" : "text-muted-foreground hover:text-foreground"
|
|
188
|
+
),
|
|
189
|
+
children: u.label
|
|
190
|
+
},
|
|
191
|
+
u.id
|
|
192
|
+
)) }),
|
|
193
|
+
/* @__PURE__ */ e(
|
|
194
|
+
F,
|
|
195
|
+
{
|
|
196
|
+
value: () => d === "html" ? f : d === "parsed" ? JSON.stringify(i, null, 2) : i.text,
|
|
197
|
+
label: d === "html" ? "Copy HTML" : d === "parsed" ? "Copy JSON" : "Copy text"
|
|
198
|
+
}
|
|
199
|
+
)
|
|
200
|
+
] }),
|
|
201
|
+
/* @__PURE__ */ r("div", { className: "max-h-80 overflow-auto px-3 pt-1 pb-3", children: [
|
|
202
|
+
d === "preview" && /* @__PURE__ */ e(k, { element: h, height: 200 }),
|
|
203
|
+
d === "html" && /* @__PURE__ */ e("pre", { className: "overflow-x-auto rounded-lg bg-muted/50 p-3 font-mono text-[11px] leading-relaxed text-foreground", children: f }),
|
|
204
|
+
d === "parsed" && /* @__PURE__ */ e(B, { info: i })
|
|
205
|
+
] })
|
|
206
|
+
] })
|
|
207
|
+
]
|
|
208
|
+
}
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
function Q({
|
|
212
|
+
label: t = "Page elements",
|
|
213
|
+
hint: o,
|
|
214
|
+
pickLabel: m = "Pick elements",
|
|
215
|
+
emptyLabel: l = "Nothing selected yet",
|
|
216
|
+
defaultView: n = "parsed",
|
|
217
|
+
className: s,
|
|
218
|
+
...d
|
|
219
|
+
}) {
|
|
220
|
+
const g = L(d), { active: c, start: x, stop: i, picked: h, remove: f, clear: u, ignoreProps: P } = g;
|
|
221
|
+
return /* @__PURE__ */ r("div", { ...P, className: b("space-y-3", s), children: [
|
|
222
|
+
/* @__PURE__ */ r("div", { className: "flex items-end justify-between gap-3", children: [
|
|
223
|
+
/* @__PURE__ */ r("div", { className: "min-w-0", children: [
|
|
224
|
+
/* @__PURE__ */ e("label", { className: "text-sm font-medium text-foreground", children: t }),
|
|
225
|
+
o && /* @__PURE__ */ e("p", { className: "mt-0.5 text-xs text-muted-foreground", children: o })
|
|
226
|
+
] }),
|
|
227
|
+
/* @__PURE__ */ r("div", { className: "flex shrink-0 items-center gap-1", children: [
|
|
228
|
+
h.length > 0 && /* @__PURE__ */ e(w, { variant: "ghost", size: "sm", icon: /* @__PURE__ */ e(O, {}), onClick: u, children: "Clear" }),
|
|
229
|
+
/* @__PURE__ */ e(
|
|
230
|
+
w,
|
|
231
|
+
{
|
|
232
|
+
variant: c ? "default" : "outline",
|
|
233
|
+
size: "sm",
|
|
234
|
+
icon: c ? /* @__PURE__ */ e(M, {}) : /* @__PURE__ */ e(v, {}),
|
|
235
|
+
onClick: c ? i : x,
|
|
236
|
+
children: c ? "Picking…" : m
|
|
237
|
+
}
|
|
238
|
+
)
|
|
239
|
+
] })
|
|
240
|
+
] }),
|
|
241
|
+
h.length === 0 ? /* @__PURE__ */ r(
|
|
242
|
+
"button",
|
|
243
|
+
{
|
|
244
|
+
type: "button",
|
|
245
|
+
onClick: x,
|
|
246
|
+
className: b(
|
|
247
|
+
"flex w-full flex-col items-center justify-center gap-1.5 rounded-xl border-2 border-dashed border-border px-6 py-8 text-center transition-colors",
|
|
248
|
+
"hover:border-ring/60 hover:bg-muted/40",
|
|
249
|
+
c && "border-ring bg-muted/50"
|
|
250
|
+
),
|
|
251
|
+
children: [
|
|
252
|
+
/* @__PURE__ */ e(v, { className: b("size-5 text-muted-foreground", c && "animate-pulse text-foreground") }),
|
|
253
|
+
/* @__PURE__ */ e("p", { className: "text-sm font-medium text-foreground", children: c ? "Point at the page…" : l }),
|
|
254
|
+
/* @__PURE__ */ e("p", { className: "text-xs text-muted-foreground", children: c ? "Hover to preview, click to select" : "Click to start picking elements" })
|
|
255
|
+
]
|
|
256
|
+
}
|
|
257
|
+
) : /* @__PURE__ */ e("ul", { className: "space-y-2", children: h.map((N, S) => /* @__PURE__ */ e(
|
|
258
|
+
I,
|
|
259
|
+
{
|
|
260
|
+
entry: N,
|
|
261
|
+
index: S,
|
|
262
|
+
defaultView: n,
|
|
263
|
+
onRemove: () => f(N.id)
|
|
264
|
+
},
|
|
265
|
+
N.id
|
|
266
|
+
)) }),
|
|
267
|
+
/* @__PURE__ */ e(H, { picker: g })
|
|
268
|
+
] });
|
|
269
|
+
}
|
|
270
|
+
export {
|
|
271
|
+
Q as ElementPickerField
|
|
272
|
+
};
|
|
273
|
+
//# sourceMappingURL=element-picker-field.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"element-picker-field.js","sources":["../../../src/components/element-picker/element-picker-field.tsx"],"sourcesContent":["import * as React from 'react';\nimport { createPortal } from 'react-dom';\nimport { ChevronDown, Crosshair, MousePointerClick, Trash2, X } from 'lucide-react';\n\nimport { cn } from '../../lib/utils';\nimport { Badge, type Tone } from '../status-badge';\nimport { Button } from '../button';\nimport { CopyButton } from '../copy-button';\nimport { ElementPickerOverlay } from './element-picker';\nimport { ElementPreview } from './element-preview';\nimport { formatHtml, STYLE_GROUPS } from './parse';\nimport { useElementPicker, type UseElementPickerOptions } from './use-element-picker';\nimport type { ElementInfo, ElementKind, PickedElement } from './types';\n\nconst KIND_TONE: Record<ElementKind, Tone> = {\n input: 'violet',\n button: 'sky',\n link: 'sky',\n media: 'amber',\n heading: 'emerald',\n text: 'emerald',\n list: 'neutral',\n table: 'neutral',\n form: 'violet',\n landmark: 'rose',\n container: 'neutral',\n};\n\ntype View = 'preview' | 'html' | 'parsed';\nconst VIEWS: { id: View; label: string }[] = [\n { id: 'preview', label: 'Preview' },\n { id: 'html', label: 'HTML' },\n { id: 'parsed', label: 'Parsed' },\n];\n\n/** Ties a card in the list back to the element it came from, out in the page. */\nfunction Spotlight({ element }: { element: HTMLElement }) {\n const [rect, setRect] = React.useState<DOMRect | null>(null);\n\n React.useEffect(() => {\n if (!element.isConnected) return;\n element.scrollIntoView({ block: 'nearest', behavior: 'smooth' });\n\n let frame = 0;\n const tick = () => {\n const next = element.getBoundingClientRect();\n setRect((prev) =>\n prev && prev.x === next.x && prev.y === next.y && prev.width === next.width && prev.height === next.height\n ? prev\n : next,\n );\n frame = requestAnimationFrame(tick);\n };\n frame = requestAnimationFrame(tick);\n return () => cancelAnimationFrame(frame);\n }, [element]);\n\n if (!rect) return null;\n\n return createPortal(\n <div\n aria-hidden\n style={{ position: 'fixed', left: rect.x - 2, top: rect.y - 2, width: rect.width + 4, height: rect.height + 4 }}\n className=\"pointer-events-none z-[9997] rounded-[3px] bg-sky-500/10 outline-2 outline-dashed outline-sky-500\"\n />,\n document.body,\n );\n}\n\nconst Row = ({ label, children }: { label: string; children: React.ReactNode }) => (\n <div className=\"grid grid-cols-[7rem_1fr] gap-2 py-1 text-xs\">\n <dt className=\"text-muted-foreground\">{label}</dt>\n <dd className=\"min-w-0 break-words font-mono text-foreground\">{children}</dd>\n </div>\n);\n\n/** The parse, laid out for reading: what it is, where it sits, what it says. */\nfunction ParsedView({ info }: { info: ElementInfo }) {\n const styleGroups = Object.entries(STYLE_GROUPS)\n .map(([group, props]) => [group, props.filter((p) => info.styles[p] !== undefined)] as const)\n .filter(([, props]) => props.length > 0);\n\n const attrs = Object.entries(info.attributes).filter(([k]) => k !== 'class' && k !== 'id' && !k.startsWith('data-'));\n\n return (\n <div className=\"space-y-4\">\n <section>\n <h4 className=\"mb-1 text-[11px] font-semibold tracking-wide text-muted-foreground uppercase\">Identity</h4>\n <dl>\n <Row label=\"type\">\n <Badge tone={KIND_TONE[info.kind]}>{info.kind}</Badge>\n </Row>\n <Row label=\"tag\"><{info.tag}></Row>\n {info.id && <Row label=\"id\">#{info.id}</Row>}\n {info.classes.length > 0 && <Row label=\"classes\">{info.classes.map((c) => `.${c}`).join(' ')}</Row>}\n {info.role && <Row label=\"role\">{info.role}</Row>}\n {info.label && <Row label=\"label\">{info.label}</Row>}\n <Row label=\"selector\">{info.selector}</Row>\n </dl>\n </section>\n\n <section>\n <h4 className=\"mb-1 text-[11px] font-semibold tracking-wide text-muted-foreground uppercase\">Hierarchy</h4>\n <dl>\n <Row label=\"path\">\n <span className=\"text-muted-foreground\">{info.path}</span>\n </Row>\n <Row label=\"depth\">{info.depth}</Row>\n <Row label=\"position\">\n {info.index} of {info.siblings}\n </Row>\n <Row label=\"children\">{info.children}</Row>\n <Row label=\"size\">\n {Math.round(info.rect.width)} × {Math.round(info.rect.height)}\n </Row>\n </dl>\n </section>\n\n <section>\n <h4 className=\"mb-1 text-[11px] font-semibold tracking-wide text-muted-foreground uppercase\">Content</h4>\n {info.text ? (\n <p className=\"rounded-md bg-muted/50 p-2 text-xs whitespace-pre-wrap text-foreground\">{info.text}</p>\n ) : (\n <p className=\"text-xs text-muted-foreground italic\">No text</p>\n )}\n </section>\n\n {info.field && (\n <section>\n <h4 className=\"mb-1 text-[11px] font-semibold tracking-wide text-muted-foreground uppercase\">Field</h4>\n <dl>\n {Object.entries(info.field).map(([key, value]) =>\n value === undefined ? null : (\n <Row key={key} label={key}>\n {key === 'options'\n ? (value as [string, string][]).map(([v]) => v).join(', ')\n : String(value) || <span className=\"text-muted-foreground\">(empty)</span>}\n </Row>\n ),\n )}\n </dl>\n </section>\n )}\n\n {(attrs.length > 0 || Object.keys(info.dataset).length > 0) && (\n <section>\n <h4 className=\"mb-1 text-[11px] font-semibold tracking-wide text-muted-foreground uppercase\">Attributes</h4>\n <dl>\n {attrs.map(([k, v]) => (\n <Row key={k} label={k}>\n {v || <span className=\"text-muted-foreground\">(empty)</span>}\n </Row>\n ))}\n {Object.entries(info.dataset).map(([k, v]) => (\n <Row key={k} label={`data.${k}`}>\n {v}\n </Row>\n ))}\n </dl>\n </section>\n )}\n\n <section>\n <h4 className=\"mb-1 text-[11px] font-semibold tracking-wide text-muted-foreground uppercase\">Styles</h4>\n {styleGroups.map(([group, props]) => (\n <div key={group} className=\"mb-2\">\n <p className=\"mb-0.5 text-[11px] font-medium text-foreground/70\">{group}</p>\n <dl>\n {props.map((p) => (\n <Row key={p} label={p}>\n {info.styles[p]}\n </Row>\n ))}\n </dl>\n </div>\n ))}\n </section>\n </div>\n );\n}\n\nfunction PickedCard({\n entry,\n index,\n defaultView,\n onRemove,\n}: {\n entry: PickedElement;\n index: number;\n defaultView: View;\n onRemove: () => void;\n}) {\n const [open, setOpen] = React.useState(false);\n const [view, setView] = React.useState<View>(defaultView);\n const [spotlit, setSpotlit] = React.useState(false);\n const { info, element } = entry;\n\n const html = React.useMemo(() => formatHtml(info.outerHTML), [info.outerHTML]);\n\n return (\n <li\n onMouseEnter={() => setSpotlit(true)}\n onMouseLeave={() => setSpotlit(false)}\n className=\"overflow-hidden rounded-xl border border-border bg-card\"\n >\n {spotlit && element.isConnected && <Spotlight element={element} />}\n\n <div className=\"flex items-center gap-3 p-2\">\n <div className=\"relative shrink-0\">\n <ElementPreview element={element} height={56} minScale={0.34} className=\"w-24\" />\n {/* Bottom corner: a cropped thumbnail reads from the top-left. */}\n <span className=\"absolute -bottom-1 -left-1 flex size-4 items-center justify-center rounded-full bg-sky-500 text-[9px] font-bold text-white\">\n {index + 1}\n </span>\n </div>\n\n <button\n type=\"button\"\n onClick={() => setOpen((v) => !v)}\n className=\"flex min-w-0 flex-1 items-center gap-2 text-left\"\n aria-expanded={open}\n >\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center gap-1.5\">\n <code className=\"truncate text-xs font-medium text-foreground\">\n {info.tag}\n {info.id ? `#${info.id}` : info.classes[0] ? `.${info.classes[0]}` : ''}\n </code>\n <Badge tone={KIND_TONE[info.kind]}>{info.kind}</Badge>\n </div>\n <p className=\"mt-0.5 truncate text-[11px] text-muted-foreground\">\n {info.text || info.label || info.path}\n </p>\n </div>\n <ChevronDown className={cn('size-4 shrink-0 text-muted-foreground transition-transform', open && 'rotate-180')} />\n </button>\n\n <button\n type=\"button\"\n onClick={onRemove}\n aria-label={`Remove ${info.tag}`}\n className=\"shrink-0 rounded-md p-1.5 text-muted-foreground transition-colors hover:bg-destructive/10 hover:text-destructive\"\n >\n <X className=\"size-4\" />\n </button>\n </div>\n\n {open && (\n <div className=\"border-t border-border\">\n <div className=\"flex items-center justify-between gap-2 px-2 py-1.5\">\n <div className=\"flex gap-0.5 rounded-lg bg-muted p-0.5\">\n {VIEWS.map((v) => (\n <button\n key={v.id}\n type=\"button\"\n onClick={() => setView(v.id)}\n className={cn(\n 'rounded-md px-2.5 py-1 text-xs font-medium transition-colors',\n view === v.id ? 'bg-background text-foreground shadow-sm' : 'text-muted-foreground hover:text-foreground',\n )}\n >\n {v.label}\n </button>\n ))}\n </div>\n <CopyButton\n value={() => (view === 'html' ? html : view === 'parsed' ? JSON.stringify(info, null, 2) : info.text)}\n label={view === 'html' ? 'Copy HTML' : view === 'parsed' ? 'Copy JSON' : 'Copy text'}\n />\n </div>\n\n <div className=\"max-h-80 overflow-auto px-3 pt-1 pb-3\">\n {view === 'preview' && <ElementPreview element={element} height={200} />}\n {view === 'html' && (\n <pre className=\"overflow-x-auto rounded-lg bg-muted/50 p-3 font-mono text-[11px] leading-relaxed text-foreground\">\n {html}\n </pre>\n )}\n {view === 'parsed' && <ParsedView info={info} />}\n </div>\n </div>\n )}\n </li>\n );\n}\n\nexport interface ElementPickerFieldProps extends UseElementPickerOptions {\n /** Field label. */\n label?: React.ReactNode;\n /** Small print under the label. */\n hint?: React.ReactNode;\n /** Text on the trigger while idle. */\n pickLabel?: string;\n /** Shown when nothing is selected yet. */\n emptyLabel?: React.ReactNode;\n /** Which detail tab a card opens on. Default `parsed`. */\n defaultView?: View;\n className?: string;\n}\n\n/**\n * The element picker as a form input: a trigger that arms the page, and a list\n * of what you selected — each with a live thumbnail, its formatted HTML, and\n * the parse (text, kind, hierarchy, attributes, computed styles) laid out for\n * reading. Hovering a card spotlights the element back in the page.\n *\n * The value is `PickedElement[]`; `info` on each is plain data, so\n * `JSON.stringify(picked.map((p) => p.info))` is a ready-made payload — which\n * is the point: this is how you let someone hand an LLM, a scraper or a bug\n * report the exact bits of a page they mean.\n */\nexport function ElementPickerField({\n label = 'Page elements',\n hint,\n pickLabel = 'Pick elements',\n emptyLabel = 'Nothing selected yet',\n defaultView = 'parsed',\n className,\n ...options\n}: ElementPickerFieldProps) {\n const picker = useElementPicker(options);\n const { active, start, stop, picked, remove, clear, ignoreProps } = picker;\n\n return (\n <div {...ignoreProps} className={cn('space-y-3', className)}>\n <div className=\"flex items-end justify-between gap-3\">\n <div className=\"min-w-0\">\n <label className=\"text-sm font-medium text-foreground\">{label}</label>\n {hint && <p className=\"mt-0.5 text-xs text-muted-foreground\">{hint}</p>}\n </div>\n\n <div className=\"flex shrink-0 items-center gap-1\">\n {picked.length > 0 && (\n <Button variant=\"ghost\" size=\"sm\" icon={<Trash2 />} onClick={clear}>\n Clear\n </Button>\n )}\n <Button\n variant={active ? 'default' : 'outline'}\n size=\"sm\"\n icon={active ? <MousePointerClick /> : <Crosshair />}\n onClick={active ? stop : start}\n >\n {active ? 'Picking…' : pickLabel}\n </Button>\n </div>\n </div>\n\n {picked.length === 0 ? (\n <button\n type=\"button\"\n onClick={start}\n className={cn(\n 'flex w-full flex-col items-center justify-center gap-1.5 rounded-xl border-2 border-dashed border-border px-6 py-8 text-center transition-colors',\n 'hover:border-ring/60 hover:bg-muted/40',\n active && 'border-ring bg-muted/50',\n )}\n >\n <Crosshair className={cn('size-5 text-muted-foreground', active && 'animate-pulse text-foreground')} />\n <p className=\"text-sm font-medium text-foreground\">{active ? 'Point at the page…' : emptyLabel}</p>\n <p className=\"text-xs text-muted-foreground\">\n {active ? 'Hover to preview, click to select' : 'Click to start picking elements'}\n </p>\n </button>\n ) : (\n <ul className=\"space-y-2\">\n {picked.map((entry, i) => (\n <PickedCard\n key={entry.id}\n entry={entry}\n index={i}\n defaultView={defaultView}\n onRemove={() => remove(entry.id)}\n />\n ))}\n </ul>\n )}\n\n {/* The same picker the field is driving — one hook, one set of listeners. */}\n <ElementPickerOverlay picker={picker} />\n </div>\n );\n}\n"],"names":["KIND_TONE","VIEWS","Spotlight","element","rect","setRect","React","frame","tick","next","prev","createPortal","jsx","Row","label","children","jsxs","ParsedView","info","styleGroups","STYLE_GROUPS","group","props","p","attrs","k","Badge","c","key","value","v","PickedCard","entry","index","defaultView","onRemove","open","setOpen","view","setView","spotlit","setSpotlit","html","formatHtml","ElementPreview","ChevronDown","cn","X","CopyButton","ElementPickerField","hint","pickLabel","emptyLabel","className","options","picker","useElementPicker","active","start","stop","picked","remove","clear","ignoreProps","Button","Trash2","MousePointerClick","Crosshair","i","ElementPickerOverlay"],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { UseElementPickerOptions, UseElementPickerResult } from './use-element-picker';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
export interface ElementPickerOverlayProps {
|
|
4
|
+
/** A live picker, from `useElementPicker`. */
|
|
5
|
+
picker: UseElementPickerResult;
|
|
6
|
+
/** Keep an outline on everything already selected. Default true. */
|
|
7
|
+
highlightSelected?: boolean;
|
|
8
|
+
/** Replace the instruction bar, or pass `false` to drop it. */
|
|
9
|
+
hint?: React.ReactNode | false;
|
|
10
|
+
/** Render into something other than `document.body`. */
|
|
11
|
+
container?: HTMLElement | null;
|
|
12
|
+
className?: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* The chrome on its own — box-model highlight, hover label, instruction bar —
|
|
16
|
+
* driven by a picker you already own. `ElementPicker` is this plus the hook.
|
|
17
|
+
*/
|
|
18
|
+
export declare function ElementPickerOverlay({ picker, highlightSelected, hint, container, className, }: ElementPickerOverlayProps): React.JSX.Element | null;
|
|
19
|
+
export interface ElementPickerProps extends UseElementPickerOptions {
|
|
20
|
+
/** Keep an outline on everything already selected. Default true. */
|
|
21
|
+
highlightSelected?: boolean;
|
|
22
|
+
/** Replace the instruction bar, or pass `false` to drop it. */
|
|
23
|
+
hint?: React.ReactNode | false;
|
|
24
|
+
/** Render the overlay into something other than `document.body`. */
|
|
25
|
+
container?: HTMLElement | null;
|
|
26
|
+
/** Handed the live picker, so a toolbar of your own can drive it. */
|
|
27
|
+
children?: (picker: UseElementPickerResult) => React.ReactNode;
|
|
28
|
+
className?: string;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Point at the page and take it apart: hover any element to preview it, click
|
|
32
|
+
* to select. It draws the devtools box model over whatever is under the cursor
|
|
33
|
+
* — margin, border, padding, content — and hands back both the live node and a
|
|
34
|
+
* serializable parse of it: text, kind, hierarchy position, computed styles.
|
|
35
|
+
*
|
|
36
|
+
* A touchscreen has no hover, so there it's a gesture: **press and hold**, then
|
|
37
|
+
* drag. Elements light up under your finger as it moves and the one you lift on
|
|
38
|
+
* is the one you pick. Until the hold registers, the page still scrolls.
|
|
39
|
+
*
|
|
40
|
+
* This renders only the overlay. `children` is a render prop for your own
|
|
41
|
+
* trigger; for the batteries-included input — thumbnails, HTML, parsed output —
|
|
42
|
+
* reach for `ElementPickerField`.
|
|
43
|
+
*/
|
|
44
|
+
export declare function ElementPicker({ highlightSelected, hint, container, children, className, ...options }: ElementPickerProps): React.JSX.Element;
|
|
45
|
+
//# sourceMappingURL=element-picker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"element-picker.d.ts","sourceRoot":"","sources":["../../../src/components/element-picker/element-picker.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAM/B,OAAO,EAAoB,KAAK,uBAAuB,EAAE,KAAK,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAwFnH,MAAM,WAAW,yBAAyB;IACxC,8CAA8C;IAC9C,MAAM,EAAE,sBAAsB,CAAC;IAC/B,oEAAoE;IACpE,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,+DAA+D;IAC/D,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,GAAG,KAAK,CAAC;IAC/B,wDAAwD;IACxD,SAAS,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,EACnC,MAAM,EACN,iBAAwB,EACxB,IAAI,EACJ,SAAS,EACT,SAAS,GACV,EAAE,yBAAyB,4BA0H3B;AAED,MAAM,WAAW,kBAAmB,SAAQ,uBAAuB;IACjE,oEAAoE;IACpE,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,+DAA+D;IAC/D,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,GAAG,KAAK,CAAC;IAC/B,oEAAoE;IACpE,SAAS,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;IAC/B,qEAAqE;IACrE,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,sBAAsB,KAAK,KAAK,CAAC,SAAS,CAAC;IAC/D,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,aAAa,CAAC,EAC5B,iBAAwB,EACxB,IAAI,EACJ,SAAS,EACT,QAAQ,EACR,SAAS,EACT,GAAG,OAAO,EACX,EAAE,kBAAkB,qBAepB"}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { jsxs as d, Fragment as x, jsx as s } from "react/jsx-runtime";
|
|
2
|
+
import * as f from "react";
|
|
3
|
+
import { createPortal as S } from "react-dom";
|
|
4
|
+
import { cn as W } from "../../lib/utils.js";
|
|
5
|
+
import { useIsTouch as A } from "../../hooks/use-media-query.js";
|
|
6
|
+
import { classify as T } from "./parse.js";
|
|
7
|
+
import { useElementPicker as $ } from "./use-element-picker.js";
|
|
8
|
+
const N = {
|
|
9
|
+
margin: "rgba(246, 178, 107, 0.45)",
|
|
10
|
+
border: "rgba(253, 224, 71, 0.45)",
|
|
11
|
+
padding: "rgba(147, 197, 114, 0.45)",
|
|
12
|
+
content: "rgba(111, 168, 220, 0.45)"
|
|
13
|
+
}, r = (e) => parseFloat(e) || 0;
|
|
14
|
+
function j(e) {
|
|
15
|
+
const t = e.getBoundingClientRect(), n = getComputedStyle(e), o = (l, b, w, u, a) => new DOMRect(l.x + a, l.y + b, Math.max(l.width - a - w, 0), Math.max(l.height - b - u, 0)), h = new DOMRect(t.x, t.y, t.width, t.height), p = o(h, -r(n.marginTop), -r(n.marginRight), -r(n.marginBottom), -r(n.marginLeft)), i = o(h, r(n.borderTopWidth), r(n.borderRightWidth), r(n.borderBottomWidth), r(n.borderLeftWidth)), k = o(i, r(n.paddingTop), r(n.paddingRight), r(n.paddingBottom), r(n.paddingLeft));
|
|
16
|
+
return { margin: p, border: h, padding: i, content: k };
|
|
17
|
+
}
|
|
18
|
+
const y = (e, t) => !!e && !!t && e.x === t.x && e.y === t.y && e.width === t.width && e.height === t.height, z = (e, t) => e === t || !!e && !!t && y(e.margin, t.margin) && y(e.border, t.border) && y(e.padding, t.padding) && y(e.content, t.content), D = (e, t) => e.length === t.length && e.every((n, o) => y(n, t[o])), v = (e, t) => ({
|
|
19
|
+
position: "fixed",
|
|
20
|
+
left: e.x,
|
|
21
|
+
top: e.y,
|
|
22
|
+
width: e.width,
|
|
23
|
+
height: e.height,
|
|
24
|
+
background: t
|
|
25
|
+
});
|
|
26
|
+
function q(e) {
|
|
27
|
+
const t = e.tagName.toLowerCase();
|
|
28
|
+
if (e.id) return `${t}#${e.id}`;
|
|
29
|
+
const n = Array.from(e.classList)[0];
|
|
30
|
+
return n ? `${t}.${n}` : t;
|
|
31
|
+
}
|
|
32
|
+
function G({ el: e, geom: t }) {
|
|
33
|
+
const { border: n } = t, o = n.y > 30;
|
|
34
|
+
return /* @__PURE__ */ d(
|
|
35
|
+
"div",
|
|
36
|
+
{
|
|
37
|
+
style: {
|
|
38
|
+
position: "fixed",
|
|
39
|
+
left: Math.max(n.x, 4),
|
|
40
|
+
top: o ? n.y - 26 : n.bottom + 4,
|
|
41
|
+
maxWidth: "calc(100vw - 8px)"
|
|
42
|
+
},
|
|
43
|
+
className: "pointer-events-none flex items-center gap-1.5 truncate rounded-md bg-slate-900/95 px-2 py-1 text-[11px] font-medium text-white shadow-lg ring-1 ring-white/10",
|
|
44
|
+
children: [
|
|
45
|
+
/* @__PURE__ */ s("span", { className: "font-mono text-sky-300", children: q(e) }),
|
|
46
|
+
/* @__PURE__ */ s("span", { className: "text-slate-500", children: "·" }),
|
|
47
|
+
/* @__PURE__ */ s("span", { className: "text-slate-300", children: T(e) }),
|
|
48
|
+
/* @__PURE__ */ s("span", { className: "text-slate-500", children: "·" }),
|
|
49
|
+
/* @__PURE__ */ d("span", { className: "font-mono text-slate-400", children: [
|
|
50
|
+
Math.round(n.width),
|
|
51
|
+
"×",
|
|
52
|
+
Math.round(n.height)
|
|
53
|
+
] })
|
|
54
|
+
]
|
|
55
|
+
}
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
function H({
|
|
59
|
+
picker: e,
|
|
60
|
+
highlightSelected: t = !0,
|
|
61
|
+
hint: n,
|
|
62
|
+
container: o,
|
|
63
|
+
className: h
|
|
64
|
+
}) {
|
|
65
|
+
const { active: p, hovered: i, holding: k, picked: l, stop: b, ignoreProps: w } = e, u = A(), [a, C] = f.useState(null), [P, M] = f.useState([]), [R, E] = f.useState(!1);
|
|
66
|
+
if (f.useEffect(() => E(!0), []), f.useEffect(() => {
|
|
67
|
+
if (!p || !R) {
|
|
68
|
+
C(null), M([]);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
let m = 0;
|
|
72
|
+
const g = () => {
|
|
73
|
+
const O = i != null && i.isConnected ? j(i) : null;
|
|
74
|
+
C((c) => z(c, O) ? c : O);
|
|
75
|
+
const B = t ? l.filter((c) => c.element.isConnected).map((c) => c.element.getBoundingClientRect()) : [];
|
|
76
|
+
M((c) => D(c, B) ? c : B), m = requestAnimationFrame(g);
|
|
77
|
+
};
|
|
78
|
+
return m = requestAnimationFrame(g), () => cancelAnimationFrame(m);
|
|
79
|
+
}, [p, R, i, l, t]), !R || !p) return null;
|
|
80
|
+
const F = o ?? document.body, L = n === !1 ? null : n ?? /* @__PURE__ */ d(x, { children: [
|
|
81
|
+
/* @__PURE__ */ s("span", { className: "font-medium text-white", children: u ? "Press and hold, then drag" : "Hover to preview" }),
|
|
82
|
+
/* @__PURE__ */ s("span", { className: "hidden text-slate-500 sm:inline", children: "·" }),
|
|
83
|
+
/* @__PURE__ */ s("span", { className: "hidden sm:inline", children: u ? "lift to select" : "click to select" }),
|
|
84
|
+
!u && /* @__PURE__ */ d(x, { children: [
|
|
85
|
+
/* @__PURE__ */ s("span", { className: "hidden text-slate-500 sm:inline", children: "·" }),
|
|
86
|
+
/* @__PURE__ */ d("span", { className: "hidden sm:inline", children: [
|
|
87
|
+
/* @__PURE__ */ s("kbd", { className: "rounded border border-white/20 px-1 font-mono text-[10px]", children: "↑" }),
|
|
88
|
+
" parent"
|
|
89
|
+
] })
|
|
90
|
+
] })
|
|
91
|
+
] });
|
|
92
|
+
return /* @__PURE__ */ s(x, { children: S(
|
|
93
|
+
/* @__PURE__ */ d(
|
|
94
|
+
"div",
|
|
95
|
+
{
|
|
96
|
+
...w,
|
|
97
|
+
className: W("pointer-events-none fixed inset-0 z-[9998]", h),
|
|
98
|
+
"aria-hidden": !0,
|
|
99
|
+
children: [
|
|
100
|
+
a && /* @__PURE__ */ d(x, { children: [
|
|
101
|
+
/* @__PURE__ */ s("div", { style: v(a.margin, N.margin) }),
|
|
102
|
+
/* @__PURE__ */ s("div", { style: v(a.border, N.border) }),
|
|
103
|
+
/* @__PURE__ */ s("div", { style: v(a.padding, N.padding) }),
|
|
104
|
+
/* @__PURE__ */ s("div", { style: v(a.content, N.content) })
|
|
105
|
+
] }),
|
|
106
|
+
P.map((m, g) => /* @__PURE__ */ s(
|
|
107
|
+
"div",
|
|
108
|
+
{
|
|
109
|
+
style: {
|
|
110
|
+
position: "fixed",
|
|
111
|
+
left: m.x - 1,
|
|
112
|
+
top: m.y - 1,
|
|
113
|
+
width: m.width + 2,
|
|
114
|
+
height: m.height + 2
|
|
115
|
+
},
|
|
116
|
+
className: "rounded-[3px] outline-2 outline-offset-0 outline-sky-500",
|
|
117
|
+
children: /* @__PURE__ */ s("span", { className: "absolute -top-1.5 -left-1.5 flex size-4 items-center justify-center rounded-full bg-sky-500 text-[9px] font-bold text-white shadow", children: g + 1 })
|
|
118
|
+
},
|
|
119
|
+
g
|
|
120
|
+
)),
|
|
121
|
+
i && a && /* @__PURE__ */ s(G, { el: i, geom: a }),
|
|
122
|
+
L && /* @__PURE__ */ d(
|
|
123
|
+
"div",
|
|
124
|
+
{
|
|
125
|
+
...w,
|
|
126
|
+
className: "pointer-events-auto fixed inset-x-0 bottom-4 mx-auto flex w-fit max-w-[calc(100vw-1rem)] items-center gap-2 rounded-full bg-slate-900/95 py-2 pr-2 pl-4 text-xs text-slate-300 shadow-xl ring-1 ring-white/10 backdrop-blur",
|
|
127
|
+
children: [
|
|
128
|
+
k && /* @__PURE__ */ s("span", { className: "size-1.5 animate-pulse rounded-full bg-emerald-400" }),
|
|
129
|
+
L,
|
|
130
|
+
l.length > 0 && /* @__PURE__ */ d("span", { className: "rounded-full bg-sky-500/20 px-2 py-0.5 font-medium text-sky-300", children: [
|
|
131
|
+
l.length,
|
|
132
|
+
" selected"
|
|
133
|
+
] }),
|
|
134
|
+
/* @__PURE__ */ s(
|
|
135
|
+
"button",
|
|
136
|
+
{
|
|
137
|
+
type: "button",
|
|
138
|
+
onClick: b,
|
|
139
|
+
className: "rounded-full bg-white px-3 py-1 text-xs font-semibold text-slate-900 transition-colors hover:bg-slate-200",
|
|
140
|
+
children: "Done"
|
|
141
|
+
}
|
|
142
|
+
)
|
|
143
|
+
]
|
|
144
|
+
}
|
|
145
|
+
)
|
|
146
|
+
]
|
|
147
|
+
}
|
|
148
|
+
),
|
|
149
|
+
F
|
|
150
|
+
) });
|
|
151
|
+
}
|
|
152
|
+
function U({
|
|
153
|
+
highlightSelected: e = !0,
|
|
154
|
+
hint: t,
|
|
155
|
+
container: n,
|
|
156
|
+
children: o,
|
|
157
|
+
className: h,
|
|
158
|
+
...p
|
|
159
|
+
}) {
|
|
160
|
+
const i = $(p);
|
|
161
|
+
return /* @__PURE__ */ d(x, { children: [
|
|
162
|
+
o == null ? void 0 : o(i),
|
|
163
|
+
/* @__PURE__ */ s(
|
|
164
|
+
H,
|
|
165
|
+
{
|
|
166
|
+
picker: i,
|
|
167
|
+
highlightSelected: e,
|
|
168
|
+
hint: t,
|
|
169
|
+
container: n,
|
|
170
|
+
className: h
|
|
171
|
+
}
|
|
172
|
+
)
|
|
173
|
+
] });
|
|
174
|
+
}
|
|
175
|
+
export {
|
|
176
|
+
U as ElementPicker,
|
|
177
|
+
H as ElementPickerOverlay
|
|
178
|
+
};
|
|
179
|
+
//# sourceMappingURL=element-picker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"element-picker.js","sources":["../../../src/components/element-picker/element-picker.tsx"],"sourcesContent":["import * as React from 'react';\nimport { createPortal } from 'react-dom';\n\nimport { cn } from '../../lib/utils';\nimport { useIsTouch } from '../../hooks/use-media-query';\nimport { classify } from './parse';\nimport { useElementPicker, type UseElementPickerOptions, type UseElementPickerResult } from './use-element-picker';\n\n/** The devtools box-model palette — deliberately not theme tokens: these have to\n * read over whatever the page happens to be painted in. */\nconst BOX_COLORS = {\n margin: 'rgba(246, 178, 107, 0.45)',\n border: 'rgba(253, 224, 71, 0.45)',\n padding: 'rgba(147, 197, 114, 0.45)',\n content: 'rgba(111, 168, 220, 0.45)',\n};\n\ninterface Geometry {\n margin: DOMRect;\n border: DOMRect;\n padding: DOMRect;\n content: DOMRect;\n}\n\nconst px = (v: string) => parseFloat(v) || 0;\n\n/** Peel the four boxes apart, the way the inspector draws them. */\nfunction geometryOf(el: HTMLElement): Geometry {\n const r = el.getBoundingClientRect();\n const s = getComputedStyle(el);\n\n const inset = (rect: DOMRect, top: number, right: number, bottom: number, left: number) =>\n new DOMRect(rect.x + left, rect.y + top, Math.max(rect.width - left - right, 0), Math.max(rect.height - top - bottom, 0));\n\n const border = new DOMRect(r.x, r.y, r.width, r.height);\n const margin = inset(border, -px(s.marginTop), -px(s.marginRight), -px(s.marginBottom), -px(s.marginLeft));\n const padding = inset(border, px(s.borderTopWidth), px(s.borderRightWidth), px(s.borderBottomWidth), px(s.borderLeftWidth));\n const content = inset(padding, px(s.paddingTop), px(s.paddingRight), px(s.paddingBottom), px(s.paddingLeft));\n\n return { margin, border, padding, content };\n}\n\nconst sameRect = (a: DOMRect | undefined, b: DOMRect | undefined) =>\n !!a && !!b && a.x === b.x && a.y === b.y && a.width === b.width && a.height === b.height;\n\nconst sameGeom = (a: Geometry | null, b: Geometry | null) =>\n a === b ||\n (!!a && !!b && sameRect(a.margin, b.margin) && sameRect(a.border, b.border) && sameRect(a.padding, b.padding) && sameRect(a.content, b.content));\n\nconst sameRects = (a: DOMRect[], b: DOMRect[]) => a.length === b.length && a.every((r, i) => sameRect(r, b[i]));\n\nconst box = (rect: DOMRect, background: string): React.CSSProperties => ({\n position: 'fixed',\n left: rect.x,\n top: rect.y,\n width: rect.width,\n height: rect.height,\n background,\n});\n\n/** Short name for an element: `div.card`, `#hero`, `button`. */\nfunction shortName(el: HTMLElement): string {\n const tag = el.tagName.toLowerCase();\n if (el.id) return `${tag}#${el.id}`;\n const cls = Array.from(el.classList)[0];\n return cls ? `${tag}.${cls}` : tag;\n}\n\n/** The floating badge: what it is, and how big. */\nfunction HoverLabel({ el, geom }: { el: HTMLElement; geom: Geometry }) {\n const { border } = geom;\n const above = border.y > 30;\n\n return (\n <div\n style={{\n position: 'fixed',\n left: Math.max(border.x, 4),\n top: above ? border.y - 26 : border.bottom + 4,\n maxWidth: 'calc(100vw - 8px)',\n }}\n className=\"pointer-events-none flex items-center gap-1.5 truncate rounded-md bg-slate-900/95 px-2 py-1 text-[11px] font-medium text-white shadow-lg ring-1 ring-white/10\"\n >\n <span className=\"font-mono text-sky-300\">{shortName(el)}</span>\n <span className=\"text-slate-500\">·</span>\n <span className=\"text-slate-300\">{classify(el)}</span>\n <span className=\"text-slate-500\">·</span>\n <span className=\"font-mono text-slate-400\">\n {Math.round(border.width)}×{Math.round(border.height)}\n </span>\n </div>\n );\n}\n\nexport interface ElementPickerOverlayProps {\n /** A live picker, from `useElementPicker`. */\n picker: UseElementPickerResult;\n /** Keep an outline on everything already selected. Default true. */\n highlightSelected?: boolean;\n /** Replace the instruction bar, or pass `false` to drop it. */\n hint?: React.ReactNode | false;\n /** Render into something other than `document.body`. */\n container?: HTMLElement | null;\n className?: string;\n}\n\n/**\n * The chrome on its own — box-model highlight, hover label, instruction bar —\n * driven by a picker you already own. `ElementPicker` is this plus the hook.\n */\nexport function ElementPickerOverlay({\n picker,\n highlightSelected = true,\n hint,\n container,\n className,\n}: ElementPickerOverlayProps) {\n const { active, hovered, holding, picked, stop, ignoreProps } = picker;\n const touch = useIsTouch();\n\n const [geom, setGeom] = React.useState<Geometry | null>(null);\n const [selectedRects, setSelectedRects] = React.useState<DOMRect[]>([]);\n const [mounted, setMounted] = React.useState(false);\n\n React.useEffect(() => setMounted(true), []);\n\n // Rects go stale the moment anything scrolls or animates, and there's no event\n // that covers every cause. A frame loop while picking is the honest fix.\n React.useEffect(() => {\n if (!active || !mounted) {\n setGeom(null);\n setSelectedRects([]);\n return;\n }\n\n let frame = 0;\n const tick = () => {\n // Re-measure every frame, but only re-render when something moved —\n // sitting still on one element costs nothing.\n const next = hovered?.isConnected ? geometryOf(hovered) : null;\n setGeom((prev) => (sameGeom(prev, next) ? prev : next));\n\n const rects = highlightSelected\n ? picked.filter((p) => p.element.isConnected).map((p) => p.element.getBoundingClientRect())\n : [];\n setSelectedRects((prev) => (sameRects(prev, rects) ? prev : rects));\n\n frame = requestAnimationFrame(tick);\n };\n frame = requestAnimationFrame(tick);\n return () => cancelAnimationFrame(frame);\n }, [active, mounted, hovered, picked, highlightSelected]);\n\n if (!mounted || !active) return null;\n\n const host = container ?? document.body;\n const instructions =\n hint === false ? null : hint ?? (\n <>\n <span className=\"font-medium text-white\">\n {touch ? 'Press and hold, then drag' : 'Hover to preview'}\n </span>\n <span className=\"hidden text-slate-500 sm:inline\">·</span>\n <span className=\"hidden sm:inline\">{touch ? 'lift to select' : 'click to select'}</span>\n {!touch && (\n <>\n <span className=\"hidden text-slate-500 sm:inline\">·</span>\n <span className=\"hidden sm:inline\">\n <kbd className=\"rounded border border-white/20 px-1 font-mono text-[10px]\">↑</kbd> parent\n </span>\n </>\n )}\n </>\n );\n\n return (\n <>\n {createPortal(\n <div\n {...ignoreProps}\n className={cn('pointer-events-none fixed inset-0 z-[9998]', className)}\n aria-hidden\n >\n {geom && (\n <>\n <div style={box(geom.margin, BOX_COLORS.margin)} />\n <div style={box(geom.border, BOX_COLORS.border)} />\n <div style={box(geom.padding, BOX_COLORS.padding)} />\n <div style={box(geom.content, BOX_COLORS.content)} />\n </>\n )}\n\n {selectedRects.map((rect, i) => (\n <div\n key={i}\n style={{\n position: 'fixed',\n left: rect.x - 1,\n top: rect.y - 1,\n width: rect.width + 2,\n height: rect.height + 2,\n }}\n className=\"rounded-[3px] outline-2 outline-offset-0 outline-sky-500\"\n >\n <span className=\"absolute -top-1.5 -left-1.5 flex size-4 items-center justify-center rounded-full bg-sky-500 text-[9px] font-bold text-white shadow\">\n {i + 1}\n </span>\n </div>\n ))}\n\n {hovered && geom && <HoverLabel el={hovered} geom={geom} />}\n\n {instructions && (\n <div\n {...ignoreProps}\n className=\"pointer-events-auto fixed inset-x-0 bottom-4 mx-auto flex w-fit max-w-[calc(100vw-1rem)] items-center gap-2 rounded-full bg-slate-900/95 py-2 pr-2 pl-4 text-xs text-slate-300 shadow-xl ring-1 ring-white/10 backdrop-blur\"\n >\n {holding && <span className=\"size-1.5 animate-pulse rounded-full bg-emerald-400\" />}\n {instructions}\n {picked.length > 0 && (\n <span className=\"rounded-full bg-sky-500/20 px-2 py-0.5 font-medium text-sky-300\">\n {picked.length} selected\n </span>\n )}\n <button\n type=\"button\"\n onClick={stop}\n className=\"rounded-full bg-white px-3 py-1 text-xs font-semibold text-slate-900 transition-colors hover:bg-slate-200\"\n >\n Done\n </button>\n </div>\n )}\n </div>,\n host,\n )}\n </>\n );\n}\n\nexport interface ElementPickerProps extends UseElementPickerOptions {\n /** Keep an outline on everything already selected. Default true. */\n highlightSelected?: boolean;\n /** Replace the instruction bar, or pass `false` to drop it. */\n hint?: React.ReactNode | false;\n /** Render the overlay into something other than `document.body`. */\n container?: HTMLElement | null;\n /** Handed the live picker, so a toolbar of your own can drive it. */\n children?: (picker: UseElementPickerResult) => React.ReactNode;\n className?: string;\n}\n\n/**\n * Point at the page and take it apart: hover any element to preview it, click\n * to select. It draws the devtools box model over whatever is under the cursor\n * — margin, border, padding, content — and hands back both the live node and a\n * serializable parse of it: text, kind, hierarchy position, computed styles.\n *\n * A touchscreen has no hover, so there it's a gesture: **press and hold**, then\n * drag. Elements light up under your finger as it moves and the one you lift on\n * is the one you pick. Until the hold registers, the page still scrolls.\n *\n * This renders only the overlay. `children` is a render prop for your own\n * trigger; for the batteries-included input — thumbnails, HTML, parsed output —\n * reach for `ElementPickerField`.\n */\nexport function ElementPicker({\n highlightSelected = true,\n hint,\n container,\n children,\n className,\n ...options\n}: ElementPickerProps) {\n const picker = useElementPicker(options);\n\n return (\n <>\n {children?.(picker)}\n <ElementPickerOverlay\n picker={picker}\n highlightSelected={highlightSelected}\n hint={hint}\n container={container}\n className={className}\n />\n </>\n );\n}\n"],"names":["BOX_COLORS","px","v","geometryOf","el","r","s","inset","rect","top","right","bottom","left","border","margin","padding","content","sameRect","a","b","sameGeom","sameRects","i","box","background","shortName","tag","cls","HoverLabel","geom","above","jsxs","jsx","classify","ElementPickerOverlay","picker","highlightSelected","hint","container","className","active","hovered","holding","picked","stop","ignoreProps","touch","useIsTouch","setGeom","React","selectedRects","setSelectedRects","mounted","setMounted","frame","tick","next","prev","rects","p","host","instructions","Fragment","createPortal","cn","ElementPicker","children","options","useElementPicker"],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
export interface ElementPreviewProps {
|
|
3
|
+
/** The element to mirror. A detached or null node renders the fallback. */
|
|
4
|
+
element: HTMLElement | null;
|
|
5
|
+
/** Height of the preview box, in px. Default 120. */
|
|
6
|
+
height?: number;
|
|
7
|
+
/** Never enlarge past 1:1, only shrink to fit. Default true. */
|
|
8
|
+
shrinkOnly?: boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Floor on the scale. Below it the preview crops from the top-left instead of
|
|
11
|
+
* shrinking further — a 900px-wide heading fitted into a thumbnail is two
|
|
12
|
+
* pixels of grey, where its first few words are recognisable. Default 0.
|
|
13
|
+
*/
|
|
14
|
+
minScale?: number;
|
|
15
|
+
className?: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* A live, inert clone of an element, scaled to fit its box. Because the copy
|
|
19
|
+
* sits in the same document it inherits the same stylesheets, so it looks
|
|
20
|
+
* exactly like the thing it came from — the page's CSS does the work.
|
|
21
|
+
*/
|
|
22
|
+
export declare function ElementPreview({ element, height, shrinkOnly, minScale, className, }: ElementPreviewProps): React.JSX.Element;
|
|
23
|
+
//# sourceMappingURL=element-preview.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"element-preview.d.ts","sourceRoot":"","sources":["../../../src/components/element-picker/element-preview.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AA2C/B,MAAM,WAAW,mBAAmB;IAClC,2EAA2E;IAC3E,OAAO,EAAE,WAAW,GAAG,IAAI,CAAC;IAC5B,qDAAqD;IACrD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gEAAgE;IAChE,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,EAC7B,OAAO,EACP,MAAY,EACZ,UAAiB,EACjB,QAAY,EACZ,SAAS,GACV,EAAE,mBAAmB,qBAgFrB"}
|