@krizaka/ui 2.3.0 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -1
- package/dist/audio-player.d.ts +113 -0
- package/dist/audio-player.js +207 -0
- package/dist/audio-player.js.map +1 -0
- package/dist/chunk-2USC2YT6.js +17 -0
- package/dist/chunk-2USC2YT6.js.map +1 -0
- package/dist/chunk-EUPTXKH7.js +95 -0
- package/dist/chunk-EUPTXKH7.js.map +1 -0
- package/dist/chunk-RUPRYJ45.js +82 -0
- package/dist/chunk-RUPRYJ45.js.map +1 -0
- package/dist/chunk-XUMJA5ZI.js +283 -0
- package/dist/chunk-XUMJA5ZI.js.map +1 -0
- package/dist/chunk-YH4LMWRU.js +92 -0
- package/dist/chunk-YH4LMWRU.js.map +1 -0
- package/dist/chunk-YX6CNVJA.js +94 -0
- package/dist/chunk-YX6CNVJA.js.map +1 -0
- package/dist/chunk-ZIXB33X7.js +95 -0
- package/dist/chunk-ZIXB33X7.js.map +1 -0
- package/dist/combobox.d.ts +188 -0
- package/dist/combobox.js +16 -0
- package/dist/combobox.js.map +1 -0
- package/dist/data-table.d.ts +246 -0
- package/dist/data-table.js +290 -0
- package/dist/data-table.js.map +1 -0
- package/dist/date-picker.d.ts +288 -0
- package/dist/date-picker.js +364 -0
- package/dist/date-picker.js.map +1 -0
- package/dist/file-upload.d.ts +238 -0
- package/dist/file-upload.js +437 -0
- package/dist/file-upload.js.map +1 -0
- package/dist/filter-bar.d.ts +139 -0
- package/dist/filter-bar.js +155 -0
- package/dist/filter-bar.js.map +1 -0
- package/dist/image-cropper.d.ts +115 -0
- package/dist/image-cropper.js +166 -0
- package/dist/image-cropper.js.map +1 -0
- package/dist/index.js.map +1 -1
- package/dist/media-capture.d.ts +155 -0
- package/dist/media-capture.js +242 -0
- package/dist/media-capture.js.map +1 -0
- package/dist/native/media-capture.d.ts +49 -0
- package/dist/native/media-capture.js +113 -0
- package/dist/native/media-capture.js.map +1 -0
- package/dist/native/video-player.d.ts +50 -0
- package/dist/native/video-player.js +69 -0
- package/dist/native/video-player.js.map +1 -0
- package/dist/native.d.ts +94 -3
- package/dist/native.js +195 -73
- package/dist/native.js.map +1 -1
- package/dist/pagination.d.ts +141 -0
- package/dist/pagination.js +165 -0
- package/dist/pagination.js.map +1 -0
- package/dist/search-field.d.ts +100 -0
- package/dist/search-field.js +167 -0
- package/dist/search-field.js.map +1 -0
- package/dist/tag-input.d.ts +44 -0
- package/dist/tag-input.js +67 -0
- package/dist/tag-input.js.map +1 -0
- package/dist/video-player.d.ts +204 -0
- package/dist/video-player.js +371 -0
- package/dist/video-player.js.map +1 -0
- package/dist/video-trimmer.d.ts +142 -0
- package/dist/video-trimmer.js +193 -0
- package/dist/video-trimmer.js.map +1 -0
- package/package.json +233 -5
- package/registry/alert.json +1 -1
- package/registry/audio-player.json +282 -0
- package/registry/avatar.json +1 -1
- package/registry/badge.json +1 -1
- package/registry/button.json +1 -1
- package/registry/card.json +1 -1
- package/registry/checkbox.json +1 -1
- package/registry/chip-link.json +1 -1
- package/registry/chip.json +1 -1
- package/registry/combobox.json +475 -0
- package/registry/command.json +1 -1
- package/registry/confirm-button.json +1 -1
- package/registry/countdown.json +1 -1
- package/registry/data-table.json +500 -0
- package/registry/date-picker.json +978 -0
- package/registry/dialog.json +1 -1
- package/registry/dropdown-menu.json +1 -1
- package/registry/empty-state.json +1 -1
- package/registry/examples/audio-player/podcast.d.ts +4 -0
- package/registry/examples/audio-player/podcast.js +22 -0
- package/registry/examples/audio-player/podcast.tsx +19 -0
- package/registry/examples/audio-player/voice-note.d.ts +4 -0
- package/registry/examples/audio-player/voice-note.js +12 -0
- package/registry/examples/audio-player/voice-note.tsx +12 -0
- package/registry/examples/combobox/async.d.ts +4 -0
- package/registry/examples/combobox/async.js +23 -0
- package/registry/examples/combobox/async.tsx +22 -0
- package/registry/examples/combobox/creatable.d.ts +4 -0
- package/registry/examples/combobox/creatable.js +32 -0
- package/registry/examples/combobox/creatable.tsx +28 -0
- package/registry/examples/combobox/invalid.d.ts +4 -0
- package/registry/examples/combobox/invalid.js +23 -0
- package/registry/examples/combobox/invalid.tsx +17 -0
- package/registry/examples/combobox/multiple.d.ts +4 -0
- package/registry/examples/combobox/multiple.js +29 -0
- package/registry/examples/combobox/multiple.tsx +26 -0
- package/registry/examples/data-table/cards.d.ts +4 -0
- package/registry/examples/data-table/cards.js +21 -0
- package/registry/examples/data-table/cards.tsx +20 -0
- package/registry/examples/data-table/empty.d.ts +4 -0
- package/registry/examples/data-table/empty.js +26 -0
- package/registry/examples/data-table/empty.tsx +21 -0
- package/registry/examples/data-table/error.d.ts +4 -0
- package/registry/examples/data-table/error.js +28 -0
- package/registry/examples/data-table/error.tsx +29 -0
- package/registry/examples/data-table/jobs.d.ts +4 -0
- package/registry/examples/data-table/jobs.js +37 -0
- package/registry/examples/data-table/jobs.tsx +36 -0
- package/registry/examples/data-table/loading.d.ts +4 -0
- package/registry/examples/data-table/loading.js +16 -0
- package/registry/examples/data-table/loading.tsx +12 -0
- package/registry/examples/data-table/virtualized.d.ts +4 -0
- package/registry/examples/data-table/virtualized.js +21 -0
- package/registry/examples/data-table/virtualized.tsx +20 -0
- package/registry/examples/date-picker/calendar.d.ts +4 -0
- package/registry/examples/date-picker/calendar.js +14 -0
- package/registry/examples/date-picker/calendar.tsx +16 -0
- package/registry/examples/date-picker/french.d.ts +4 -0
- package/registry/examples/date-picker/french.js +20 -0
- package/registry/examples/date-picker/french.tsx +14 -0
- package/registry/examples/date-picker/open.d.ts +4 -0
- package/registry/examples/date-picker/open.js +11 -0
- package/registry/examples/date-picker/open.tsx +8 -0
- package/registry/examples/date-picker/range.d.ts +4 -0
- package/registry/examples/date-picker/range.js +29 -0
- package/registry/examples/date-picker/range.tsx +25 -0
- package/registry/examples/date-picker/schedule.d.ts +4 -0
- package/registry/examples/date-picker/schedule.js +26 -0
- package/registry/examples/date-picker/schedule.tsx +22 -0
- package/registry/examples/file-upload/images.d.ts +4 -0
- package/registry/examples/file-upload/images.js +32 -0
- package/registry/examples/file-upload/images.tsx +25 -0
- package/registry/examples/file-upload/states.d.ts +4 -0
- package/registry/examples/file-upload/states.js +28 -0
- package/registry/examples/file-upload/states.tsx +23 -0
- package/registry/examples/file-upload/tus.d.ts +4 -0
- package/registry/examples/file-upload/tus.js +23 -0
- package/registry/examples/file-upload/tus.tsx +26 -0
- package/registry/examples/file-upload/video.d.ts +4 -0
- package/registry/examples/file-upload/video.js +39 -0
- package/registry/examples/file-upload/video.tsx +36 -0
- package/registry/examples/filter-bar/address.d.ts +4 -0
- package/registry/examples/filter-bar/address.js +29 -0
- package/registry/examples/filter-bar/address.tsx +27 -0
- package/registry/examples/filter-bar/jobs.d.ts +4 -0
- package/registry/examples/filter-bar/jobs.js +37 -0
- package/registry/examples/filter-bar/jobs.tsx +37 -0
- package/registry/examples/filter-bar/menu.d.ts +4 -0
- package/registry/examples/filter-bar/menu.js +27 -0
- package/registry/examples/filter-bar/menu.tsx +23 -0
- package/registry/examples/image-cropper/avatar.d.ts +4 -0
- package/registry/examples/image-cropper/avatar.js +13 -0
- package/registry/examples/image-cropper/avatar.tsx +14 -0
- package/registry/examples/image-cropper/banner.d.ts +4 -0
- package/registry/examples/image-cropper/banner.js +11 -0
- package/registry/examples/image-cropper/banner.tsx +10 -0
- package/registry/examples/image-cropper/vertical-video.d.ts +4 -0
- package/registry/examples/image-cropper/vertical-video.js +12 -0
- package/registry/examples/image-cropper/vertical-video.tsx +16 -0
- package/registry/examples/media-capture/clip.d.ts +4 -0
- package/registry/examples/media-capture/clip.js +20 -0
- package/registry/examples/media-capture/clip.tsx +18 -0
- package/registry/examples/media-capture/native/photo.d.ts +4 -0
- package/registry/examples/media-capture/native/photo.dark.png +0 -0
- package/registry/examples/media-capture/native/photo.js +27 -0
- package/registry/examples/media-capture/native/photo.light.png +0 -0
- package/registry/examples/media-capture/native/photo.tsx +20 -0
- package/registry/examples/media-capture/profile-photo.d.ts +4 -0
- package/registry/examples/media-capture/profile-photo.js +24 -0
- package/registry/examples/media-capture/profile-photo.tsx +20 -0
- package/registry/examples/pagination/cursor.d.ts +4 -0
- package/registry/examples/pagination/cursor.js +24 -0
- package/registry/examples/pagination/cursor.tsx +20 -0
- package/registry/examples/pagination/links.d.ts +4 -0
- package/registry/examples/pagination/links.js +11 -0
- package/registry/examples/pagination/links.tsx +6 -0
- package/registry/examples/pagination/native/cursor.d.ts +4 -0
- package/registry/examples/pagination/native/cursor.dark.png +0 -0
- package/registry/examples/pagination/native/cursor.js +19 -0
- package/registry/examples/pagination/native/cursor.light.png +0 -0
- package/registry/examples/pagination/native/cursor.tsx +13 -0
- package/registry/examples/pagination/native/pages.d.ts +4 -0
- package/registry/examples/pagination/native/pages.dark.png +0 -0
- package/registry/examples/pagination/native/pages.js +21 -0
- package/registry/examples/pagination/native/pages.light.png +0 -0
- package/registry/examples/pagination/native/pages.tsx +14 -0
- package/registry/examples/pagination/results.d.ts +4 -0
- package/registry/examples/pagination/results.js +23 -0
- package/registry/examples/pagination/results.tsx +19 -0
- package/registry/examples/pagination/table-footer.d.ts +4 -0
- package/registry/examples/pagination/table-footer.js +31 -0
- package/registry/examples/pagination/table-footer.tsx +27 -0
- package/registry/examples/search-field/global.d.ts +4 -0
- package/registry/examples/search-field/global.js +19 -0
- package/registry/examples/search-field/global.tsx +15 -0
- package/registry/examples/search-field/native/default.d.ts +4 -0
- package/registry/examples/search-field/native/default.dark.png +0 -0
- package/registry/examples/search-field/native/default.js +13 -0
- package/registry/examples/search-field/native/default.light.png +0 -0
- package/registry/examples/search-field/native/default.tsx +7 -0
- package/registry/examples/search-field/suggestions.d.ts +4 -0
- package/registry/examples/search-field/suggestions.js +30 -0
- package/registry/examples/search-field/suggestions.tsx +27 -0
- package/registry/examples/search-field/toolbar.d.ts +4 -0
- package/registry/examples/search-field/toolbar.js +17 -0
- package/registry/examples/search-field/toolbar.tsx +21 -0
- package/registry/examples/tag-input/recipients.d.ts +4 -0
- package/registry/examples/tag-input/recipients.js +23 -0
- package/registry/examples/tag-input/recipients.tsx +20 -0
- package/registry/examples/tag-input/video-tags.d.ts +4 -0
- package/registry/examples/tag-input/video-tags.js +26 -0
- package/registry/examples/tag-input/video-tags.tsx +22 -0
- package/registry/examples/video-player/default.d.ts +4 -0
- package/registry/examples/video-player/default.js +12 -0
- package/registry/examples/video-player/default.tsx +8 -0
- package/registry/examples/video-player/gated.d.ts +4 -0
- package/registry/examples/video-player/gated.js +26 -0
- package/registry/examples/video-player/gated.tsx +23 -0
- package/registry/examples/video-player/native/default.d.ts +4 -0
- package/registry/examples/video-player/native/default.dark.png +0 -0
- package/registry/examples/video-player/native/default.js +18 -0
- package/registry/examples/video-player/native/default.light.png +0 -0
- package/registry/examples/video-player/native/default.tsx +12 -0
- package/registry/examples/video-player/signed-source.d.ts +4 -0
- package/registry/examples/video-player/signed-source.js +16 -0
- package/registry/examples/video-player/signed-source.tsx +15 -0
- package/registry/examples/video-player/vertical.d.ts +4 -0
- package/registry/examples/video-player/vertical.js +12 -0
- package/registry/examples/video-player/vertical.tsx +12 -0
- package/registry/examples/video-trimmer/custom.d.ts +4 -0
- package/registry/examples/video-trimmer/custom.js +24 -0
- package/registry/examples/video-trimmer/custom.tsx +26 -0
- package/registry/examples/video-trimmer/filmstrip.d.ts +4 -0
- package/registry/examples/video-trimmer/filmstrip.js +15 -0
- package/registry/examples/video-trimmer/filmstrip.tsx +13 -0
- package/registry/examples/video-trimmer/playhead.d.ts +4 -0
- package/registry/examples/video-trimmer/playhead.js +15 -0
- package/registry/examples/video-trimmer/playhead.tsx +13 -0
- package/registry/field.json +1 -1
- package/registry/file-upload.json +471 -0
- package/registry/filter-bar.json +347 -0
- package/registry/glow-card.json +1 -1
- package/registry/image-cropper.json +313 -0
- package/registry/index.json +255 -1
- package/registry/iso.json +1 -1
- package/registry/kbd.json +1 -1
- package/registry/loader.json +1 -1
- package/registry/marquee.json +1 -1
- package/registry/mascots.json +1 -1
- package/registry/media-capture.json +347 -0
- package/registry/meter.json +3 -3
- package/registry/nav-bar.json +1 -1
- package/registry/number-ticker.json +1 -1
- package/registry/page-header.json +1 -1
- package/registry/pagination.json +475 -0
- package/registry/popover.json +1 -1
- package/registry/progress.json +5 -5
- package/registry/radio-group.json +1 -1
- package/registry/search-field.json +436 -0
- package/registry/section-backdrop.json +1 -1
- package/registry/separator.json +1 -1
- package/registry/skeleton.json +1 -1
- package/registry/slider.json +1 -1
- package/registry/slot.json +1 -1
- package/registry/spinner.json +1 -1
- package/registry/stat.json +1 -1
- package/registry/stepper.json +1 -1
- package/registry/switch.json +1 -1
- package/registry/tabs.json +1 -1
- package/registry/tag-input.json +363 -0
- package/registry/theme.json +1 -1
- package/registry/toast.json +1 -1
- package/registry/tooltip.json +1 -1
- package/registry/video-player.json +598 -0
- package/registry/video-trimmer.json +385 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/file-upload/file-upload.tsx","../src/progress/progress.tsx"],"sourcesContent":["// Client. Three parts that work alone or together:\n// - `Dropzone`: drop files on it or pick them with its button (a real `<input type=\"file\">`, so the keyboard and the\n// phone's camera / files sheet work); it checks the type, the size and the count, and hands over the accepted files\n// and the refused ones with their reason;\n// - `UploadList`: each file with its preview, size, progress, speed, and the actions its state allows (pause, resume,\n// retry, cancel, remove); the progress is a `progressbar`, a failure is announced;\n// - `useUploadQueue`: runs the app's `upload(file, { signal, onProgress, resumed })` with a concurrency, pauses by\n// aborting the signal and resumes by calling it again with `resumed: true` — what a Tus client (tus-js-client's\n// `abort()` / `start()`) or a multipart upload needs, without depending on either.\nimport { getNumberFormat } from \"@krizaka/intl\";\nimport * as React from \"react\";\nimport { tv } from \"tailwind-variants\";\n\nimport { CloseGlyph, FileGlyph, PauseGlyph, PlayGlyph, RetryGlyph, UploadGlyph } from \"../glyphs\";\nimport { Progress } from \"../progress\";\n\nexport const fileUpload = tv({\n slots: {\n zone:\n \"relative flex w-full flex-col items-center justify-center gap-3 rounded-xl border-2 border-dashed border-border-default bg-surface-1 px-6 py-10 text-center \" +\n \"transition-colors data-[dragging]:border-accent data-[dragging]:bg-accent-soft data-[disabled]:opacity-50 has-[:focus-visible]:border-accent\",\n zoneIcon: \"flex h-12 w-12 items-center justify-center rounded-full bg-surface-3 text-fg-secondary\",\n zoneTitle: \"text-sm font-semibold text-fg\",\n zoneHint: \"text-xs text-fg-secondary\",\n browse:\n \"inline-flex h-9 items-center justify-center rounded-lg bg-accent px-4 text-sm font-semibold text-on-accent transition-colors hover:bg-accent-hover \" +\n \"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-surface-0 disabled:pointer-events-none\",\n refusals: \"w-full text-left text-xs font-medium text-fg-danger\",\n list: \"flex w-full flex-col gap-2\",\n item: \"flex items-center gap-3 rounded-xl border border-border-default bg-surface-1 p-3 data-[status=error]:border-danger/60\",\n preview: \"flex h-12 w-12 shrink-0 items-center justify-center overflow-hidden rounded-lg bg-surface-3 text-fg-secondary\",\n body: \"flex min-w-0 flex-1 flex-col gap-1.5\",\n name: \"truncate text-sm font-semibold text-fg\",\n meta: \"flex flex-wrap gap-x-2 text-xs tabular-nums text-fg-secondary\",\n error: \"text-xs font-medium text-fg-danger\",\n actions: \"flex shrink-0 items-center gap-1\",\n action:\n \"inline-flex h-8 w-8 items-center justify-center rounded-md text-fg-secondary transition-colors hover:bg-surface-3 hover:text-fg \" +\n \"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring\",\n },\n});\n\n/** Why a file was refused. */\nexport type FileRejection = { file: File; reason: \"type\" | \"size\" | \"count\" };\n\n/** Whether a file matches an `accept` list (`\"video/*,.mov,image/png\"`). */\nexport function acceptsFile(file: File, accept?: string): boolean {\n if (!accept) return true;\n const name = file.name.toLowerCase();\n const type = file.type.toLowerCase();\n return accept\n .split(\",\")\n .map((rule) => rule.trim().toLowerCase())\n .filter(Boolean)\n .some((rule) => (rule.startsWith(\".\") ? name.endsWith(rule) : rule.endsWith(\"/*\") ? type.startsWith(rule.slice(0, -1)) : type === rule));\n}\n\n/** A size in the locale's words (`\"1.4 GB\"`, `\"820 kB\"`), through `@krizaka/intl`. */\nexport function formatBytes(bytes: number, locale = \"en\"): string {\n const units = [\"byte\", \"kilobyte\", \"megabyte\", \"gigabyte\", \"terabyte\"] as const;\n let value = Math.max(0, bytes);\n let unit = 0;\n while (value >= 1000 && unit < units.length - 1) {\n value /= 1000;\n unit += 1;\n }\n return getNumberFormat(locale, { style: \"unit\", unit: units[unit], unitDisplay: \"short\", maximumFractionDigits: value < 10 && unit > 0 ? 1 : 0 }).format(value);\n}\n\nexport type DropzoneProps = {\n /** The title in the zone (\"Drop your video here\"), passed translated. */\n title: string;\n /** The button that opens the file picker (\"Choose a file\"), passed translated. */\n browseLabel: string;\n /** What is accepted, in words (\"MP4 or MOV, up to 4 GB\"). */\n hint?: string;\n /** The types accepted, as the `accept` attribute (`\"video/*\"`, `\"image/png,.pdf\"`). */\n accept?: string;\n /** The largest file, in bytes. */\n maxSize?: number;\n /** The most files at once (1 unless `multiple`). */\n maxFiles?: number;\n /** Several files at once. */\n multiple?: boolean;\n /** Not available (an upload is running). */\n disabled?: boolean;\n /** On a phone, opens the camera directly (`\"environment\"` the back one, `\"user\"` the front one). */\n capture?: \"user\" | \"environment\";\n /** Called with the files accepted (never empty). */\n onFiles: (files: File[]) => void;\n /** Called with the files refused and why (show `refusalLabel`'s words, or your own). */\n onReject?: (rejections: FileRejection[]) => void;\n /** The words of a refusal, shown in the zone: `(r) => r.file.name + \" is too large\"`. Without it, nothing is shown. */\n refusalLabel?: (rejection: FileRejection) => string;\n /** An icon instead of the upload glyph. */\n icon?: React.ReactNode;\n /** Classes merged on the zone. */\n className?: string;\n};\n\n/** A zone to drop files on, with a button that opens the picker: checks type, size and count. */\nexport function Dropzone({\n title,\n browseLabel,\n hint,\n accept,\n maxSize,\n maxFiles,\n multiple,\n disabled,\n capture,\n onFiles,\n onReject,\n refusalLabel,\n icon,\n className,\n}: DropzoneProps) {\n const s = fileUpload();\n const input = React.useRef<HTMLInputElement>(null);\n const [dragging, setDragging] = React.useState(false);\n const [refused, setRefused] = React.useState<FileRejection[]>([]);\n const depth = React.useRef(0);\n const id = React.useId();\n const limit = maxFiles ?? (multiple ? Infinity : 1);\n\n const take = (list: FileList | File[]) => {\n const files = [...list];\n const accepted: File[] = [];\n const rejections: FileRejection[] = [];\n for (const file of files) {\n if (!acceptsFile(file, accept)) rejections.push({ file, reason: \"type\" });\n else if (maxSize !== undefined && file.size > maxSize) rejections.push({ file, reason: \"size\" });\n else if (accepted.length >= limit) rejections.push({ file, reason: \"count\" });\n else accepted.push(file);\n }\n setRefused(rejections);\n if (accepted.length) onFiles(accepted);\n if (rejections.length) onReject?.(rejections);\n };\n\n return (\n <div\n className={s.zone({ className })}\n data-dragging={dragging ? \"\" : undefined}\n data-disabled={disabled ? \"\" : undefined}\n onDragEnter={(event) => {\n if (disabled) return;\n event.preventDefault();\n depth.current += 1;\n setDragging(true);\n }}\n onDragOver={(event) => {\n if (disabled) return;\n event.preventDefault();\n event.dataTransfer.dropEffect = \"copy\";\n }}\n onDragLeave={() => {\n depth.current = Math.max(0, depth.current - 1);\n if (depth.current === 0) setDragging(false);\n }}\n onDrop={(event) => {\n if (disabled) return;\n event.preventDefault();\n depth.current = 0;\n setDragging(false);\n take(event.dataTransfer.files);\n }}\n >\n <span className={s.zoneIcon()} aria-hidden>\n {icon ?? <UploadGlyph className=\"h-6 w-6\" />}\n </span>\n <p id={`${id}-title`} className={s.zoneTitle()}>\n {title}\n </p>\n {hint ? (\n <p id={`${id}-hint`} className={s.zoneHint()}>\n {hint}\n </p>\n ) : null}\n <button\n type=\"button\"\n className={s.browse()}\n disabled={disabled}\n aria-describedby={[`${id}-title`, hint ? `${id}-hint` : null].filter(Boolean).join(\" \")}\n onClick={() => input.current?.click()}\n >\n {browseLabel}\n </button>\n <input\n ref={input}\n type=\"file\"\n tabIndex={-1}\n aria-hidden\n className=\"sr-only\"\n accept={accept}\n multiple={multiple}\n capture={capture}\n disabled={disabled}\n onChange={(event) => {\n if (event.target.files?.length) take(event.target.files);\n event.target.value = \"\";\n }}\n />\n {refusalLabel && refused.length > 0 ? (\n <ul role=\"alert\" className={s.refusals()}>\n {refused.map((r) => (\n <li key={`${r.file.name}-${r.reason}`}>{refusalLabel(r)}</li>\n ))}\n </ul>\n ) : null}\n </div>\n );\n}\n\nexport type UploadStatus = \"queued\" | \"uploading\" | \"paused\" | \"done\" | \"error\";\n\nexport type UploadItem = {\n /** A stable id. */\n id: string;\n /** The file's name. */\n name: string;\n /** Its size, in bytes. */\n size: number;\n /** Its MIME type (`video/mp4`): picks the preview. */\n type?: string;\n /** Sent so far, between 0 and 1. */\n progress: number;\n /** Where it stands. */\n status: UploadStatus;\n /** Why it failed, passed translated. */\n error?: string;\n /** A preview (an object URL of the file, a thumbnail from the server). */\n previewUrl?: string;\n /** Bytes per second, for the speed and the time left. */\n speed?: number;\n};\n\nexport type UploadListLabels = {\n /** The list's accessible name (\"Uploads\"). */\n list: string;\n pause: (name: string) => string;\n resume: (name: string) => string;\n retry: (name: string) => string;\n cancel: (name: string) => string;\n remove: (name: string) => string;\n /** A state in words: `(status) => \"Paused\"`… */\n status: (status: UploadStatus) => string;\n /** The time left: `(seconds) => \"2 min left\"`. */\n remaining: (seconds: number) => string;\n};\n\nexport const uploadListLabels: UploadListLabels = {\n list: \"Uploads\",\n pause: (name) => `Pause ${name}`,\n resume: (name) => `Resume ${name}`,\n retry: (name) => `Retry ${name}`,\n cancel: (name) => `Cancel ${name}`,\n remove: (name) => `Remove ${name}`,\n status: (status) => ({ queued: \"Waiting\", uploading: \"Uploading\", paused: \"Paused\", done: \"Uploaded\", error: \"Failed\" })[status],\n remaining: (seconds) => (seconds < 60 ? `${Math.ceil(seconds)} s left` : `${Math.ceil(seconds / 60)} min left`),\n};\n\nexport type UploadListProps = {\n /** The uploads, as `useUploadQueue` gives them (or your own state). */\n items: readonly UploadItem[];\n /** Pauses an upload (shown while it uploads, when given). */\n onPause?: (id: string) => void;\n /** Resumes a paused one. */\n onResume?: (id: string) => void;\n /** Tries a failed one again. */\n onRetry?: (id: string) => void;\n /** Stops an upload under way and drops it. */\n onCancel?: (id: string) => void;\n /** Drops a finished or failed one from the list. */\n onRemove?: (id: string) => void;\n /** The locale of sizes and speeds. Default `\"en\"`. */\n locale?: string;\n /** The words, passed translated; English by default. */\n labels?: Partial<UploadListLabels>;\n /** Classes merged on the list. */\n className?: string;\n};\n\nfunction Preview({ item }: { item: UploadItem }) {\n if (item.previewUrl && item.type?.startsWith(\"image/\")) return <img src={item.previewUrl} alt=\"\" className=\"h-full w-full object-cover\" />;\n if (item.previewUrl && item.type?.startsWith(\"video/\")) return <video src={item.previewUrl} muted playsInline preload=\"metadata\" className=\"h-full w-full object-cover\" aria-hidden />;\n return <FileGlyph className=\"h-5 w-5\" />;\n}\n\n/** The files being sent: preview, size, progress, speed and time left, and the actions each state allows. */\nexport function UploadList({ items, onPause, onResume, onRetry, onCancel, onRemove, locale = \"en\", labels: given, className }: UploadListProps) {\n const labels = { ...uploadListLabels, ...given };\n const s = fileUpload();\n const action = (label: string, run: () => void, glyph: React.ReactNode) => (\n <button type=\"button\" className={s.action()} aria-label={label} title={label} onClick={run}>\n {glyph}\n </button>\n );\n return (\n <ul aria-label={labels.list} className={s.list({ className })}>\n {items.map((item) => {\n const left = item.status === \"uploading\" && item.speed ? ((1 - item.progress) * item.size) / item.speed : null;\n return (\n <li key={item.id} className={s.item()} data-status={item.status}>\n <span className={s.preview()}>\n <Preview item={item} />\n </span>\n <div className={s.body()}>\n <p className={s.name()}>{item.name}</p>\n {item.status !== \"done\" && item.status !== \"error\" ? (\n <Progress size=\"sm\" label={item.name} value={item.status === \"queued\" ? null : Math.round(item.progress * 100)} />\n ) : null}\n <p className={s.meta()}>\n <span>{labels.status(item.status)}</span>\n <span>{item.status === \"done\" ? formatBytes(item.size, locale) : `${formatBytes(item.progress * item.size, locale)} / ${formatBytes(item.size, locale)}`}</span>\n {item.status === \"uploading\" && item.speed ? <span>{formatBytes(item.speed, locale)}/s</span> : null}\n {left !== null ? <span>{labels.remaining(left)}</span> : null}\n </p>\n {item.status === \"error\" && item.error ? (\n <p role=\"alert\" className={s.error()}>\n {item.error}\n </p>\n ) : null}\n </div>\n <div className={s.actions()}>\n {item.status === \"uploading\" && onPause && action(labels.pause(item.name), () => onPause(item.id), <PauseGlyph className=\"h-4 w-4\" />)}\n {item.status === \"paused\" && onResume && action(labels.resume(item.name), () => onResume(item.id), <PlayGlyph className=\"h-4 w-4\" />)}\n {item.status === \"error\" && onRetry && action(labels.retry(item.name), () => onRetry(item.id), <RetryGlyph className=\"h-4 w-4\" />)}\n {(item.status === \"done\" || item.status === \"error\") && onRemove\n ? action(labels.remove(item.name), () => onRemove(item.id), <CloseGlyph className=\"h-4 w-4\" />)\n : item.status !== \"done\" && onCancel\n ? action(labels.cancel(item.name), () => onCancel(item.id), <CloseGlyph className=\"h-4 w-4\" />)\n : null}\n </div>\n </li>\n );\n })}\n </ul>\n );\n}\n\nexport type UploadContext = {\n /** Aborted on pause and cancel: stop sending (`tusUpload.abort()`, `xhr.abort()`). */\n signal: AbortSignal;\n /** Report the bytes sent so far. */\n onProgress: (sent: number) => void;\n /** `true` when this call resumes a paused or failed upload (a Tus client then continues from its offset). */\n resumed: boolean;\n};\n\nexport type UploadQueueOptions = {\n /** Sends one file; resolves when it is done, rejects on failure (its `message` becomes the item's error). */\n upload: (file: File, context: UploadContext) => Promise<void>;\n /** How many files go at once. Default 2. */\n concurrency?: number;\n /** Makes an object URL for each file to preview (revoked when it leaves). Default `true`. */\n previews?: boolean;\n};\n\ntype Entry = { item: UploadItem; file: File; controller: AbortController | null; started: boolean; last?: { at: number; sent: number } };\n\n/** The queue itself, outside React: entries, the pump that starts the next uploads, and the actions. */\nfunction createQueue(initial: UploadQueueOptions, emit: (items: UploadItem[]) => void) {\n let current = initial;\n const options = () => current;\n const entries = new Map<string, Entry>();\n const sync = () => emit([...entries.values()].map((e) => e.item));\n const patch = (id: string, next: Partial<UploadItem>) => {\n const entry = entries.get(id);\n if (!entry) return;\n entry.item = { ...entry.item, ...next };\n sync();\n };\n const pump = () => {\n const { upload, concurrency = 2 } = options();\n const all = [...entries.values()];\n let running = all.filter((e) => e.item.status === \"uploading\").length;\n for (const entry of all) {\n if (running >= concurrency) break;\n if (entry.item.status !== \"queued\") continue;\n running += 1;\n const controller = new AbortController();\n const resumed = entry.started;\n entry.controller = controller;\n entry.started = true;\n entry.last = { at: Date.now(), sent: entry.item.progress * entry.item.size };\n patch(entry.item.id, { status: \"uploading\", error: undefined });\n upload(entry.file, {\n signal: controller.signal,\n resumed,\n onProgress: (sent) => {\n if (controller.signal.aborted) return;\n const now = Date.now();\n const last = entry.last ?? { at: now, sent };\n const fresh = now - last.at > 400;\n const speed = fresh ? ((sent - last.sent) / (now - last.at)) * 1000 : entry.item.speed;\n if (fresh) entry.last = { at: now, sent };\n patch(entry.item.id, { progress: entry.item.size ? Math.min(1, sent / entry.item.size) : 0, speed });\n },\n }).then(\n () => {\n if (controller.signal.aborted) return;\n patch(entry.item.id, { status: \"done\", progress: 1, speed: undefined });\n pump();\n },\n (error: unknown) => {\n if (controller.signal.aborted) return;\n patch(entry.item.id, { status: \"error\", error: error instanceof Error ? error.message : String(error), speed: undefined });\n pump();\n },\n );\n }\n };\n const release = (entry: Entry) => {\n entry.controller?.abort();\n if (entry.item.previewUrl) URL.revokeObjectURL(entry.item.previewUrl);\n };\n return {\n configure(next: UploadQueueOptions) {\n current = next;\n },\n add(files: readonly File[]) {\n const { previews = true } = options();\n for (const file of files) {\n const id = `${file.name}-${file.size}-${Math.random().toString(36).slice(2, 8)}`;\n const previewUrl = previews && typeof URL.createObjectURL === \"function\" ? URL.createObjectURL(file) : undefined;\n entries.set(id, { file, controller: null, started: false, item: { id, name: file.name, size: file.size, type: file.type, progress: 0, status: \"queued\", previewUrl } });\n }\n sync();\n pump();\n },\n pause(id: string) {\n entries.get(id)?.controller?.abort();\n patch(id, { status: \"paused\", speed: undefined });\n pump();\n },\n resume(id: string) {\n patch(id, { status: \"queued\" });\n pump();\n },\n remove(id: string) {\n const entry = entries.get(id);\n if (!entry) return;\n release(entry);\n entries.delete(id);\n sync();\n pump();\n },\n dispose() {\n for (const entry of entries.values()) release(entry);\n },\n };\n}\n\n/** The state of an upload queue around the app's `upload` function: `add`, `pause`, `resume`, `retry`, `cancel`, `remove`. */\nexport function useUploadQueue(options: UploadQueueOptions) {\n const [items, setItems] = React.useState<UploadItem[]>([]);\n const [queue] = React.useState(() => createQueue(options, setItems));\n React.useLayoutEffect(() => queue.configure(options));\n React.useEffect(() => () => queue.dispose(), [queue]);\n return { items, add: queue.add, pause: queue.pause, resume: queue.resume, retry: queue.resume, cancel: queue.remove, remove: queue.remove };\n}\n","// Server-safe: no hook, no context. role=\"progressbar\" with aria-valuenow / aria-valuemax (none when indeterminate),\n// named by `label`. A bar or a ring; the ring can hold content in its centre (an amount, a percentage).\nimport * as React from \"react\";\nimport { tv, type VariantProps } from \"tailwind-variants\";\n\nexport const progress = tv({\n slots: {\n root: \"relative\",\n track: \"\",\n indicator: \"\",\n center: \"absolute inset-0 flex flex-col items-center justify-center text-center\",\n buffer: \"absolute inset-y-0 left-0 -z-10 rounded-full bg-accent/25 transition-[width] duration-500 motion-reduce:transition-none\",\n frame: \"flex w-full flex-col gap-1.5\",\n header: \"flex items-baseline gap-2 text-xs\",\n caption: \"min-w-0 flex-1 truncate font-medium text-fg\",\n figure: \"shrink-0 tabular-nums text-fg-secondary\",\n },\n variants: {\n variant: {\n bar: {\n root: \"relative isolate w-full overflow-hidden rounded-full bg-surface-3\",\n indicator:\n \"kz-progress h-full rounded-full bg-linear-to-r from-accent to-accent-2 transition-[width] duration-500 motion-reduce:transition-none \" +\n \"data-[state=indeterminate]:w-2/5\",\n },\n ring: {\n root: \"inline-flex shrink-0 items-center justify-center\",\n track: \"stroke-border-default\",\n indicator:\n \"stroke-accent transition-[stroke-dashoffset] duration-1000 motion-reduce:transition-none \" +\n \"data-[state=indeterminate]:origin-center data-[state=indeterminate]:animate-spin motion-reduce:data-[state=indeterminate]:animate-none\",\n },\n },\n size: { sm: {}, md: {}, lg: {} },\n status: {\n active: {},\n // Paused: the fill rests, striped — the task is waiting for someone, not broken.\n paused: { indicator: \"kz-progress-paused opacity-70\" },\n // Failed: the fill turns to the danger colour where it stopped.\n error: { indicator: \"from-danger to-danger stroke-danger\" },\n },\n },\n compoundVariants: [\n { variant: \"bar\", size: \"sm\", class: { root: \"h-1\" } },\n { variant: \"bar\", size: \"md\", class: { root: \"h-2\" } },\n { variant: \"bar\", size: \"lg\", class: { root: \"h-3\" } },\n { variant: \"ring\", size: \"sm\", class: { root: \"h-10 w-10\" } },\n { variant: \"ring\", size: \"md\", class: { root: \"h-20 w-20\" } },\n { variant: \"ring\", size: \"lg\", class: { root: \"h-36 w-36\" } },\n ],\n defaultVariants: { variant: \"bar\", size: \"md\", status: \"active\" },\n});\n\nexport type ProgressVariants = VariantProps<typeof progress>;\n\nexport type ProgressProps = Omit<React.ComponentProps<\"div\">, \"children\"> &\n ProgressVariants & {\n /** bar · ring (a ring holds its `children` in the centre). */\n variant?: ProgressVariants[\"variant\"];\n /** sm · md · lg: the bar's thickness, the ring's diameter. */\n size?: ProgressVariants[\"size\"];\n /** How far, between 0 and `max`. `null` or absent: indeterminate (a wait of unknown length). */\n value?: number | null;\n /** The value that means complete. */\n max?: number;\n /** The accessible name — passed translated (\"Upload\", \"Raised towards the goal\"). */\n label: string;\n /** The value in words for assistive technology (\"$420 of $1,000\") — default: the percentage. */\n valueText?: string;\n /** A ring's centre: an amount, a percentage, an icon. */\n children?: React.ReactNode;\n /** How far is already available beyond the value (a video buffered ahead), between 0 and `max` — bar only. */\n buffer?: number;\n /** Splits the bar or the ring into this many segments: the steps of a task, the parts of a goal. */\n segments?: number;\n /**\n * active · paused (the fill rests, striped: waiting for someone) · error (the fill turns to the danger colour where it stopped).\n * @default \"active\"\n */\n status?: ProgressVariants[\"status\"];\n /** Shows the label above the bar, with the percentage (or `valueText`) and `eta` — bar only. */\n showLabel?: boolean;\n /** The time left, in words (“About 2 min left”), shown with `showLabel` and read with the value — passed translated. */\n eta?: string;\n };\n\nconst STROKE = { sm: 12, md: 9, lg: 7 } as const;\n\n/** A progress bar or ring: `value` / `max`, indeterminate without a value. State: `data-state` (loading · complete · indeterminate). */\nexport function Progress({\n variant = \"bar\",\n size = \"md\",\n value,\n max = 100,\n label,\n valueText,\n className,\n children,\n buffer,\n segments,\n status = \"active\",\n showLabel,\n eta,\n ...props\n}: ProgressProps) {\n const s = progress({ variant, size, status });\n const maskId = `kz-seg-${React.useId().replace(/[^a-zA-Z0-9_-]/g, \"\")}`;\n const known = typeof value === \"number\" && Number.isFinite(value);\n const ratio = known ? Math.min(1, Math.max(0, value / (max || 1))) : 0;\n const percent = Math.round(ratio * 100);\n const state = !known ? \"indeterminate\" : ratio >= 1 ? \"complete\" : \"loading\";\n const aria = {\n role: \"progressbar\" as const,\n \"aria-label\": label,\n \"aria-valuemin\": known ? 0 : undefined,\n \"aria-valuemax\": known ? max : undefined,\n \"aria-valuenow\": known ? Math.min(max, Math.max(0, value)) : undefined,\n \"aria-valuetext\": [known ? (valueText ?? `${percent}%`) : valueText, eta].filter(Boolean).join(\", \") || undefined,\n \"data-state\": state,\n \"data-status\": status === \"active\" ? undefined : status,\n \"data-value\": known ? value : undefined,\n \"data-variant\": variant,\n };\n if (variant === \"ring\") {\n const stroke = STROKE[size];\n const r = 50 - stroke / 2;\n return (\n <div {...aria} className={s.root({ className })} {...props}>\n <svg viewBox=\"0 0 100 100\" className=\"h-full w-full -rotate-90\" aria-hidden>\n {segments && segments > 1 && (\n <mask id={maskId}>\n <circle cx=\"50\" cy=\"50\" r={r} fill=\"none\" stroke=\"white\" strokeWidth={stroke + 2} pathLength={100} strokeDasharray={`${100 / segments - 2} 2`} />\n </mask>\n )}\n <g mask={segments && segments > 1 ? `url(#${maskId})` : undefined}>\n <circle cx=\"50\" cy=\"50\" r={r} fill=\"none\" strokeWidth={stroke} className={s.track()} />\n <circle\n cx=\"50\"\n cy=\"50\"\n r={r}\n fill=\"none\"\n strokeWidth={stroke}\n strokeLinecap=\"round\"\n pathLength={100}\n strokeDasharray={100}\n strokeDashoffset={known ? 100 - percent : 75}\n data-state={state}\n className={s.indicator()}\n />\n </g>\n </svg>\n {children !== undefined && <div className={s.center()}>{children}</div>}\n </div>\n );\n }\n const segmented = segments && segments > 1 ? ({ \"--kz-segments\": segments } as React.CSSProperties) : undefined;\n const bufferRatio = typeof buffer === \"number\" && Number.isFinite(buffer) ? Math.min(1, Math.max(0, buffer / (max || 1))) : null;\n const bar = (\n <div {...aria} className={s.root({ className: [segmented && \"kz-progress-segmented\", className].filter(Boolean).join(\" \") || undefined })} style={segmented} {...props}>\n <div data-state={state} className={s.indicator()} style={known ? { width: `${ratio * 100}%` } : undefined} />\n {bufferRatio !== null && <div aria-hidden className={s.buffer()} style={{ width: `${bufferRatio * 100}%` }} />}\n </div>\n );\n if (!showLabel) return bar;\n const figure = [known ? (valueText ?? `${percent} %`) : valueText, eta].filter(Boolean).join(\" · \");\n return (\n <div className={s.frame()}>\n <div aria-hidden className={s.header()}>\n <span className={s.caption()}>{label}</span>\n {figure && <span className={s.figure()}>{figure}</span>}\n </div>\n {bar}\n </div>\n );\n}\n"],"mappings":"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","names":["React","tv","jsx","jsxs","tv"]}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import * as tailwind_variants from 'tailwind-variants';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
|
|
4
|
+
declare const filterBar: tailwind_variants.TVReturnType<{
|
|
5
|
+
[key: string]: {
|
|
6
|
+
[key: string]: tailwind_variants.ClassValue | {
|
|
7
|
+
results?: tailwind_variants.ClassValue;
|
|
8
|
+
root?: tailwind_variants.ClassValue;
|
|
9
|
+
controls?: tailwind_variants.ClassValue;
|
|
10
|
+
active?: tailwind_variants.ClassValue;
|
|
11
|
+
clear?: tailwind_variants.ClassValue;
|
|
12
|
+
count?: tailwind_variants.ClassValue;
|
|
13
|
+
optionCount?: tailwind_variants.ClassValue;
|
|
14
|
+
trigger?: tailwind_variants.ClassValue;
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
} | {
|
|
18
|
+
[x: string]: {
|
|
19
|
+
[x: string]: tailwind_variants.ClassValue | {
|
|
20
|
+
results?: tailwind_variants.ClassValue;
|
|
21
|
+
root?: tailwind_variants.ClassValue;
|
|
22
|
+
controls?: tailwind_variants.ClassValue;
|
|
23
|
+
active?: tailwind_variants.ClassValue;
|
|
24
|
+
clear?: tailwind_variants.ClassValue;
|
|
25
|
+
count?: tailwind_variants.ClassValue;
|
|
26
|
+
optionCount?: tailwind_variants.ClassValue;
|
|
27
|
+
trigger?: tailwind_variants.ClassValue;
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
} | {}, {
|
|
31
|
+
root: string;
|
|
32
|
+
controls: string;
|
|
33
|
+
active: string;
|
|
34
|
+
clear: string;
|
|
35
|
+
results: string;
|
|
36
|
+
trigger: string;
|
|
37
|
+
count: string;
|
|
38
|
+
optionCount: string;
|
|
39
|
+
}, undefined, {
|
|
40
|
+
[key: string]: {
|
|
41
|
+
[key: string]: tailwind_variants.ClassValue | {
|
|
42
|
+
results?: tailwind_variants.ClassValue;
|
|
43
|
+
root?: tailwind_variants.ClassValue;
|
|
44
|
+
controls?: tailwind_variants.ClassValue;
|
|
45
|
+
active?: tailwind_variants.ClassValue;
|
|
46
|
+
clear?: tailwind_variants.ClassValue;
|
|
47
|
+
count?: tailwind_variants.ClassValue;
|
|
48
|
+
optionCount?: tailwind_variants.ClassValue;
|
|
49
|
+
trigger?: tailwind_variants.ClassValue;
|
|
50
|
+
};
|
|
51
|
+
};
|
|
52
|
+
} | {}, {
|
|
53
|
+
root: string;
|
|
54
|
+
controls: string;
|
|
55
|
+
active: string;
|
|
56
|
+
clear: string;
|
|
57
|
+
results: string;
|
|
58
|
+
trigger: string;
|
|
59
|
+
count: string;
|
|
60
|
+
optionCount: string;
|
|
61
|
+
}, tailwind_variants.TVReturnTypeLike<unknown, {
|
|
62
|
+
root: string;
|
|
63
|
+
controls: string;
|
|
64
|
+
active: string;
|
|
65
|
+
clear: string;
|
|
66
|
+
results: string;
|
|
67
|
+
trigger: string;
|
|
68
|
+
count: string;
|
|
69
|
+
optionCount: string;
|
|
70
|
+
}>>;
|
|
71
|
+
type ActiveFilter = {
|
|
72
|
+
/** A stable key (`"status:failed"`). */
|
|
73
|
+
id: string;
|
|
74
|
+
/** What the chip says, passed translated ("Status: Failed"). */
|
|
75
|
+
label: string;
|
|
76
|
+
/** Removes this filter. */
|
|
77
|
+
onRemove: () => void;
|
|
78
|
+
};
|
|
79
|
+
type FilterBarProps = Omit<React.ComponentProps<"div">, "children" | "results"> & {
|
|
80
|
+
/** The accessible name of the bar ("Filter jobs"), passed translated. */
|
|
81
|
+
label: string;
|
|
82
|
+
/** The controls: `SearchField`, `FilterMenu`, `DateRangePicker`, a `Chip.Group`… */
|
|
83
|
+
children?: React.ReactNode;
|
|
84
|
+
/** The filters in force, each a removable chip. */
|
|
85
|
+
active?: readonly ActiveFilter[];
|
|
86
|
+
/** Clears every filter; the "Clear all" button shows when there is at least one. */
|
|
87
|
+
onClearAll?: () => void;
|
|
88
|
+
/** The "Clear all" button ("Clear filters"), passed translated. */
|
|
89
|
+
clearLabel?: string;
|
|
90
|
+
/** A chip's remove button: `(label) => "Remove " + label`, passed translated. */
|
|
91
|
+
removeLabel?: (label: string) => string;
|
|
92
|
+
/** The count of results ("42 jobs"), announced politely when it changes. */
|
|
93
|
+
results?: string;
|
|
94
|
+
};
|
|
95
|
+
/** The bar of filters above a list: controls, active filters as chips, clear all, the count of results. */
|
|
96
|
+
declare function FilterBar({ label, children, active, onClearAll, clearLabel, removeLabel, results, className, ...props }: FilterBarProps): React.JSX.Element;
|
|
97
|
+
type FilterOption = {
|
|
98
|
+
/** What the app stores (and puts in the address). */
|
|
99
|
+
value: string;
|
|
100
|
+
/** What the person reads, passed translated. */
|
|
101
|
+
label: string;
|
|
102
|
+
/** How many results it would give: shown on the right. */
|
|
103
|
+
count?: number;
|
|
104
|
+
/** Shown but not choosable. */
|
|
105
|
+
disabled?: boolean;
|
|
106
|
+
};
|
|
107
|
+
type FilterMenuBase = {
|
|
108
|
+
/** The name of the filter on its button ("Status"), passed translated. */
|
|
109
|
+
label: string;
|
|
110
|
+
/** The choices. */
|
|
111
|
+
options: readonly FilterOption[];
|
|
112
|
+
/** Opens the menu at first (a story, a test). */
|
|
113
|
+
defaultOpen?: boolean;
|
|
114
|
+
/** Classes merged on the button. */
|
|
115
|
+
className?: string;
|
|
116
|
+
};
|
|
117
|
+
type FilterMenuProps = (FilterMenuBase & {
|
|
118
|
+
/** Several values (checkboxes, default) or one (radios). */
|
|
119
|
+
multiple?: true;
|
|
120
|
+
/** The values chosen. */
|
|
121
|
+
value: readonly string[];
|
|
122
|
+
/** Called with the values chosen. */
|
|
123
|
+
onValueChange: (value: string[]) => void;
|
|
124
|
+
}) | (FilterMenuBase & {
|
|
125
|
+
/** One value: the options are radios. */
|
|
126
|
+
multiple: false;
|
|
127
|
+
/** The value chosen, `""` for none. */
|
|
128
|
+
value: string;
|
|
129
|
+
/** Called with the value chosen. */
|
|
130
|
+
onValueChange: (value: string) => void;
|
|
131
|
+
});
|
|
132
|
+
/** A filter's button and its menu of options — checkboxes (several values) or radios (one) — with a count of chosen ones. */
|
|
133
|
+
declare function FilterMenu(props: FilterMenuProps): React.JSX.Element;
|
|
134
|
+
/** Filters read from an address: `{ status: ["failed", "running"], q: ["cat"] }` for the keys asked (repeated or comma-separated values). */
|
|
135
|
+
declare function readFilterParams(params: URLSearchParams, keys: readonly string[]): Record<string, string[]>;
|
|
136
|
+
/** Filters written into an address (a copy of `base`): empty filters removed, several values comma-separated, the page reset. */
|
|
137
|
+
declare function writeFilterParams(base: URLSearchParams, filters: Record<string, string | readonly string[] | null | undefined>, resetKey?: string): URLSearchParams;
|
|
138
|
+
|
|
139
|
+
export { type ActiveFilter, FilterBar, type FilterBarProps, FilterMenu, type FilterMenuProps, type FilterOption, filterBar, readFilterParams, writeFilterParams };
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import {
|
|
3
|
+
Chip
|
|
4
|
+
} from "./chunk-YH4LMWRU.js";
|
|
5
|
+
import {
|
|
6
|
+
cn,
|
|
7
|
+
floating
|
|
8
|
+
} from "./chunk-2USC2YT6.js";
|
|
9
|
+
import {
|
|
10
|
+
ChevronGlyph
|
|
11
|
+
} from "./chunk-YX6CNVJA.js";
|
|
12
|
+
|
|
13
|
+
// src/filter-bar/filter-bar.tsx
|
|
14
|
+
import { DropdownMenu as MenuPrimitive2 } from "radix-ui";
|
|
15
|
+
import { tv as tv2 } from "tailwind-variants";
|
|
16
|
+
|
|
17
|
+
// src/dropdown-menu/dropdown-menu.tsx
|
|
18
|
+
import { DropdownMenu as MenuPrimitive } from "radix-ui";
|
|
19
|
+
import { tv } from "tailwind-variants";
|
|
20
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
21
|
+
var menu = tv({
|
|
22
|
+
slots: {
|
|
23
|
+
content: cn(floating, "min-w-48 max-h-(--radix-dropdown-menu-content-available-height) overflow-y-auto p-1 origin-(--radix-dropdown-menu-content-transform-origin)"),
|
|
24
|
+
item: "relative flex cursor-pointer select-none items-center gap-2 rounded-md px-2.5 py-2 text-sm text-fg outline-hidden transition-colors data-highlighted:bg-surface-3 data-disabled:pointer-events-none data-disabled:opacity-40 [&>svg]:h-4 [&>svg]:w-4 [&>svg]:shrink-0 [&>svg]:text-fg-secondary",
|
|
25
|
+
indicator: "ml-auto flex h-4 w-4 items-center justify-center text-fg-accent",
|
|
26
|
+
label: "px-2.5 pb-1 pt-2 text-[11px] font-semibold uppercase tracking-wider text-fg-secondary",
|
|
27
|
+
separator: "-mx-1 my-1 h-px bg-border-subtle"
|
|
28
|
+
},
|
|
29
|
+
variants: {
|
|
30
|
+
// The label reads in the danger text role (≥ 4.5:1 on the menu surface, both themes); the icon and the highlight
|
|
31
|
+
// carry the danger fill.
|
|
32
|
+
tone: { default: {}, danger: { item: "text-fg-danger data-highlighted:bg-danger/15 [&>svg]:text-danger" } }
|
|
33
|
+
},
|
|
34
|
+
defaultVariants: { tone: "default" }
|
|
35
|
+
});
|
|
36
|
+
var s = menu();
|
|
37
|
+
function DropdownMenuContent({ sideOffset = 6, align = "end", collisionPadding = 8, container, className, ...props }) {
|
|
38
|
+
return /* @__PURE__ */ jsx(MenuPrimitive.Portal, { container, children: /* @__PURE__ */ jsx(MenuPrimitive.Content, { sideOffset, align, collisionPadding, className: s.content({ className }), ...props }) });
|
|
39
|
+
}
|
|
40
|
+
function DropdownMenuItem({ tone, className, ...props }) {
|
|
41
|
+
return /* @__PURE__ */ jsx(MenuPrimitive.Item, { "data-tone": tone ?? "default", className: menu({ tone }).item({ className }), ...props });
|
|
42
|
+
}
|
|
43
|
+
function DropdownMenuCheckboxItem({ className, children, ...props }) {
|
|
44
|
+
return /* @__PURE__ */ jsxs(MenuPrimitive.CheckboxItem, { className: s.item({ className }), ...props, children: [
|
|
45
|
+
children,
|
|
46
|
+
/* @__PURE__ */ jsx(MenuPrimitive.ItemIndicator, { className: s.indicator(), children: /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: /* @__PURE__ */ jsx("path", { d: "M20 6 9 17l-5-5" }) }) })
|
|
47
|
+
] });
|
|
48
|
+
}
|
|
49
|
+
function DropdownMenuLabel({ className, ...props }) {
|
|
50
|
+
return /* @__PURE__ */ jsx(MenuPrimitive.Label, { className: s.label({ className }), ...props });
|
|
51
|
+
}
|
|
52
|
+
function DropdownMenuSeparator({ className, ...props }) {
|
|
53
|
+
return /* @__PURE__ */ jsx(MenuPrimitive.Separator, { className: s.separator({ className }), ...props });
|
|
54
|
+
}
|
|
55
|
+
var DropdownMenu = {
|
|
56
|
+
Root: MenuPrimitive.Root,
|
|
57
|
+
Trigger: MenuPrimitive.Trigger,
|
|
58
|
+
Content: DropdownMenuContent,
|
|
59
|
+
Item: DropdownMenuItem,
|
|
60
|
+
CheckboxItem: DropdownMenuCheckboxItem,
|
|
61
|
+
Label: DropdownMenuLabel,
|
|
62
|
+
Separator: DropdownMenuSeparator,
|
|
63
|
+
Group: MenuPrimitive.Group
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
// src/filter-bar/filter-bar.tsx
|
|
67
|
+
import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
68
|
+
var filterBar = tv2({
|
|
69
|
+
slots: {
|
|
70
|
+
root: "flex w-full flex-col gap-3",
|
|
71
|
+
controls: "flex w-full flex-wrap items-center gap-2 [&>*]:min-w-0",
|
|
72
|
+
active: "flex flex-wrap items-center gap-2",
|
|
73
|
+
clear: "rounded-md px-2 py-1 text-xs font-semibold text-fg-accent hover:bg-surface-2 focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring",
|
|
74
|
+
results: "ml-auto text-xs text-fg-secondary tabular-nums",
|
|
75
|
+
trigger: "inline-flex h-11 shrink-0 items-center gap-2 rounded-lg border border-border-default bg-surface-1 px-3.5 text-sm font-semibold text-fg-secondary transition-colors hover:border-border-strong hover:text-fg focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring data-[active]:border-accent data-[active]:bg-accent-soft data-[active]:text-fg data-[state=open]:border-border-strong",
|
|
76
|
+
count: "inline-flex h-5 min-w-5 items-center justify-center rounded-full bg-accent px-1.5 text-[11px] font-bold tabular-nums text-on-accent",
|
|
77
|
+
optionCount: "ml-auto pl-4 text-xs tabular-nums text-fg-secondary"
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
function FilterBar({
|
|
81
|
+
label,
|
|
82
|
+
children,
|
|
83
|
+
active = [],
|
|
84
|
+
onClearAll,
|
|
85
|
+
clearLabel = "Clear filters",
|
|
86
|
+
removeLabel = (text) => `Remove ${text}`,
|
|
87
|
+
results,
|
|
88
|
+
className,
|
|
89
|
+
...props
|
|
90
|
+
}) {
|
|
91
|
+
const s2 = filterBar();
|
|
92
|
+
return /* @__PURE__ */ jsxs2("div", { role: "group", "aria-label": label, className: s2.root({ className }), ...props, children: [
|
|
93
|
+
children ? /* @__PURE__ */ jsx2("div", { className: s2.controls(), children }) : null,
|
|
94
|
+
active.length > 0 || results !== void 0 ? /* @__PURE__ */ jsxs2("div", { className: s2.active(), children: [
|
|
95
|
+
active.map((filter) => /* @__PURE__ */ jsx2(Chip, { size: "sm", removable: true, removeLabel: removeLabel(filter.label), onRemove: filter.onRemove, children: filter.label }, filter.id)),
|
|
96
|
+
active.length > 0 && onClearAll ? /* @__PURE__ */ jsx2("button", { type: "button", className: s2.clear(), onClick: onClearAll, children: clearLabel }) : null,
|
|
97
|
+
/* @__PURE__ */ jsx2("p", { className: s2.results(), "aria-live": "polite", "aria-atomic": true, children: results })
|
|
98
|
+
] }) : null
|
|
99
|
+
] });
|
|
100
|
+
}
|
|
101
|
+
function FilterMenu(props) {
|
|
102
|
+
const { label, options, defaultOpen, className } = props;
|
|
103
|
+
const s2 = filterBar();
|
|
104
|
+
const m = menu();
|
|
105
|
+
const chosen = props.multiple === false ? props.value ? [props.value] : [] : props.value;
|
|
106
|
+
const single = props.multiple === false ? options.find((o) => o.value === props.value) : void 0;
|
|
107
|
+
const content = (option) => /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
108
|
+
option.label,
|
|
109
|
+
option.count !== void 0 ? /* @__PURE__ */ jsx2("span", { className: s2.optionCount(), children: option.count }) : null
|
|
110
|
+
] });
|
|
111
|
+
return /* @__PURE__ */ jsxs2(DropdownMenu.Root, { defaultOpen, modal: false, children: [
|
|
112
|
+
/* @__PURE__ */ jsxs2(DropdownMenu.Trigger, { className: s2.trigger({ className }), "data-active": chosen.length > 0 ? "" : void 0, children: [
|
|
113
|
+
single ? `${label}: ${single.label}` : label,
|
|
114
|
+
props.multiple !== false && chosen.length > 0 ? /* @__PURE__ */ jsx2("span", { className: s2.count(), children: chosen.length }) : null,
|
|
115
|
+
/* @__PURE__ */ jsx2(ChevronGlyph, { className: "h-4 w-4" })
|
|
116
|
+
] }),
|
|
117
|
+
/* @__PURE__ */ jsx2(DropdownMenu.Content, { align: "start", "aria-label": label, children: props.multiple === false ? /* @__PURE__ */ jsx2(MenuPrimitive2.RadioGroup, { value: props.value, onValueChange: props.onValueChange, children: options.map((option) => /* @__PURE__ */ jsxs2(MenuPrimitive2.RadioItem, { value: option.value, disabled: option.disabled, className: m.item(), children: [
|
|
118
|
+
/* @__PURE__ */ jsx2("span", { className: "flex h-4 w-4 shrink-0 items-center justify-center rounded-full border border-border-strong", children: /* @__PURE__ */ jsx2(MenuPrimitive2.ItemIndicator, { className: "h-2 w-2 rounded-full bg-accent" }) }),
|
|
119
|
+
content(option)
|
|
120
|
+
] }, option.value)) }) : options.map((option) => /* @__PURE__ */ jsx2(
|
|
121
|
+
DropdownMenu.CheckboxItem,
|
|
122
|
+
{
|
|
123
|
+
disabled: option.disabled,
|
|
124
|
+
checked: props.value.includes(option.value),
|
|
125
|
+
onSelect: (event) => event.preventDefault(),
|
|
126
|
+
onCheckedChange: (checked) => props.onValueChange(checked ? [...props.value, option.value] : props.value.filter((v) => v !== option.value)),
|
|
127
|
+
children: content(option)
|
|
128
|
+
},
|
|
129
|
+
option.value
|
|
130
|
+
)) })
|
|
131
|
+
] });
|
|
132
|
+
}
|
|
133
|
+
function readFilterParams(params, keys) {
|
|
134
|
+
return Object.fromEntries(
|
|
135
|
+
keys.map((key) => [key, params.getAll(key).flatMap((v) => v.split(",")).map((v) => v.trim()).filter(Boolean)])
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
function writeFilterParams(base, filters, resetKey = "page") {
|
|
139
|
+
const next = new URLSearchParams(base);
|
|
140
|
+
for (const [key, value] of Object.entries(filters)) {
|
|
141
|
+
const list = value == null ? [] : typeof value === "string" ? [value] : [...value];
|
|
142
|
+
if (list.filter(Boolean).length === 0) next.delete(key);
|
|
143
|
+
else next.set(key, list.filter(Boolean).join(","));
|
|
144
|
+
}
|
|
145
|
+
if (resetKey) next.delete(resetKey);
|
|
146
|
+
return next;
|
|
147
|
+
}
|
|
148
|
+
export {
|
|
149
|
+
FilterBar,
|
|
150
|
+
FilterMenu,
|
|
151
|
+
filterBar,
|
|
152
|
+
readFilterParams,
|
|
153
|
+
writeFilterParams
|
|
154
|
+
};
|
|
155
|
+
//# sourceMappingURL=filter-bar.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/filter-bar/filter-bar.tsx","../src/dropdown-menu/dropdown-menu.tsx"],"sourcesContent":["// Client (menus in a portal). The bar above a list: the controls that narrow it (a SearchField, FilterMenus, a\n// DateRangePicker — any child), the filters in force as removable chips, \"Clear all\", and the count of results in a\n// polite live region. It holds no state: the app keeps the filters — in the address, ideally — and the\n// `readFilterParams` / `writeFilterParams` helpers move them between a `URLSearchParams` and an object.\nimport { DropdownMenu as MenuPrimitive } from \"radix-ui\";\nimport * as React from \"react\";\nimport { tv } from \"tailwind-variants\";\n\nimport { Chip } from \"../chip\";\nimport { DropdownMenu, menu } from \"../dropdown-menu\";\nimport { ChevronGlyph } from \"../glyphs\";\n\nexport const filterBar = tv({\n slots: {\n root: \"flex w-full flex-col gap-3\",\n controls: \"flex w-full flex-wrap items-center gap-2 [&>*]:min-w-0\",\n active: \"flex flex-wrap items-center gap-2\",\n clear:\n \"rounded-md px-2 py-1 text-xs font-semibold text-fg-accent hover:bg-surface-2 focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring\",\n results: \"ml-auto text-xs text-fg-secondary tabular-nums\",\n trigger:\n \"inline-flex h-11 shrink-0 items-center gap-2 rounded-lg border border-border-default bg-surface-1 px-3.5 text-sm font-semibold text-fg-secondary \" +\n \"transition-colors hover:border-border-strong hover:text-fg focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring \" +\n \"data-[active]:border-accent data-[active]:bg-accent-soft data-[active]:text-fg data-[state=open]:border-border-strong\",\n count: \"inline-flex h-5 min-w-5 items-center justify-center rounded-full bg-accent px-1.5 text-[11px] font-bold tabular-nums text-on-accent\",\n optionCount: \"ml-auto pl-4 text-xs tabular-nums text-fg-secondary\",\n },\n});\n\nexport type ActiveFilter = {\n /** A stable key (`\"status:failed\"`). */\n id: string;\n /** What the chip says, passed translated (\"Status: Failed\"). */\n label: string;\n /** Removes this filter. */\n onRemove: () => void;\n};\n\nexport type FilterBarProps = Omit<React.ComponentProps<\"div\">, \"children\" | \"results\"> & {\n /** The accessible name of the bar (\"Filter jobs\"), passed translated. */\n label: string;\n /** The controls: `SearchField`, `FilterMenu`, `DateRangePicker`, a `Chip.Group`… */\n children?: React.ReactNode;\n /** The filters in force, each a removable chip. */\n active?: readonly ActiveFilter[];\n /** Clears every filter; the \"Clear all\" button shows when there is at least one. */\n onClearAll?: () => void;\n /** The \"Clear all\" button (\"Clear filters\"), passed translated. */\n clearLabel?: string;\n /** A chip's remove button: `(label) => \"Remove \" + label`, passed translated. */\n removeLabel?: (label: string) => string;\n /** The count of results (\"42 jobs\"), announced politely when it changes. */\n results?: string;\n};\n\n/** The bar of filters above a list: controls, active filters as chips, clear all, the count of results. */\nexport function FilterBar({\n label,\n children,\n active = [],\n onClearAll,\n clearLabel = \"Clear filters\",\n removeLabel = (text) => `Remove ${text}`,\n results,\n className,\n ...props\n}: FilterBarProps) {\n const s = filterBar();\n return (\n <div role=\"group\" aria-label={label} className={s.root({ className })} {...props}>\n {children ? <div className={s.controls()}>{children}</div> : null}\n {active.length > 0 || results !== undefined ? (\n <div className={s.active()}>\n {active.map((filter) => (\n <Chip key={filter.id} size=\"sm\" removable removeLabel={removeLabel(filter.label)} onRemove={filter.onRemove}>\n {filter.label}\n </Chip>\n ))}\n {active.length > 0 && onClearAll ? (\n <button type=\"button\" className={s.clear()} onClick={onClearAll}>\n {clearLabel}\n </button>\n ) : null}\n <p className={s.results()} aria-live=\"polite\" aria-atomic>\n {results}\n </p>\n </div>\n ) : null}\n </div>\n );\n}\n\nexport type FilterOption = {\n /** What the app stores (and puts in the address). */\n value: string;\n /** What the person reads, passed translated. */\n label: string;\n /** How many results it would give: shown on the right. */\n count?: number;\n /** Shown but not choosable. */\n disabled?: boolean;\n};\n\ntype FilterMenuBase = {\n /** The name of the filter on its button (\"Status\"), passed translated. */\n label: string;\n /** The choices. */\n options: readonly FilterOption[];\n /** Opens the menu at first (a story, a test). */\n defaultOpen?: boolean;\n /** Classes merged on the button. */\n className?: string;\n};\n\nexport type FilterMenuProps =\n | (FilterMenuBase & {\n /** Several values (checkboxes, default) or one (radios). */\n multiple?: true;\n /** The values chosen. */\n value: readonly string[];\n /** Called with the values chosen. */\n onValueChange: (value: string[]) => void;\n })\n | (FilterMenuBase & {\n /** One value: the options are radios. */\n multiple: false;\n /** The value chosen, `\"\"` for none. */\n value: string;\n /** Called with the value chosen. */\n onValueChange: (value: string) => void;\n });\n\n/** A filter's button and its menu of options — checkboxes (several values) or radios (one) — with a count of chosen ones. */\nexport function FilterMenu(props: FilterMenuProps) {\n const { label, options, defaultOpen, className } = props;\n const s = filterBar();\n const m = menu();\n const chosen = props.multiple === false ? (props.value ? [props.value] : []) : props.value;\n const single = props.multiple === false ? options.find((o) => o.value === props.value) : undefined;\n const content = (option: FilterOption) => (\n <>\n {option.label}\n {option.count !== undefined ? <span className={s.optionCount()}>{option.count}</span> : null}\n </>\n );\n return (\n <DropdownMenu.Root defaultOpen={defaultOpen} modal={false}>\n <DropdownMenu.Trigger className={s.trigger({ className })} data-active={chosen.length > 0 ? \"\" : undefined}>\n {single ? `${label}: ${single.label}` : label}\n {props.multiple !== false && chosen.length > 0 ? <span className={s.count()}>{chosen.length}</span> : null}\n <ChevronGlyph className=\"h-4 w-4\" />\n </DropdownMenu.Trigger>\n <DropdownMenu.Content align=\"start\" aria-label={label}>\n {props.multiple === false ? (\n <MenuPrimitive.RadioGroup value={props.value} onValueChange={props.onValueChange}>\n {options.map((option) => (\n <MenuPrimitive.RadioItem key={option.value} value={option.value} disabled={option.disabled} className={m.item()}>\n <span className=\"flex h-4 w-4 shrink-0 items-center justify-center rounded-full border border-border-strong\">\n <MenuPrimitive.ItemIndicator className=\"h-2 w-2 rounded-full bg-accent\" />\n </span>\n {content(option)}\n </MenuPrimitive.RadioItem>\n ))}\n </MenuPrimitive.RadioGroup>\n ) : (\n options.map((option) => (\n <DropdownMenu.CheckboxItem\n key={option.value}\n disabled={option.disabled}\n checked={props.value.includes(option.value)}\n onSelect={(event) => event.preventDefault()}\n onCheckedChange={(checked) =>\n props.onValueChange(checked ? [...props.value, option.value] : props.value.filter((v) => v !== option.value))\n }\n >\n {content(option)}\n </DropdownMenu.CheckboxItem>\n ))\n )}\n </DropdownMenu.Content>\n </DropdownMenu.Root>\n );\n}\n\n/** Filters read from an address: `{ status: [\"failed\", \"running\"], q: [\"cat\"] }` for the keys asked (repeated or comma-separated values). */\nexport function readFilterParams(params: URLSearchParams, keys: readonly string[]): Record<string, string[]> {\n return Object.fromEntries(\n keys.map((key) => [key, params.getAll(key).flatMap((v) => v.split(\",\")).map((v) => v.trim()).filter(Boolean)]),\n );\n}\n\n/** Filters written into an address (a copy of `base`): empty filters removed, several values comma-separated, the page reset. */\nexport function writeFilterParams(base: URLSearchParams, filters: Record<string, string | readonly string[] | null | undefined>, resetKey = \"page\") {\n const next = new URLSearchParams(base);\n for (const [key, value] of Object.entries(filters)) {\n const list = value == null ? [] : typeof value === \"string\" ? [value] : [...value];\n if (list.filter(Boolean).length === 0) next.delete(key);\n else next.set(key, list.filter(Boolean).join(\",\"));\n }\n if (resetKey) next.delete(resetKey);\n return next;\n}\n","// Client (a portal). Radix does the roving focus, the arrows, Home/End, typeahead, Escape and the focus return.\nimport { DropdownMenu as MenuPrimitive } from \"radix-ui\";\nimport type * as React from \"react\";\nimport { tv } from \"tailwind-variants\";\n\nimport { cn } from \"../cn\";\nimport { floating } from \"../floating\";\n\nexport const menu = tv({\n slots: {\n content: cn(floating, \"min-w-48 max-h-(--radix-dropdown-menu-content-available-height) overflow-y-auto p-1 origin-(--radix-dropdown-menu-content-transform-origin)\"),\n item:\n \"relative flex cursor-pointer select-none items-center gap-2 rounded-md px-2.5 py-2 text-sm text-fg outline-hidden transition-colors \" +\n \"data-highlighted:bg-surface-3 data-disabled:pointer-events-none data-disabled:opacity-40 [&>svg]:h-4 [&>svg]:w-4 [&>svg]:shrink-0 \" +\n \"[&>svg]:text-fg-secondary\",\n indicator: \"ml-auto flex h-4 w-4 items-center justify-center text-fg-accent\",\n label: \"px-2.5 pb-1 pt-2 text-[11px] font-semibold uppercase tracking-wider text-fg-secondary\",\n separator: \"-mx-1 my-1 h-px bg-border-subtle\",\n },\n variants: {\n // The label reads in the danger text role (≥ 4.5:1 on the menu surface, both themes); the icon and the highlight\n // carry the danger fill.\n tone: { default: {}, danger: { item: \"text-fg-danger data-highlighted:bg-danger/15 [&>svg]:text-danger\" } },\n },\n defaultVariants: { tone: \"default\" },\n});\nconst s = menu();\n\nexport type DropdownMenuContentProps = React.ComponentProps<typeof MenuPrimitive.Content> & {\n /** The element the portal renders into (default: document.body). */\n container?: HTMLElement | null;\n};\n\nexport function DropdownMenuContent({ sideOffset = 6, align = \"end\", collisionPadding = 8, container, className, ...props }: DropdownMenuContentProps) {\n return (\n <MenuPrimitive.Portal container={container}>\n <MenuPrimitive.Content sideOffset={sideOffset} align={align} collisionPadding={collisionPadding} className={s.content({ className })} {...props} />\n </MenuPrimitive.Portal>\n );\n}\n\nexport type DropdownMenuItemProps = React.ComponentProps<typeof MenuPrimitive.Item> & {\n /** `danger` for a destructive action: the icon and the highlight carry the danger, the label stays legible. */\n tone?: \"default\" | \"danger\";\n};\n\n/** An action. `tone=\"danger\"` for a destructive one; `onSelect` runs it (the menu closes). */\nexport function DropdownMenuItem({ tone, className, ...props }: DropdownMenuItemProps) {\n return <MenuPrimitive.Item data-tone={tone ?? \"default\"} className={menu({ tone }).item({ className })} {...props} />;\n}\n\n/** A toggled option: `checked` + `onCheckedChange`. */\nexport function DropdownMenuCheckboxItem({ className, children, ...props }: React.ComponentProps<typeof MenuPrimitive.CheckboxItem>) {\n return (\n <MenuPrimitive.CheckboxItem className={s.item({ className })} {...props}>\n {children}\n <MenuPrimitive.ItemIndicator className={s.indicator()}>\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"M20 6 9 17l-5-5\" />\n </svg>\n </MenuPrimitive.ItemIndicator>\n </MenuPrimitive.CheckboxItem>\n );\n}\n\nexport function DropdownMenuLabel({ className, ...props }: React.ComponentProps<typeof MenuPrimitive.Label>) {\n return <MenuPrimitive.Label className={s.label({ className })} {...props} />;\n}\n\nexport function DropdownMenuSeparator({ className, ...props }: React.ComponentProps<typeof MenuPrimitive.Separator>) {\n return <MenuPrimitive.Separator className={s.separator({ className })} {...props} />;\n}\n\n/** `DropdownMenu.Root/Trigger/Content/Item/CheckboxItem/Label/Separator/Group`. `open`/`defaultOpen` + `onOpenChange`. */\nexport const DropdownMenu = {\n Root: MenuPrimitive.Root,\n Trigger: MenuPrimitive.Trigger,\n Content: DropdownMenuContent,\n Item: DropdownMenuItem,\n CheckboxItem: DropdownMenuCheckboxItem,\n Label: DropdownMenuLabel,\n Separator: DropdownMenuSeparator,\n Group: MenuPrimitive.Group,\n};\n"],"mappings":"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","names":["MenuPrimitive","tv","jsx","jsxs","tv","s","MenuPrimitive"]}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import * as tailwind_variants from 'tailwind-variants';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
|
|
4
|
+
declare const imageCropper: tailwind_variants.TVReturnType<{
|
|
5
|
+
[key: string]: {
|
|
6
|
+
[key: string]: tailwind_variants.ClassValue | {
|
|
7
|
+
root?: tailwind_variants.ClassValue;
|
|
8
|
+
grid?: tailwind_variants.ClassValue;
|
|
9
|
+
media?: tailwind_variants.ClassValue;
|
|
10
|
+
round?: tailwind_variants.ClassValue;
|
|
11
|
+
frame?: tailwind_variants.ClassValue;
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
} | {
|
|
15
|
+
[x: string]: {
|
|
16
|
+
[x: string]: tailwind_variants.ClassValue | {
|
|
17
|
+
root?: tailwind_variants.ClassValue;
|
|
18
|
+
grid?: tailwind_variants.ClassValue;
|
|
19
|
+
media?: tailwind_variants.ClassValue;
|
|
20
|
+
round?: tailwind_variants.ClassValue;
|
|
21
|
+
frame?: tailwind_variants.ClassValue;
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
} | {}, {
|
|
25
|
+
root: string;
|
|
26
|
+
frame: string;
|
|
27
|
+
media: string;
|
|
28
|
+
grid: string;
|
|
29
|
+
round: string;
|
|
30
|
+
}, undefined, {
|
|
31
|
+
[key: string]: {
|
|
32
|
+
[key: string]: tailwind_variants.ClassValue | {
|
|
33
|
+
root?: tailwind_variants.ClassValue;
|
|
34
|
+
grid?: tailwind_variants.ClassValue;
|
|
35
|
+
media?: tailwind_variants.ClassValue;
|
|
36
|
+
round?: tailwind_variants.ClassValue;
|
|
37
|
+
frame?: tailwind_variants.ClassValue;
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
} | {}, {
|
|
41
|
+
root: string;
|
|
42
|
+
frame: string;
|
|
43
|
+
media: string;
|
|
44
|
+
grid: string;
|
|
45
|
+
round: string;
|
|
46
|
+
}, tailwind_variants.TVReturnTypeLike<unknown, {
|
|
47
|
+
root: string;
|
|
48
|
+
frame: string;
|
|
49
|
+
media: string;
|
|
50
|
+
grid: string;
|
|
51
|
+
round: string;
|
|
52
|
+
}>>;
|
|
53
|
+
/** Where the frame looks: the focus point (0–1 from the left and the top of the source) and the zoom (1 = covers). */
|
|
54
|
+
type CropValue = {
|
|
55
|
+
x: number;
|
|
56
|
+
y: number;
|
|
57
|
+
zoom: number;
|
|
58
|
+
};
|
|
59
|
+
/** The crop in pixels of the source: what to cut from an image of `width` × `height` for a frame of `aspect`. */
|
|
60
|
+
declare function cropRect(value: CropValue, source: {
|
|
61
|
+
width: number;
|
|
62
|
+
height: number;
|
|
63
|
+
}, aspect: number): {
|
|
64
|
+
x: number;
|
|
65
|
+
y: number;
|
|
66
|
+
width: number;
|
|
67
|
+
height: number;
|
|
68
|
+
};
|
|
69
|
+
type ImageCropperLabels = {
|
|
70
|
+
/** The frame's accessible name ("Crop area"). */
|
|
71
|
+
frame: string;
|
|
72
|
+
/** How to use it with the keyboard, read with the frame ("Use the arrow keys to move the picture, + and − to zoom."). */
|
|
73
|
+
instructions: string;
|
|
74
|
+
/** The zoom slider ("Zoom"). */
|
|
75
|
+
zoom: string;
|
|
76
|
+
};
|
|
77
|
+
declare const imageCropperLabels: ImageCropperLabels;
|
|
78
|
+
type ImageCropperProps = {
|
|
79
|
+
/** The picture to crop. Or `children`: a `<video>` (with the class it is given through `mediaClassName`). */
|
|
80
|
+
src?: string;
|
|
81
|
+
/** The picture's alternative text, passed translated (empty when the frame's name says enough). */
|
|
82
|
+
alt?: string;
|
|
83
|
+
/** A media element instead of `src` — a `<video>` to choose the frame of a clip. Its natural size is in `naturalSize`. */
|
|
84
|
+
children?: React.ReactElement<{
|
|
85
|
+
className?: string;
|
|
86
|
+
style?: React.CSSProperties;
|
|
87
|
+
}>;
|
|
88
|
+
/** The source's size when `children` is a video: `{ width: video.videoWidth, height: video.videoHeight }`. */
|
|
89
|
+
naturalSize?: {
|
|
90
|
+
width: number;
|
|
91
|
+
height: number;
|
|
92
|
+
};
|
|
93
|
+
/** The frame's width / height: 1 (a square), 9 / 16 (vertical), 3 (a banner). */
|
|
94
|
+
aspect: number;
|
|
95
|
+
/** A round mask over the square frame (a profile picture). */
|
|
96
|
+
round?: boolean;
|
|
97
|
+
/** The crop (controlled). */
|
|
98
|
+
value?: CropValue;
|
|
99
|
+
/** The crop at first, uncontrolled. Default: centred, no zoom. */
|
|
100
|
+
defaultValue?: CropValue;
|
|
101
|
+
/** Called on every move. */
|
|
102
|
+
onValueChange?: (value: CropValue) => void;
|
|
103
|
+
/** The largest zoom. Default 4. */
|
|
104
|
+
maxZoom?: number;
|
|
105
|
+
/** Shows the zoom slider under the frame. Default `true`. */
|
|
106
|
+
showZoom?: boolean;
|
|
107
|
+
/** The words, passed translated; English by default. */
|
|
108
|
+
labels?: Partial<ImageCropperLabels>;
|
|
109
|
+
/** Classes merged on the root. */
|
|
110
|
+
className?: string;
|
|
111
|
+
};
|
|
112
|
+
/** Chooses what a fixed frame keeps of a picture or a video: drag, zoom, keyboard; a focus point and a zoom out. */
|
|
113
|
+
declare function ImageCropper({ src, alt, children, naturalSize, aspect, round, value: valueProp, defaultValue, onValueChange, maxZoom, showZoom, labels: given, className, }: ImageCropperProps): React.JSX.Element;
|
|
114
|
+
|
|
115
|
+
export { type CropValue, ImageCropper, type ImageCropperLabels, type ImageCropperProps, cropRect, imageCropper, imageCropperLabels };
|