@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
package/README.md
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# @gabvdl/ui
|
|
2
|
+
|
|
3
|
+
> ⚠️ **Very early alpha.** Expect breaking changes — the API moves fast and any
|
|
4
|
+
> `0.0.x` bump may break you. Pin an exact version. `1.0.0`, coming shortly,
|
|
5
|
+
> will be the first stable cut.
|
|
6
|
+
|
|
7
|
+
Gabriel Vidal's personal design system — tree-shakeable React components built on
|
|
8
|
+
shadcn primitives, reused across the homelab. Flagship: a full-screen image
|
|
9
|
+
viewer with zoom, pan and swipe.
|
|
10
|
+
|
|
11
|
+
Source is mirrored at
|
|
12
|
+
**[github.com/GabrielVidal1/design-system](https://github.com/GabrielVidal1/design-system)**;
|
|
13
|
+
the docs + live demo are at **[ui.gabvdl.xyz](https://ui.gabvdl.xyz)**.
|
|
14
|
+
|
|
15
|
+
## Install
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install @gabvdl/ui
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Use
|
|
22
|
+
|
|
23
|
+
```tsx
|
|
24
|
+
import { ImageViewerProvider, ViewableImage } from '@gabvdl/ui'
|
|
25
|
+
import '@gabvdl/ui/styles.css'
|
|
26
|
+
|
|
27
|
+
export function Gallery({ urls }: { urls: string[] }) {
|
|
28
|
+
return (
|
|
29
|
+
<ImageViewerProvider>
|
|
30
|
+
{urls.map((u) => (
|
|
31
|
+
<ViewableImage key={u} src={u} />
|
|
32
|
+
))}
|
|
33
|
+
</ImageViewerProvider>
|
|
34
|
+
)
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Peer deps: `react >=18`, `react-dom >=18`. Ships ESM + type declarations, and
|
|
39
|
+
side-effect-free except the CSS entry points (`styles.css`, `theme.css`,
|
|
40
|
+
`image-viewer.css`, `virtual-list.css`).
|
|
41
|
+
|
|
42
|
+
## Search: `FuzzyList` and `GlobalSearch`
|
|
43
|
+
|
|
44
|
+
`FuzzyList` is a Fuse.js search box over a windowed `VirtualList`. Two things
|
|
45
|
+
shape how it searches:
|
|
46
|
+
|
|
47
|
+
- **Quoting pins an exact match.** A bare query fuzzy-matches; any
|
|
48
|
+
`"double-quoted"` segment must appear as a literal, case-insensitive substring.
|
|
49
|
+
They mix — `parser "utils.ts"` fuzzy-matches `parser` and then keeps only rows
|
|
50
|
+
whose fields contain `utils.ts`. Both kinds of hit are `<mark>`-highlighted.
|
|
51
|
+
- **The search is debounced** by `debounce` ms (default `400`). The input stays
|
|
52
|
+
instant; only the Fuse pass and the list re-render trail behind it, and a small
|
|
53
|
+
spinner shows while they do. `debounce={0}` searches on every keystroke.
|
|
54
|
+
|
|
55
|
+
`GlobalSearch` is the Cmd-K palette built from those pieces — a `Modal` holding a
|
|
56
|
+
`FuzzyList`. `items` may be an array *or a loader*, called the first time the
|
|
57
|
+
palette opens, which is how you lazily pull a build-time search index:
|
|
58
|
+
|
|
59
|
+
```tsx
|
|
60
|
+
<GlobalSearch
|
|
61
|
+
searchKey="Ctrl+K" // "Mod+K" = ⌘ on Apple, Ctrl elsewhere · null = off
|
|
62
|
+
items={() => fetch('/search-index.json').then((r) => r.json())}
|
|
63
|
+
keys={[{ name: 'name', weight: 3 }, 'summary', 'props']}
|
|
64
|
+
titleKey="name"
|
|
65
|
+
descriptionKey="summary"
|
|
66
|
+
badgeKey="kind"
|
|
67
|
+
trigger="bar" // or "icon" (default) / "none" + controlled `open`
|
|
68
|
+
onSelect={(entry) => navigate(entry.route)}
|
|
69
|
+
/>
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Generating that index at build time
|
|
73
|
+
|
|
74
|
+
The docs app ([`apps/docs/plugins/search-index.ts`](../../apps/docs/plugins/search-index.ts))
|
|
75
|
+
is the reference implementation: a Vite plugin that walks `packages/ui/src` with
|
|
76
|
+
the **TypeScript compiler API**, and for every symbol re-exported from
|
|
77
|
+
`src/index.ts` emits an entry — components, hooks, utilities, **and one entry per
|
|
78
|
+
prop** (`FuzzyList.debounce`, with its type and TSDoc). It writes
|
|
79
|
+
`public/search-index.json` on `buildStart` and regenerates on source changes in
|
|
80
|
+
dev, so nothing is hand-maintained: add a component and it is searchable on the
|
|
81
|
+
next build.
|
|
82
|
+
|
|
83
|
+
## Publishing (maintainer)
|
|
84
|
+
|
|
85
|
+
Public npm releases ride on a version tag — see [`RELEASING.md`](../../RELEASING.md)
|
|
86
|
+
at the repo root. Dev builds still go to the homelab's private verdaccio
|
|
87
|
+
(browse it at [registry.lab.gabvdl.xyz](https://registry.lab.gabvdl.xyz)):
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
# from the homelab repo root, with the registry profile up:
|
|
91
|
+
./services/registry/publish.sh
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Bump `version` in this package's `package.json` first — verdaccio rejects
|
|
95
|
+
re-publishing an existing version.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { TooltipSide } from './tooltip';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
export type ButtonVariant = 'default' | 'outline' | 'ghost' | 'destructive';
|
|
4
|
+
/**
|
|
5
|
+
* Three text tiers (`sm` / `md` / `lg`) plus a square icon-only tier for each.
|
|
6
|
+
* `default` and `icon` are kept as aliases of `md` / `icon-md`.
|
|
7
|
+
*/
|
|
8
|
+
export type ButtonSize = 'sm' | 'md' | 'lg' | 'icon-sm' | 'icon-md' | 'icon-lg' | 'default' | 'icon';
|
|
9
|
+
export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
10
|
+
variant?: ButtonVariant;
|
|
11
|
+
size?: ButtonSize;
|
|
12
|
+
/**
|
|
13
|
+
* Swap the leading icon for a spinner, disable the button and mark it
|
|
14
|
+
* `aria-busy`. The label stays put so the button does not resize.
|
|
15
|
+
*/
|
|
16
|
+
loading?: boolean;
|
|
17
|
+
/** Announced (and shown, on a text-tier button) in place of the label while `loading`. */
|
|
18
|
+
loadingText?: string;
|
|
19
|
+
/** Leading icon — or the only content, on an `icon-*` tier. */
|
|
20
|
+
icon?: React.ReactNode;
|
|
21
|
+
/** Put `icon` after the label instead of before it. */
|
|
22
|
+
iconPosition?: 'left' | 'right';
|
|
23
|
+
/**
|
|
24
|
+
* Hover/focus hint. On an icon-only button it also becomes the accessible
|
|
25
|
+
* name when no `aria-label` is given; otherwise it is wired up as
|
|
26
|
+
* `aria-describedby`.
|
|
27
|
+
*/
|
|
28
|
+
tooltip?: React.ReactNode;
|
|
29
|
+
tooltipSide?: TooltipSide;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* The design-system button: four variants, three size tiers (icon-only twins of
|
|
33
|
+
* each), an icon slot, a loading state and an optional tooltip.
|
|
34
|
+
*
|
|
35
|
+
* `type` defaults to `"button"` so dropping one inside a form never submits it
|
|
36
|
+
* by accident — pass `type="submit"` when that is what you want.
|
|
37
|
+
*/
|
|
38
|
+
export declare const Button: React.ForwardRefExoticComponent<ButtonProps & React.RefAttributes<HTMLButtonElement>>;
|
|
39
|
+
//# sourceMappingURL=button.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"button.d.ts","sourceRoot":"","sources":["../../../src/components/button/button.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,OAAO,EAAW,KAAK,WAAW,EAAE,MAAM,WAAW,CAAC;AAEtD,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,aAAa,CAAC;AAE5E;;;GAGG;AACH,MAAM,MAAM,UAAU,GAClB,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,SAAS,GACT,SAAS,GACT,SAAS,GACT,SAAS,GACT,MAAM,CAAC;AA2BX,MAAM,WAAW,WAAY,SAAQ,KAAK,CAAC,oBAAoB,CAAC,iBAAiB,CAAC;IAChF,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,0FAA0F;IAC1F,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,+DAA+D;IAC/D,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,uDAAuD;IACvD,YAAY,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAChC;;;;OAIG;IACH,OAAO,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B;AAED;;;;;;GAMG;AACH,eAAO,MAAM,MAAM,uFAoEjB,CAAC"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { jsxs as _, jsx as n } from "react/jsx-runtime";
|
|
2
|
+
import { Loader2 as w } from "lucide-react";
|
|
3
|
+
import * as N from "react";
|
|
4
|
+
import { cn as L } from "../../lib/utils.js";
|
|
5
|
+
import { Tooltip as j } from "./tooltip.js";
|
|
6
|
+
const O = {
|
|
7
|
+
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
|
8
|
+
outline: "border border-border bg-transparent hover:bg-muted",
|
|
9
|
+
ghost: "hover:bg-muted",
|
|
10
|
+
destructive: "text-destructive hover:bg-destructive/10"
|
|
11
|
+
}, k = {
|
|
12
|
+
sm: "h-8 px-3 gap-1.5 text-xs [&_svg]:size-3.5",
|
|
13
|
+
md: "h-9 px-4 gap-2 text-sm [&_svg]:size-4",
|
|
14
|
+
lg: "h-11 px-6 gap-2.5 text-base [&_svg]:size-5",
|
|
15
|
+
"icon-sm": "size-8 [&_svg]:size-3.5",
|
|
16
|
+
"icon-md": "size-9 [&_svg]:size-4",
|
|
17
|
+
"icon-lg": "size-11 [&_svg]:size-5",
|
|
18
|
+
default: "h-9 px-4 gap-2 text-sm [&_svg]:size-4",
|
|
19
|
+
icon: "size-9 [&_svg]:size-4"
|
|
20
|
+
}, B = /* @__PURE__ */ new Set([
|
|
21
|
+
"icon-sm",
|
|
22
|
+
"icon-md",
|
|
23
|
+
"icon-lg",
|
|
24
|
+
"icon"
|
|
25
|
+
]), q = N.forwardRef(function({
|
|
26
|
+
className: u,
|
|
27
|
+
variant: g = "default",
|
|
28
|
+
size: o = "default",
|
|
29
|
+
type: b = "button",
|
|
30
|
+
loading: e = !1,
|
|
31
|
+
loadingText: i,
|
|
32
|
+
icon: f,
|
|
33
|
+
iconPosition: a = "left",
|
|
34
|
+
tooltip: s,
|
|
35
|
+
tooltipSide: v = "top",
|
|
36
|
+
disabled: p,
|
|
37
|
+
children: h,
|
|
38
|
+
...r
|
|
39
|
+
}, x) {
|
|
40
|
+
const c = B.has(o), d = e && !!i && !c, z = d ? i : h, t = e ? /* @__PURE__ */ n(w, { className: "animate-spin", "aria-hidden": "true" }) : f, l = c && typeof s == "string" && !r["aria-label"], y = r["aria-label"] ?? (l ? s : void 0), m = /* @__PURE__ */ _(
|
|
41
|
+
"button",
|
|
42
|
+
{
|
|
43
|
+
...r,
|
|
44
|
+
ref: x,
|
|
45
|
+
type: b,
|
|
46
|
+
"aria-label": y,
|
|
47
|
+
"aria-busy": e || void 0,
|
|
48
|
+
disabled: p || e,
|
|
49
|
+
className: L(
|
|
50
|
+
"inline-flex shrink-0 items-center justify-center rounded-lg font-medium whitespace-nowrap transition-colors outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:opacity-50 [&_svg]:shrink-0",
|
|
51
|
+
// A disabled button normally stops taking pointer events at all — but a
|
|
52
|
+
// tooltip is exactly what you want to read when a button is disabled or
|
|
53
|
+
// busy, so keep it hoverable in that case.
|
|
54
|
+
s ? "disabled:cursor-not-allowed" : "disabled:pointer-events-none",
|
|
55
|
+
O[g],
|
|
56
|
+
k[o],
|
|
57
|
+
u
|
|
58
|
+
),
|
|
59
|
+
children: [
|
|
60
|
+
t && a === "left" && t,
|
|
61
|
+
z,
|
|
62
|
+
t && a === "right" && t,
|
|
63
|
+
e && !d && /* @__PURE__ */ n("span", { className: "sr-only", role: "status", children: i ?? "Loading" })
|
|
64
|
+
]
|
|
65
|
+
}
|
|
66
|
+
);
|
|
67
|
+
return s ? /* @__PURE__ */ n(j, { content: s, side: v, describes: !l, children: m }) : m;
|
|
68
|
+
});
|
|
69
|
+
export {
|
|
70
|
+
q as Button
|
|
71
|
+
};
|
|
72
|
+
//# sourceMappingURL=button.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"button.js","sources":["../../../src/components/button/button.tsx"],"sourcesContent":["import { Loader2 } from 'lucide-react';\nimport * as React from 'react';\n\nimport { cn } from '../../lib/utils';\nimport { Tooltip, type TooltipSide } from './tooltip';\n\nexport type ButtonVariant = 'default' | 'outline' | 'ghost' | 'destructive';\n\n/**\n * Three text tiers (`sm` / `md` / `lg`) plus a square icon-only tier for each.\n * `default` and `icon` are kept as aliases of `md` / `icon-md`.\n */\nexport type ButtonSize =\n | 'sm'\n | 'md'\n | 'lg'\n | 'icon-sm'\n | 'icon-md'\n | 'icon-lg'\n | 'default'\n | 'icon';\n\nconst variants: Record<ButtonVariant, string> = {\n default: 'bg-primary text-primary-foreground hover:bg-primary/90',\n outline: 'border border-border bg-transparent hover:bg-muted',\n ghost: 'hover:bg-muted',\n destructive: 'text-destructive hover:bg-destructive/10',\n};\n\nconst sizes: Record<ButtonSize, string> = {\n sm: 'h-8 px-3 gap-1.5 text-xs [&_svg]:size-3.5',\n md: 'h-9 px-4 gap-2 text-sm [&_svg]:size-4',\n lg: 'h-11 px-6 gap-2.5 text-base [&_svg]:size-5',\n 'icon-sm': 'size-8 [&_svg]:size-3.5',\n 'icon-md': 'size-9 [&_svg]:size-4',\n 'icon-lg': 'size-11 [&_svg]:size-5',\n default: 'h-9 px-4 gap-2 text-sm [&_svg]:size-4',\n icon: 'size-9 [&_svg]:size-4',\n};\n\nconst ICON_ONLY: ReadonlySet<ButtonSize> = new Set<ButtonSize>([\n 'icon-sm',\n 'icon-md',\n 'icon-lg',\n 'icon',\n]);\n\nexport interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n variant?: ButtonVariant;\n size?: ButtonSize;\n /**\n * Swap the leading icon for a spinner, disable the button and mark it\n * `aria-busy`. The label stays put so the button does not resize.\n */\n loading?: boolean;\n /** Announced (and shown, on a text-tier button) in place of the label while `loading`. */\n loadingText?: string;\n /** Leading icon — or the only content, on an `icon-*` tier. */\n icon?: React.ReactNode;\n /** Put `icon` after the label instead of before it. */\n iconPosition?: 'left' | 'right';\n /**\n * Hover/focus hint. On an icon-only button it also becomes the accessible\n * name when no `aria-label` is given; otherwise it is wired up as\n * `aria-describedby`.\n */\n tooltip?: React.ReactNode;\n tooltipSide?: TooltipSide;\n}\n\n/**\n * The design-system button: four variants, three size tiers (icon-only twins of\n * each), an icon slot, a loading state and an optional tooltip.\n *\n * `type` defaults to `\"button\"` so dropping one inside a form never submits it\n * by accident — pass `type=\"submit\"` when that is what you want.\n */\nexport const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(function Button(\n {\n className,\n variant = 'default',\n size = 'default',\n type = 'button',\n loading = false,\n loadingText,\n icon,\n iconPosition = 'left',\n tooltip,\n tooltipSide = 'top',\n disabled,\n children,\n ...props\n },\n ref,\n) {\n const iconOnly = ICON_ONLY.has(size);\n const showsLoadingText = loading && !!loadingText && !iconOnly;\n const label = showsLoadingText ? loadingText : children;\n const glyph = loading ? <Loader2 className=\"animate-spin\" aria-hidden=\"true\" /> : icon;\n\n // An icon-only button has no text to name it: fall back to the tooltip when\n // it is a plain string, so `<Button size=\"icon\" icon={<X />} tooltip=\"Close\" />`\n // is announced as \"Close\" rather than as an unlabelled button. When it serves\n // as the name it must not also describe the button — that reads it twice.\n const tooltipIsName = iconOnly && typeof tooltip === 'string' && !props['aria-label'];\n const ariaLabel = props['aria-label'] ?? (tooltipIsName ? (tooltip as string) : undefined);\n\n const button = (\n <button\n {...props}\n ref={ref}\n type={type}\n aria-label={ariaLabel}\n aria-busy={loading || undefined}\n disabled={disabled || loading}\n className={cn(\n 'inline-flex shrink-0 items-center justify-center rounded-lg font-medium whitespace-nowrap transition-colors outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:opacity-50 [&_svg]:shrink-0',\n // A disabled button normally stops taking pointer events at all — but a\n // tooltip is exactly what you want to read when a button is disabled or\n // busy, so keep it hoverable in that case.\n tooltip ? 'disabled:cursor-not-allowed' : 'disabled:pointer-events-none',\n variants[variant],\n sizes[size],\n className,\n )}\n >\n {glyph && iconPosition === 'left' && glyph}\n {label}\n {glyph && iconPosition === 'right' && glyph}\n {/* Announce the busy state — unless the visible label already says it. */}\n {loading && !showsLoadingText && (\n <span className=\"sr-only\" role=\"status\">\n {loadingText ?? 'Loading'}\n </span>\n )}\n </button>\n );\n\n if (!tooltip) return button;\n\n return (\n <Tooltip content={tooltip} side={tooltipSide} describes={!tooltipIsName}>\n {button}\n </Tooltip>\n );\n});\n"],"names":["variants","sizes","ICON_ONLY","Button","React","className","variant","size","type","loading","loadingText","icon","iconPosition","tooltip","tooltipSide","disabled","children","props","ref","iconOnly","showsLoadingText","label","glyph","jsx","Loader2","tooltipIsName","ariaLabel","button","jsxs","cn","Tooltip"],"mappings":";;;;;AAsBA,MAAMA,IAA0C;AAAA,EAC9C,SAAS;AAAA,EACT,SAAS;AAAA,EACT,OAAO;AAAA,EACP,aAAa;AACf,GAEMC,IAAoC;AAAA,EACxC,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,WAAW;AAAA,EACX,WAAW;AAAA,EACX,WAAW;AAAA,EACX,SAAS;AAAA,EACT,MAAM;AACR,GAEMC,wBAAyC,IAAgB;AAAA,EAC7D;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC,GAgCYC,IAASC,EAAM,WAA2C,SACrE;AAAA,EACE,WAAAC;AAAA,EACA,SAAAC,IAAU;AAAA,EACV,MAAAC,IAAO;AAAA,EACP,MAAAC,IAAO;AAAA,EACP,SAAAC,IAAU;AAAA,EACV,aAAAC;AAAA,EACA,MAAAC;AAAA,EACA,cAAAC,IAAe;AAAA,EACf,SAAAC;AAAA,EACA,aAAAC,IAAc;AAAA,EACd,UAAAC;AAAA,EACA,UAAAC;AAAA,EACA,GAAGC;AACL,GACAC,GACA;AACA,QAAMC,IAAWjB,EAAU,IAAIK,CAAI,GAC7Ba,IAAmBX,KAAW,CAAC,CAACC,KAAe,CAACS,GAChDE,IAAQD,IAAmBV,IAAcM,GACzCM,IAAQb,IAAU,gBAAAc,EAACC,GAAA,EAAQ,WAAU,gBAAe,eAAY,QAAO,IAAKb,GAM5Ec,IAAgBN,KAAY,OAAON,KAAY,YAAY,CAACI,EAAM,YAAY,GAC9ES,IAAYT,EAAM,YAAY,MAAMQ,IAAiBZ,IAAqB,SAE1Ec,IACJ,gBAAAC;AAAA,IAAC;AAAA,IAAA;AAAA,MACE,GAAGX;AAAA,MACJ,KAAAC;AAAA,MACA,MAAAV;AAAA,MACA,cAAYkB;AAAA,MACZ,aAAWjB,KAAW;AAAA,MACtB,UAAUM,KAAYN;AAAA,MACtB,WAAWoB;AAAA,QACT;AAAA;AAAA;AAAA;AAAA,QAIAhB,IAAU,gCAAgC;AAAA,QAC1Cb,EAASM,CAAO;AAAA,QAChBL,EAAMM,CAAI;AAAA,QACVF;AAAA,MAAA;AAAA,MAGD,UAAA;AAAA,QAAAiB,KAASV,MAAiB,UAAUU;AAAA,QACpCD;AAAA,QACAC,KAASV,MAAiB,WAAWU;AAAA,QAErCb,KAAW,CAACW,KACX,gBAAAG,EAAC,QAAA,EAAK,WAAU,WAAU,MAAK,UAC5B,UAAAb,KAAe,UAAA,CAClB;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAKN,SAAKG,IAGH,gBAAAU,EAACO,KAAQ,SAASjB,GAAS,MAAMC,GAAa,WAAW,CAACW,GACvD,UAAAE,EAAA,CACH,IALmBA;AAOvB,CAAC;"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/button/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACvE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
export type TooltipSide = 'top' | 'bottom' | 'left' | 'right';
|
|
3
|
+
export interface TooltipProps {
|
|
4
|
+
content: React.ReactNode;
|
|
5
|
+
side?: TooltipSide;
|
|
6
|
+
/**
|
|
7
|
+
* Wire the bubble to the trigger with `aria-describedby`. Pass `false` when the
|
|
8
|
+
* tooltip text already *is* the trigger's accessible name (an icon-only button
|
|
9
|
+
* that took its `aria-label` from it) — a screen reader would otherwise read it
|
|
10
|
+
* twice.
|
|
11
|
+
*/
|
|
12
|
+
describes?: boolean;
|
|
13
|
+
children: React.ReactElement<{
|
|
14
|
+
ref?: React.Ref<HTMLElement>;
|
|
15
|
+
'aria-describedby'?: string;
|
|
16
|
+
onPointerEnter?: React.PointerEventHandler;
|
|
17
|
+
onPointerLeave?: React.PointerEventHandler;
|
|
18
|
+
onFocus?: React.FocusEventHandler;
|
|
19
|
+
onBlur?: React.FocusEventHandler;
|
|
20
|
+
}>;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* A hover/focus hint for a single trigger element.
|
|
24
|
+
*
|
|
25
|
+
* Rendered in a portal and positioned from the trigger's box, so it escapes any
|
|
26
|
+
* `overflow: hidden` ancestor without a wrapper element distorting the layout.
|
|
27
|
+
* Follows the WAI-ARIA tooltip pattern: `role="tooltip"`, shown on hover *and*
|
|
28
|
+
* keyboard focus, dismissed with Escape.
|
|
29
|
+
*/
|
|
30
|
+
export declare function Tooltip({ content, side, describes, children }: TooltipProps): React.JSX.Element;
|
|
31
|
+
//# sourceMappingURL=tooltip.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tooltip.d.ts","sourceRoot":"","sources":["../../../src/components/button/tooltip.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,MAAM,MAAM,WAAW,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;AAwB9D,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC;IACzB,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,EAAE,KAAK,CAAC,YAAY,CAAC;QAC3B,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QAC7B,kBAAkB,CAAC,EAAE,MAAM,CAAC;QAC5B,cAAc,CAAC,EAAE,KAAK,CAAC,mBAAmB,CAAC;QAC3C,cAAc,CAAC,EAAE,KAAK,CAAC,mBAAmB,CAAC;QAC3C,OAAO,CAAC,EAAE,KAAK,CAAC,iBAAiB,CAAC;QAClC,MAAM,CAAC,EAAE,KAAK,CAAC,iBAAiB,CAAC;KAClC,CAAC,CAAC;CACJ;AAED;;;;;;;GAOG;AACH,wBAAgB,OAAO,CAAC,EAAE,OAAO,EAAE,IAAY,EAAE,SAAgB,EAAE,QAAQ,EAAE,EAAE,YAAY,qBAsF1F"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { jsxs as b, Fragment as y, jsx as h } from "react/jsx-runtime";
|
|
2
|
+
import * as r from "react";
|
|
3
|
+
import { createPortal as E } from "react-dom";
|
|
4
|
+
const a = 8, L = {
|
|
5
|
+
top: "translate(-50%, -100%)",
|
|
6
|
+
bottom: "translate(-50%, 0)",
|
|
7
|
+
left: "translate(-100%, -50%)",
|
|
8
|
+
right: "translate(0, -50%)"
|
|
9
|
+
};
|
|
10
|
+
function P(o, c) {
|
|
11
|
+
switch (c) {
|
|
12
|
+
case "bottom":
|
|
13
|
+
return { top: o.bottom + a, left: o.left + o.width / 2 };
|
|
14
|
+
case "left":
|
|
15
|
+
return { top: o.top + o.height / 2, left: o.left - a };
|
|
16
|
+
case "right":
|
|
17
|
+
return { top: o.top + o.height / 2, left: o.right + a };
|
|
18
|
+
default:
|
|
19
|
+
return { top: o.top - a, left: o.left + o.width / 2 };
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function k({ content: o, side: c = "top", describes: w = !0, children: v }) {
|
|
23
|
+
const f = r.useId(), p = r.useRef(null), u = r.useRef(void 0), [l, d] = r.useState(null), i = r.useCallback(() => {
|
|
24
|
+
clearTimeout(u.current), d(null);
|
|
25
|
+
}, []), m = r.useCallback((e) => {
|
|
26
|
+
clearTimeout(u.current), u.current = setTimeout(() => {
|
|
27
|
+
var n;
|
|
28
|
+
const t = (n = p.current) == null ? void 0 : n.getBoundingClientRect();
|
|
29
|
+
t && d(P(t, c));
|
|
30
|
+
}, e);
|
|
31
|
+
}, [c]);
|
|
32
|
+
r.useEffect(() => () => clearTimeout(u.current), []), r.useEffect(() => {
|
|
33
|
+
if (!l) return;
|
|
34
|
+
const e = (t) => {
|
|
35
|
+
t.key === "Escape" && i();
|
|
36
|
+
};
|
|
37
|
+
return document.addEventListener("keydown", e), window.addEventListener("scroll", i, !0), window.addEventListener("resize", i), () => {
|
|
38
|
+
document.removeEventListener("keydown", e), window.removeEventListener("scroll", i, !0), window.removeEventListener("resize", i);
|
|
39
|
+
};
|
|
40
|
+
}, [l, i]);
|
|
41
|
+
const s = v, g = r.cloneElement(s, {
|
|
42
|
+
ref: (e) => {
|
|
43
|
+
p.current = e;
|
|
44
|
+
const t = s.ref;
|
|
45
|
+
typeof t == "function" ? t(e) : t && typeof t == "object" && (t.current = e);
|
|
46
|
+
},
|
|
47
|
+
"aria-describedby": l && w ? f : s.props["aria-describedby"],
|
|
48
|
+
onPointerEnter: (e) => {
|
|
49
|
+
var t, n;
|
|
50
|
+
(n = (t = s.props).onPointerEnter) == null || n.call(t, e), e.pointerType !== "touch" && m(150);
|
|
51
|
+
},
|
|
52
|
+
onPointerLeave: (e) => {
|
|
53
|
+
var t, n;
|
|
54
|
+
(n = (t = s.props).onPointerLeave) == null || n.call(t, e), i();
|
|
55
|
+
},
|
|
56
|
+
onFocus: (e) => {
|
|
57
|
+
var t, n;
|
|
58
|
+
(n = (t = s.props).onFocus) == null || n.call(t, e), e.target.matches(":focus-visible") && m(0);
|
|
59
|
+
},
|
|
60
|
+
onBlur: (e) => {
|
|
61
|
+
var t, n;
|
|
62
|
+
(n = (t = s.props).onBlur) == null || n.call(t, e), i();
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
return /* @__PURE__ */ b(y, { children: [
|
|
66
|
+
g,
|
|
67
|
+
l && typeof document < "u" && E(
|
|
68
|
+
/* @__PURE__ */ h(
|
|
69
|
+
"div",
|
|
70
|
+
{
|
|
71
|
+
className: "ds-tooltip-anchor",
|
|
72
|
+
style: { top: l.top, left: l.left, transform: L[c] },
|
|
73
|
+
children: /* @__PURE__ */ h("span", { id: f, role: "tooltip", className: "ds-tooltip", children: o })
|
|
74
|
+
}
|
|
75
|
+
),
|
|
76
|
+
document.body
|
|
77
|
+
)
|
|
78
|
+
] });
|
|
79
|
+
}
|
|
80
|
+
export {
|
|
81
|
+
k as Tooltip
|
|
82
|
+
};
|
|
83
|
+
//# sourceMappingURL=tooltip.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tooltip.js","sources":["../../../src/components/button/tooltip.tsx"],"sourcesContent":["import * as React from 'react';\nimport { createPortal } from 'react-dom';\n\nexport type TooltipSide = 'top' | 'bottom' | 'left' | 'right';\n\nconst GAP = 8;\n\nconst TRANSFORMS: Record<TooltipSide, string> = {\n top: 'translate(-50%, -100%)',\n bottom: 'translate(-50%, 0)',\n left: 'translate(-100%, -50%)',\n right: 'translate(0, -50%)',\n};\n\nfunction anchorPoint(rect: DOMRect, side: TooltipSide) {\n switch (side) {\n case 'bottom':\n return { top: rect.bottom + GAP, left: rect.left + rect.width / 2 };\n case 'left':\n return { top: rect.top + rect.height / 2, left: rect.left - GAP };\n case 'right':\n return { top: rect.top + rect.height / 2, left: rect.right + GAP };\n default:\n return { top: rect.top - GAP, left: rect.left + rect.width / 2 };\n }\n}\n\nexport interface TooltipProps {\n content: React.ReactNode;\n side?: TooltipSide;\n /**\n * Wire the bubble to the trigger with `aria-describedby`. Pass `false` when the\n * tooltip text already *is* the trigger's accessible name (an icon-only button\n * that took its `aria-label` from it) — a screen reader would otherwise read it\n * twice.\n */\n describes?: boolean;\n children: React.ReactElement<{\n ref?: React.Ref<HTMLElement>;\n 'aria-describedby'?: string;\n onPointerEnter?: React.PointerEventHandler;\n onPointerLeave?: React.PointerEventHandler;\n onFocus?: React.FocusEventHandler;\n onBlur?: React.FocusEventHandler;\n }>;\n}\n\n/**\n * A hover/focus hint for a single trigger element.\n *\n * Rendered in a portal and positioned from the trigger's box, so it escapes any\n * `overflow: hidden` ancestor without a wrapper element distorting the layout.\n * Follows the WAI-ARIA tooltip pattern: `role=\"tooltip\"`, shown on hover *and*\n * keyboard focus, dismissed with Escape.\n */\nexport function Tooltip({ content, side = 'top', describes = true, children }: TooltipProps) {\n const id = React.useId();\n const triggerRef = React.useRef<HTMLElement | null>(null);\n const timer = React.useRef<ReturnType<typeof setTimeout> | undefined>(undefined);\n const [point, setPoint] = React.useState<{ top: number; left: number } | null>(null);\n\n const hide = React.useCallback(() => {\n clearTimeout(timer.current);\n setPoint(null);\n }, []);\n\n const show = React.useCallback((delay: number) => {\n clearTimeout(timer.current);\n timer.current = setTimeout(() => {\n const rect = triggerRef.current?.getBoundingClientRect();\n if (rect) setPoint(anchorPoint(rect, side));\n }, delay);\n }, [side]);\n\n React.useEffect(() => () => clearTimeout(timer.current), []);\n\n // While it is up: Escape dismisses it, and any scroll or resize invalidates the\n // point it was measured from, so drop it rather than let it drift.\n React.useEffect(() => {\n if (!point) return;\n const onKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'Escape') hide();\n };\n document.addEventListener('keydown', onKeyDown);\n window.addEventListener('scroll', hide, true);\n window.addEventListener('resize', hide);\n return () => {\n document.removeEventListener('keydown', onKeyDown);\n window.removeEventListener('scroll', hide, true);\n window.removeEventListener('resize', hide);\n };\n }, [point, hide]);\n\n const child = children;\n const trigger = React.cloneElement(child, {\n ref: (node: HTMLElement | null) => {\n triggerRef.current = node;\n const ref = (child as unknown as { ref?: React.Ref<HTMLElement> }).ref;\n if (typeof ref === 'function') ref(node);\n else if (ref && typeof ref === 'object') (ref as React.RefObject<HTMLElement | null>).current = node;\n },\n 'aria-describedby': point && describes ? id : child.props['aria-describedby'],\n onPointerEnter: (e: React.PointerEvent) => {\n child.props.onPointerEnter?.(e);\n // Touch already fires a click; a hover bubble there is just noise.\n if (e.pointerType !== 'touch') show(150);\n },\n onPointerLeave: (e: React.PointerEvent) => {\n child.props.onPointerLeave?.(e);\n hide();\n },\n onFocus: (e: React.FocusEvent) => {\n child.props.onFocus?.(e);\n // Keyboard focus only — a mouse click focuses too, and the hover bubble\n // that is already up should not be pinned open by it.\n if (e.target.matches(':focus-visible')) show(0);\n },\n onBlur: (e: React.FocusEvent) => {\n child.props.onBlur?.(e);\n hide();\n },\n });\n\n return (\n <>\n {trigger}\n {point &&\n typeof document !== 'undefined' &&\n createPortal(\n <div\n className=\"ds-tooltip-anchor\"\n style={{ top: point.top, left: point.left, transform: TRANSFORMS[side] }}\n >\n <span id={id} role=\"tooltip\" className=\"ds-tooltip\">\n {content}\n </span>\n </div>,\n document.body,\n )}\n </>\n );\n}\n"],"names":["GAP","TRANSFORMS","anchorPoint","rect","side","Tooltip","content","describes","children","id","React","triggerRef","timer","point","setPoint","hide","show","delay","_a","onKeyDown","e","child","trigger","node","ref","_b","jsxs","Fragment","createPortal","jsx"],"mappings":";;;AAKA,MAAMA,IAAM,GAENC,IAA0C;AAAA,EAC9C,KAAK;AAAA,EACL,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,OAAO;AACT;AAEA,SAASC,EAAYC,GAAeC,GAAmB;AACrD,UAAQA,GAAA;AAAA,IACN,KAAK;AACH,aAAO,EAAE,KAAKD,EAAK,SAASH,GAAK,MAAMG,EAAK,OAAOA,EAAK,QAAQ,EAAA;AAAA,IAClE,KAAK;AACH,aAAO,EAAE,KAAKA,EAAK,MAAMA,EAAK,SAAS,GAAG,MAAMA,EAAK,OAAOH,EAAA;AAAA,IAC9D,KAAK;AACH,aAAO,EAAE,KAAKG,EAAK,MAAMA,EAAK,SAAS,GAAG,MAAMA,EAAK,QAAQH,EAAA;AAAA,IAC/D;AACE,aAAO,EAAE,KAAKG,EAAK,MAAMH,GAAK,MAAMG,EAAK,OAAOA,EAAK,QAAQ,EAAA;AAAA,EAAE;AAErE;AA8BO,SAASE,EAAQ,EAAE,SAAAC,GAAS,MAAAF,IAAO,OAAO,WAAAG,IAAY,IAAM,UAAAC,KAA0B;AAC3F,QAAMC,IAAKC,EAAM,MAAA,GACXC,IAAaD,EAAM,OAA2B,IAAI,GAClDE,IAAQF,EAAM,OAAkD,MAAS,GACzE,CAACG,GAAOC,CAAQ,IAAIJ,EAAM,SAA+C,IAAI,GAE7EK,IAAOL,EAAM,YAAY,MAAM;AACnC,iBAAaE,EAAM,OAAO,GAC1BE,EAAS,IAAI;AAAA,EACf,GAAG,CAAA,CAAE,GAECE,IAAON,EAAM,YAAY,CAACO,MAAkB;AAChD,iBAAaL,EAAM,OAAO,GAC1BA,EAAM,UAAU,WAAW,MAAM;;AAC/B,YAAMT,KAAOe,IAAAP,EAAW,YAAX,gBAAAO,EAAoB;AACjC,MAAIf,KAAMW,EAASZ,EAAYC,GAAMC,CAAI,CAAC;AAAA,IAC5C,GAAGa,CAAK;AAAA,EACV,GAAG,CAACb,CAAI,CAAC;AAET,EAAAM,EAAM,UAAU,MAAM,MAAM,aAAaE,EAAM,OAAO,GAAG,EAAE,GAI3DF,EAAM,UAAU,MAAM;AACpB,QAAI,CAACG,EAAO;AACZ,UAAMM,IAAY,CAACC,MAAqB;AACtC,MAAIA,EAAE,QAAQ,YAAUL,EAAA;AAAA,IAC1B;AACA,oBAAS,iBAAiB,WAAWI,CAAS,GAC9C,OAAO,iBAAiB,UAAUJ,GAAM,EAAI,GAC5C,OAAO,iBAAiB,UAAUA,CAAI,GAC/B,MAAM;AACX,eAAS,oBAAoB,WAAWI,CAAS,GACjD,OAAO,oBAAoB,UAAUJ,GAAM,EAAI,GAC/C,OAAO,oBAAoB,UAAUA,CAAI;AAAA,IAC3C;AAAA,EACF,GAAG,CAACF,GAAOE,CAAI,CAAC;AAEhB,QAAMM,IAAQb,GACRc,IAAUZ,EAAM,aAAaW,GAAO;AAAA,IACxC,KAAK,CAACE,MAA6B;AACjC,MAAAZ,EAAW,UAAUY;AACrB,YAAMC,IAAOH,EAAsD;AACnE,MAAI,OAAOG,KAAQ,aAAYA,EAAID,CAAI,IAC9BC,KAAO,OAAOA,KAAQ,aAAWA,EAA4C,UAAUD;AAAA,IAClG;AAAA,IACA,oBAAoBV,KAASN,IAAYE,IAAKY,EAAM,MAAM,kBAAkB;AAAA,IAC5E,gBAAgB,CAAC,MAA0B;;AACzC,OAAAI,KAAAP,IAAAG,EAAM,OAAM,mBAAZ,QAAAI,EAAA,KAAAP,GAA6B,IAEzB,EAAE,gBAAgB,WAASF,EAAK,GAAG;AAAA,IACzC;AAAA,IACA,gBAAgB,CAAC,MAA0B;;AACzC,OAAAS,KAAAP,IAAAG,EAAM,OAAM,mBAAZ,QAAAI,EAAA,KAAAP,GAA6B,IAC7BH,EAAA;AAAA,IACF;AAAA,IACA,SAAS,CAAC,MAAwB;;AAChC,OAAAU,KAAAP,IAAAG,EAAM,OAAM,YAAZ,QAAAI,EAAA,KAAAP,GAAsB,IAGlB,EAAE,OAAO,QAAQ,gBAAgB,OAAQ,CAAC;AAAA,IAChD;AAAA,IACA,QAAQ,CAAC,MAAwB;;AAC/B,OAAAO,KAAAP,IAAAG,EAAM,OAAM,WAAZ,QAAAI,EAAA,KAAAP,GAAqB,IACrBH,EAAA;AAAA,IACF;AAAA,EAAA,CACD;AAED,SACE,gBAAAW,EAAAC,GAAA,EACG,UAAA;AAAA,IAAAL;AAAA,IACAT,KACC,OAAO,WAAa,OACpBe;AAAA,MACE,gBAAAC;AAAA,QAAC;AAAA,QAAA;AAAA,UACC,WAAU;AAAA,UACV,OAAO,EAAE,KAAKhB,EAAM,KAAK,MAAMA,EAAM,MAAM,WAAWZ,EAAWG,CAAI,EAAA;AAAA,UAErE,4BAAC,QAAA,EAAK,IAAAK,GAAQ,MAAK,WAAU,WAAU,cACpC,UAAAH,EAAA,CACH;AAAA,QAAA;AAAA,MAAA;AAAA,MAEF,SAAS;AAAA,IAAA;AAAA,EACX,GACJ;AAEJ;"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
export interface ChangelogEntry {
|
|
3
|
+
version: string;
|
|
4
|
+
date?: string;
|
|
5
|
+
title?: string;
|
|
6
|
+
changes: string[];
|
|
7
|
+
sha?: string;
|
|
8
|
+
}
|
|
9
|
+
export interface ChangelogProps {
|
|
10
|
+
/**
|
|
11
|
+
* Supply entries directly (controlled). When set, the hosted SDK is not
|
|
12
|
+
* loaded — pair with `onLoadMore` / `hasMore` / `loading` to page a long
|
|
13
|
+
* history. Omit to auto-load from the changelog-widget SDK.
|
|
14
|
+
*/
|
|
15
|
+
entries?: ChangelogEntry[];
|
|
16
|
+
/** URL of the JSONL changelog (SDK mode). Default: /changelog.jsonl */
|
|
17
|
+
url?: string;
|
|
18
|
+
/** URL of the SDK script (SDK mode). Default: the hosted changelog script. */
|
|
19
|
+
sdkUrl?: string;
|
|
20
|
+
/** Modal heading. Default: "Changelog". */
|
|
21
|
+
title?: string;
|
|
22
|
+
/**
|
|
23
|
+
* Custom trigger. Receives `open` (show the modal) and `hasUpdate` (a newer
|
|
24
|
+
* version is available). If omitted, a plain "Changelog" text button renders.
|
|
25
|
+
*/
|
|
26
|
+
trigger?: (args: {
|
|
27
|
+
open: () => void;
|
|
28
|
+
hasUpdate: boolean;
|
|
29
|
+
}) => ReactNode;
|
|
30
|
+
/** Controlled mode: append the next page (used by the virtual list's tail). */
|
|
31
|
+
onLoadMore?: () => void;
|
|
32
|
+
/** Controlled mode: more pages remain. */
|
|
33
|
+
hasMore?: boolean;
|
|
34
|
+
/** Controlled mode: a page load is in flight. */
|
|
35
|
+
loading?: boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Controlled "new version" reload toast. Set to an entry to show the toast
|
|
38
|
+
* for it — useful when you detect an update yourself (e.g. in controlled
|
|
39
|
+
* mode, where the SDK watcher isn't running). Set to `null`/omit to hide it.
|
|
40
|
+
*/
|
|
41
|
+
newVersion?: ChangelogEntry | null;
|
|
42
|
+
/** Called when the user dismisses the "new version" toast. */
|
|
43
|
+
onDismissNewVersion?: () => void;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* A changelog trigger + modal + "new version" reload toast, styled from the
|
|
47
|
+
* design tokens. The entry list is a {@link VirtualList}, so a long history
|
|
48
|
+
* scrolls cheaply and can lazily page in via `onLoadMore`. Data comes from the
|
|
49
|
+
* headless changelog-widget SDK by default, or pass `entries` directly.
|
|
50
|
+
*/
|
|
51
|
+
export declare function Changelog({ entries, url, sdkUrl, title, trigger, onLoadMore, hasMore, loading, newVersion, onDismissNewVersion, }: ChangelogProps): import("react").JSX.Element;
|
|
52
|
+
//# sourceMappingURL=changelog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"changelog.d.ts","sourceRoot":"","sources":["../../../src/components/changelog/changelog.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA+B,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAQpE,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AA2CD,MAAM,WAAW,cAAc;IAC7B;;;;OAIG;IACH,OAAO,CAAC,EAAE,cAAc,EAAE,CAAC;IAC3B,uEAAuE;IACvE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,8EAA8E;IAC9E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,2CAA2C;IAC3C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,IAAI,CAAC;QAAC,SAAS,EAAE,OAAO,CAAA;KAAE,KAAK,SAAS,CAAC;IACxE,+EAA+E;IAC/E,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;IACxB,0CAA0C;IAC1C,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,iDAAiD;IACjD,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;OAIG;IACH,UAAU,CAAC,EAAE,cAAc,GAAG,IAAI,CAAC;IACnC,8DAA8D;IAC9D,mBAAmB,CAAC,EAAE,MAAM,IAAI,CAAC;CAClC;AAED;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,EACxB,OAAO,EACP,GAAuB,EACvB,MAAgB,EAChB,KAAmB,EACnB,OAAO,EACP,UAAU,EACV,OAAe,EACf,OAAe,EACf,UAAiB,EACjB,mBAAmB,GACpB,EAAE,cAAc,+BAqIhB"}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { jsxs as n, Fragment as z, jsx as e } from "react/jsx-runtime";
|
|
2
|
+
import { useState as b, useRef as j, useEffect as k } from "react";
|
|
3
|
+
import { X as A, Sparkles as T } from "lucide-react";
|
|
4
|
+
import { VirtualList as D } from "../virtual-list/virtual-list.js";
|
|
5
|
+
const F = "https://changelog-widget.dev.gabvdl.xyz/script.js", G = "/changelog.jsonl";
|
|
6
|
+
function v() {
|
|
7
|
+
return window.Changelog;
|
|
8
|
+
}
|
|
9
|
+
function I(t) {
|
|
10
|
+
const o = v();
|
|
11
|
+
return o ? Promise.resolve(o) : new Promise((a, s) => {
|
|
12
|
+
const c = () => {
|
|
13
|
+
const d = v();
|
|
14
|
+
d ? a(d) : s(new Error("SDK missing"));
|
|
15
|
+
}, i = document.querySelector(`script[src="${t}"]`);
|
|
16
|
+
if (i) {
|
|
17
|
+
i.addEventListener("load", c, { once: !0 }), i.addEventListener("error", () => s(new Error("SDK failed")), { once: !0 });
|
|
18
|
+
const d = v();
|
|
19
|
+
d && a(d);
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
const l = document.createElement("script");
|
|
23
|
+
l.src = t, l.async = !0, l.addEventListener("load", c, { once: !0 }), l.addEventListener("error", () => s(new Error("SDK failed")), { once: !0 }), document.head.appendChild(l);
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
function X({
|
|
27
|
+
entries: t,
|
|
28
|
+
url: o = G,
|
|
29
|
+
sdkUrl: a = F,
|
|
30
|
+
title: s = "Changelog",
|
|
31
|
+
trigger: c,
|
|
32
|
+
onLoadMore: i,
|
|
33
|
+
hasMore: l = !1,
|
|
34
|
+
loading: d = !1,
|
|
35
|
+
newVersion: C = null,
|
|
36
|
+
onDismissNewVersion: f
|
|
37
|
+
}) {
|
|
38
|
+
const h = t !== void 0, [L, y] = b([]), [x, m] = b(!1), [S, N] = b(null), w = j(() => {
|
|
39
|
+
}), K = h ? t : L;
|
|
40
|
+
k(() => {
|
|
41
|
+
if (h) return;
|
|
42
|
+
let r = !0;
|
|
43
|
+
return I(a).then((p) => {
|
|
44
|
+
r && (p.fetch(o).then((g) => r && y(g)), w.current = p.watch({
|
|
45
|
+
url: o,
|
|
46
|
+
onUpdate: (g, R) => {
|
|
47
|
+
r && (y(R), N(g));
|
|
48
|
+
}
|
|
49
|
+
}));
|
|
50
|
+
}).catch(() => {
|
|
51
|
+
}), () => {
|
|
52
|
+
r = !1, w.current();
|
|
53
|
+
};
|
|
54
|
+
}, [h, o, a]), k(() => {
|
|
55
|
+
if (!x) return;
|
|
56
|
+
const r = (p) => p.key === "Escape" && m(!1);
|
|
57
|
+
return document.addEventListener("keydown", r), () => document.removeEventListener("keydown", r);
|
|
58
|
+
}, [x]);
|
|
59
|
+
const u = C ?? S, E = u !== null, U = () => {
|
|
60
|
+
N(null), f == null || f();
|
|
61
|
+
};
|
|
62
|
+
return /* @__PURE__ */ n(z, { children: [
|
|
63
|
+
c ? c({ open: () => m(!0), hasUpdate: E }) : /* @__PURE__ */ n(
|
|
64
|
+
"button",
|
|
65
|
+
{
|
|
66
|
+
type: "button",
|
|
67
|
+
onClick: () => m(!0),
|
|
68
|
+
className: "relative inline-flex items-center text-sm font-medium text-muted-foreground transition-colors hover:text-foreground",
|
|
69
|
+
children: [
|
|
70
|
+
"Changelog",
|
|
71
|
+
E && /* @__PURE__ */ e("span", { className: "ml-1.5 h-1.5 w-1.5 rounded-full bg-primary", "aria-hidden": !0 })
|
|
72
|
+
]
|
|
73
|
+
}
|
|
74
|
+
),
|
|
75
|
+
x && /* @__PURE__ */ e(
|
|
76
|
+
"div",
|
|
77
|
+
{
|
|
78
|
+
className: "fixed inset-0 z-[9999] flex items-center justify-center bg-black/50 p-4 backdrop-blur-sm",
|
|
79
|
+
onClick: (r) => r.target === r.currentTarget && m(!1),
|
|
80
|
+
children: /* @__PURE__ */ n("div", { className: "flex max-h-[80vh] w-full max-w-md flex-col overflow-hidden rounded-2xl border border-border bg-card shadow-2xl", children: [
|
|
81
|
+
/* @__PURE__ */ n("div", { className: "flex items-center gap-2 border-b border-border px-5 py-4", children: [
|
|
82
|
+
/* @__PURE__ */ e("h2", { className: "flex-1 text-base font-bold text-foreground", children: s }),
|
|
83
|
+
/* @__PURE__ */ e(
|
|
84
|
+
"button",
|
|
85
|
+
{
|
|
86
|
+
type: "button",
|
|
87
|
+
onClick: () => m(!1),
|
|
88
|
+
"aria-label": "Close",
|
|
89
|
+
className: "grid h-8 w-8 place-items-center rounded-lg text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
|
|
90
|
+
children: /* @__PURE__ */ e(A, { className: "h-4 w-4" })
|
|
91
|
+
}
|
|
92
|
+
)
|
|
93
|
+
] }),
|
|
94
|
+
/* @__PURE__ */ e(
|
|
95
|
+
D,
|
|
96
|
+
{
|
|
97
|
+
items: K,
|
|
98
|
+
className: "min-h-0 flex-1 px-5",
|
|
99
|
+
estimateSize: 120,
|
|
100
|
+
overscan: 4,
|
|
101
|
+
getItemKey: (r) => r.version,
|
|
102
|
+
onEndReached: i,
|
|
103
|
+
hasMore: l,
|
|
104
|
+
loading: d,
|
|
105
|
+
emptyState: /* @__PURE__ */ e("p", { className: "py-6 text-center text-sm text-muted-foreground", children: "No changelog yet." }),
|
|
106
|
+
renderItem: (r) => /* @__PURE__ */ e(O, { entry: r })
|
|
107
|
+
}
|
|
108
|
+
)
|
|
109
|
+
] })
|
|
110
|
+
}
|
|
111
|
+
),
|
|
112
|
+
u && /* @__PURE__ */ e("div", { className: "fixed left-1/2 top-3 z-[9999] w-[min(92vw,400px)] -translate-x-1/2", children: /* @__PURE__ */ n("div", { className: "flex items-start gap-3 rounded-2xl border border-primary/40 bg-card p-3.5 shadow-xl", children: [
|
|
113
|
+
/* @__PURE__ */ e("span", { className: "grid h-8 w-8 shrink-0 place-items-center rounded-lg bg-primary/10 text-primary", children: /* @__PURE__ */ e(T, { className: "h-4 w-4" }) }),
|
|
114
|
+
/* @__PURE__ */ n("div", { className: "min-w-0 flex-1", children: [
|
|
115
|
+
/* @__PURE__ */ n("p", { className: "font-mono text-[10px] font-bold uppercase tracking-[0.14em] text-primary", children: [
|
|
116
|
+
"New version · v",
|
|
117
|
+
u.version
|
|
118
|
+
] }),
|
|
119
|
+
/* @__PURE__ */ e("p", { className: "mt-0.5 text-sm text-foreground", children: u.title || u.changes[0] || "A new version is available." }),
|
|
120
|
+
/* @__PURE__ */ n("div", { className: "mt-2 flex items-center gap-2", children: [
|
|
121
|
+
/* @__PURE__ */ e(
|
|
122
|
+
"button",
|
|
123
|
+
{
|
|
124
|
+
type: "button",
|
|
125
|
+
onClick: () => location.reload(),
|
|
126
|
+
className: "rounded-lg bg-primary px-3 py-1 text-xs font-bold text-primary-foreground transition-opacity hover:opacity-90",
|
|
127
|
+
children: "Reload"
|
|
128
|
+
}
|
|
129
|
+
),
|
|
130
|
+
/* @__PURE__ */ e(
|
|
131
|
+
"button",
|
|
132
|
+
{
|
|
133
|
+
type: "button",
|
|
134
|
+
onClick: U,
|
|
135
|
+
className: "rounded-lg px-2 py-1 text-xs font-semibold text-muted-foreground transition-colors hover:text-foreground",
|
|
136
|
+
children: "Dismiss"
|
|
137
|
+
}
|
|
138
|
+
)
|
|
139
|
+
] })
|
|
140
|
+
] })
|
|
141
|
+
] }) })
|
|
142
|
+
] });
|
|
143
|
+
}
|
|
144
|
+
function O({ entry: t }) {
|
|
145
|
+
const o = t.title && !(t.changes.length === 1 && t.changes[0] === t.title);
|
|
146
|
+
return /* @__PURE__ */ n("div", { className: "border-b border-border py-4 last:border-none", children: [
|
|
147
|
+
/* @__PURE__ */ n("div", { className: "mb-1.5 flex items-baseline gap-2", children: [
|
|
148
|
+
/* @__PURE__ */ n("span", { className: "rounded-md bg-primary/10 px-2 py-0.5 font-mono text-[11px] font-bold text-primary", children: [
|
|
149
|
+
"v",
|
|
150
|
+
t.version
|
|
151
|
+
] }),
|
|
152
|
+
t.date && /* @__PURE__ */ e("span", { className: "text-xs text-muted-foreground", children: t.date })
|
|
153
|
+
] }),
|
|
154
|
+
o && /* @__PURE__ */ e("p", { className: "mb-1.5 text-sm font-semibold text-foreground", children: t.title }),
|
|
155
|
+
/* @__PURE__ */ e("ul", { className: "space-y-1", children: t.changes.map((a, s) => /* @__PURE__ */ n("li", { className: "relative pl-4 text-[13px] leading-snug text-muted-foreground", children: [
|
|
156
|
+
/* @__PURE__ */ e("span", { className: "absolute left-0 top-2 h-1 w-1 rounded-full bg-primary" }),
|
|
157
|
+
a
|
|
158
|
+
] }, s)) })
|
|
159
|
+
] });
|
|
160
|
+
}
|
|
161
|
+
export {
|
|
162
|
+
X as Changelog
|
|
163
|
+
};
|
|
164
|
+
//# sourceMappingURL=changelog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"changelog.js","sources":["../../../src/components/changelog/changelog.tsx"],"sourcesContent":["import { useEffect, useRef, useState, type ReactNode } from 'react';\nimport { Sparkles, X } from 'lucide-react';\n\nimport { VirtualList } from '../virtual-list';\n\nconst SDK_URL = 'https://changelog-widget.dev.gabvdl.xyz/script.js';\nconst DEFAULT_CHANGELOG = '/changelog.jsonl';\n\nexport interface ChangelogEntry {\n version: string;\n date?: string;\n title?: string;\n changes: string[];\n sha?: string;\n}\n\ninterface ChangelogSDK {\n fetch(url?: string): Promise<ChangelogEntry[]>;\n latest(entries: ChangelogEntry[]): ChangelogEntry | null;\n watch(opts: {\n url?: string;\n intervalMs?: number;\n onUpdate: (latest: ChangelogEntry, entries: ChangelogEntry[]) => void;\n }): () => void;\n}\n\n/** Read window.Changelog without a global augmentation (keeps this drop-in). */\nfunction globalSdk(): ChangelogSDK | undefined {\n return (window as unknown as { Changelog?: ChangelogSDK }).Changelog;\n}\n\n/** Load the SDK script once (idempotent) and resolve window.Changelog. */\nfunction loadSdk(src: string): Promise<ChangelogSDK> {\n const present = globalSdk();\n if (present) return Promise.resolve(present);\n return new Promise((resolve, reject) => {\n const done = () => {\n const sdk = globalSdk();\n sdk ? resolve(sdk) : reject(new Error('SDK missing'));\n };\n const existing = document.querySelector<HTMLScriptElement>(`script[src=\"${src}\"]`);\n if (existing) {\n existing.addEventListener('load', done, { once: true });\n existing.addEventListener('error', () => reject(new Error('SDK failed')), { once: true });\n const sdk = globalSdk();\n if (sdk) resolve(sdk);\n return;\n }\n const s = document.createElement('script');\n s.src = src;\n s.async = true;\n s.addEventListener('load', done, { once: true });\n s.addEventListener('error', () => reject(new Error('SDK failed')), { once: true });\n document.head.appendChild(s);\n });\n}\n\nexport interface ChangelogProps {\n /**\n * Supply entries directly (controlled). When set, the hosted SDK is not\n * loaded — pair with `onLoadMore` / `hasMore` / `loading` to page a long\n * history. Omit to auto-load from the changelog-widget SDK.\n */\n entries?: ChangelogEntry[];\n /** URL of the JSONL changelog (SDK mode). Default: /changelog.jsonl */\n url?: string;\n /** URL of the SDK script (SDK mode). Default: the hosted changelog script. */\n sdkUrl?: string;\n /** Modal heading. Default: \"Changelog\". */\n title?: string;\n /**\n * Custom trigger. Receives `open` (show the modal) and `hasUpdate` (a newer\n * version is available). If omitted, a plain \"Changelog\" text button renders.\n */\n trigger?: (args: { open: () => void; hasUpdate: boolean }) => ReactNode;\n /** Controlled mode: append the next page (used by the virtual list's tail). */\n onLoadMore?: () => void;\n /** Controlled mode: more pages remain. */\n hasMore?: boolean;\n /** Controlled mode: a page load is in flight. */\n loading?: boolean;\n /**\n * Controlled \"new version\" reload toast. Set to an entry to show the toast\n * for it — useful when you detect an update yourself (e.g. in controlled\n * mode, where the SDK watcher isn't running). Set to `null`/omit to hide it.\n */\n newVersion?: ChangelogEntry | null;\n /** Called when the user dismisses the \"new version\" toast. */\n onDismissNewVersion?: () => void;\n}\n\n/**\n * A changelog trigger + modal + \"new version\" reload toast, styled from the\n * design tokens. The entry list is a {@link VirtualList}, so a long history\n * scrolls cheaply and can lazily page in via `onLoadMore`. Data comes from the\n * headless changelog-widget SDK by default, or pass `entries` directly.\n */\nexport function Changelog({\n entries,\n url = DEFAULT_CHANGELOG,\n sdkUrl = SDK_URL,\n title = 'Changelog',\n trigger,\n onLoadMore,\n hasMore = false,\n loading = false,\n newVersion = null,\n onDismissNewVersion,\n}: ChangelogProps) {\n const controlled = entries !== undefined;\n const [loaded, setLoaded] = useState<ChangelogEntry[]>([]);\n const [open, setOpen] = useState(false);\n const [update, setUpdate] = useState<ChangelogEntry | null>(null);\n const stopRef = useRef<() => void>(() => {});\n\n const data = controlled ? entries : loaded;\n\n useEffect(() => {\n if (controlled) return;\n let alive = true;\n loadSdk(sdkUrl)\n .then((sdk) => {\n if (!alive) return;\n void sdk.fetch(url).then((e) => alive && setLoaded(e));\n stopRef.current = sdk.watch({\n url,\n onUpdate: (latest, all) => {\n if (!alive) return;\n setLoaded(all);\n setUpdate(latest);\n },\n });\n })\n .catch(() => {});\n return () => {\n alive = false;\n stopRef.current();\n };\n }, [controlled, url, sdkUrl]);\n\n useEffect(() => {\n if (!open) return;\n const onKey = (e: KeyboardEvent) => e.key === 'Escape' && setOpen(false);\n document.addEventListener('keydown', onKey);\n return () => document.removeEventListener('keydown', onKey);\n }, [open]);\n\n // The controlled `newVersion` prop takes precedence over the SDK watcher's\n // own detected update, so a consumer can drive the reload toast directly.\n const activeUpdate = newVersion ?? update;\n const hasUpdate = activeUpdate !== null;\n const dismissUpdate = () => {\n setUpdate(null);\n onDismissNewVersion?.();\n };\n\n return (\n <>\n {trigger ? (\n trigger({ open: () => setOpen(true), hasUpdate })\n ) : (\n <button\n type=\"button\"\n onClick={() => setOpen(true)}\n className=\"relative inline-flex items-center text-sm font-medium text-muted-foreground transition-colors hover:text-foreground\"\n >\n Changelog\n {hasUpdate && (\n <span className=\"ml-1.5 h-1.5 w-1.5 rounded-full bg-primary\" aria-hidden />\n )}\n </button>\n )}\n\n {open && (\n <div\n className=\"fixed inset-0 z-[9999] flex items-center justify-center bg-black/50 p-4 backdrop-blur-sm\"\n onClick={(e) => e.target === e.currentTarget && setOpen(false)}\n >\n <div className=\"flex max-h-[80vh] w-full max-w-md flex-col overflow-hidden rounded-2xl border border-border bg-card shadow-2xl\">\n <div className=\"flex items-center gap-2 border-b border-border px-5 py-4\">\n <h2 className=\"flex-1 text-base font-bold text-foreground\">{title}</h2>\n <button\n type=\"button\"\n onClick={() => setOpen(false)}\n aria-label=\"Close\"\n className=\"grid h-8 w-8 place-items-center rounded-lg text-muted-foreground transition-colors hover:bg-muted hover:text-foreground\"\n >\n <X className=\"h-4 w-4\" />\n </button>\n </div>\n <VirtualList\n items={data}\n className=\"min-h-0 flex-1 px-5\"\n estimateSize={120}\n overscan={4}\n getItemKey={(e) => e.version}\n onEndReached={onLoadMore}\n hasMore={hasMore}\n loading={loading}\n emptyState={<p className=\"py-6 text-center text-sm text-muted-foreground\">No changelog yet.</p>}\n renderItem={(e) => <Entry entry={e} />}\n />\n </div>\n </div>\n )}\n\n {activeUpdate && (\n <div className=\"fixed left-1/2 top-3 z-[9999] w-[min(92vw,400px)] -translate-x-1/2\">\n <div className=\"flex items-start gap-3 rounded-2xl border border-primary/40 bg-card p-3.5 shadow-xl\">\n <span className=\"grid h-8 w-8 shrink-0 place-items-center rounded-lg bg-primary/10 text-primary\">\n <Sparkles className=\"h-4 w-4\" />\n </span>\n <div className=\"min-w-0 flex-1\">\n <p className=\"font-mono text-[10px] font-bold uppercase tracking-[0.14em] text-primary\">\n New version · v{activeUpdate.version}\n </p>\n <p className=\"mt-0.5 text-sm text-foreground\">\n {activeUpdate.title || activeUpdate.changes[0] || 'A new version is available.'}\n </p>\n <div className=\"mt-2 flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => location.reload()}\n className=\"rounded-lg bg-primary px-3 py-1 text-xs font-bold text-primary-foreground transition-opacity hover:opacity-90\"\n >\n Reload\n </button>\n <button\n type=\"button\"\n onClick={dismissUpdate}\n className=\"rounded-lg px-2 py-1 text-xs font-semibold text-muted-foreground transition-colors hover:text-foreground\"\n >\n Dismiss\n </button>\n </div>\n </div>\n </div>\n </div>\n )}\n </>\n );\n}\n\nfunction Entry({ entry }: { entry: ChangelogEntry }) {\n const showTitle = entry.title && !(entry.changes.length === 1 && entry.changes[0] === entry.title);\n return (\n <div className=\"border-b border-border py-4 last:border-none\">\n <div className=\"mb-1.5 flex items-baseline gap-2\">\n <span className=\"rounded-md bg-primary/10 px-2 py-0.5 font-mono text-[11px] font-bold text-primary\">\n v{entry.version}\n </span>\n {entry.date && <span className=\"text-xs text-muted-foreground\">{entry.date}</span>}\n </div>\n {showTitle && <p className=\"mb-1.5 text-sm font-semibold text-foreground\">{entry.title}</p>}\n <ul className=\"space-y-1\">\n {entry.changes.map((c, i) => (\n <li key={i} className=\"relative pl-4 text-[13px] leading-snug text-muted-foreground\">\n <span className=\"absolute left-0 top-2 h-1 w-1 rounded-full bg-primary\" />\n {c}\n </li>\n ))}\n </ul>\n </div>\n );\n}\n"],"names":["SDK_URL","DEFAULT_CHANGELOG","globalSdk","loadSdk","src","present","resolve","reject","done","sdk","existing","s","Changelog","entries","url","sdkUrl","title","trigger","onLoadMore","hasMore","loading","newVersion","onDismissNewVersion","controlled","loaded","setLoaded","useState","open","setOpen","update","setUpdate","stopRef","useRef","data","useEffect","alive","e","latest","all","onKey","activeUpdate","hasUpdate","dismissUpdate","jsxs","Fragment","jsx","X","VirtualList","Entry","Sparkles","entry","showTitle","c","i"],"mappings":"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"}
|