@nqmcreative/ui 0.1.1 → 0.2.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 +89 -34
- package/cli/index.mjs +67 -30
- package/dist/core/calendar.d.ts +3 -0
- package/dist/core/calendar.js +36 -0
- package/dist/core/dialog.svelte.d.ts +14 -0
- package/dist/core/dialog.svelte.js +23 -0
- package/dist/core/files.d.ts +35 -0
- package/dist/core/files.js +62 -0
- package/dist/core/index.d.ts +37 -0
- package/dist/core/index.js +35 -0
- package/dist/core/list.svelte.d.ts +41 -0
- package/dist/core/list.svelte.js +77 -0
- package/dist/core/number.d.ts +7 -0
- package/dist/core/number.js +17 -0
- package/dist/core/pagination.d.ts +6 -0
- package/dist/core/pagination.js +21 -0
- package/dist/core/password.d.ts +8 -0
- package/dist/core/password.js +23 -0
- package/dist/core/table.d.ts +22 -0
- package/dist/core/table.js +41 -0
- package/dist/core/theme.svelte.d.ts +16 -0
- package/dist/core/theme.svelte.js +34 -0
- package/dist/core/trigger.d.ts +35 -0
- package/dist/core/trigger.js +77 -0
- package/dist/index.d.ts +11 -103
- package/dist/index.js +11 -75
- package/dist/{components → styles/matte}/AccordionItem.svelte +1 -1
- package/dist/{components → styles/matte}/AccordionItem.svelte.d.ts +1 -1
- package/dist/{components → styles/matte}/Alert.svelte +8 -2
- package/dist/{components → styles/matte}/Alert.svelte.d.ts +1 -1
- package/dist/{components → styles/matte}/Avatar.svelte +1 -1
- package/dist/{components → styles/matte}/Avatar.svelte.d.ts +1 -1
- package/dist/{components → styles/matte}/Badge.svelte +8 -3
- package/dist/{components → styles/matte}/Badge.svelte.d.ts +1 -3
- package/dist/{components → styles/matte}/Breadcrumb.svelte +2 -2
- package/dist/{components → styles/matte}/Breadcrumb.svelte.d.ts +1 -1
- package/dist/{components → styles/matte}/Button.svelte +1 -1
- package/dist/{components → styles/matte}/Button.svelte.d.ts +1 -1
- package/dist/styles/matte/CTASection.svelte +76 -0
- package/dist/styles/matte/CTASection.svelte.d.ts +18 -0
- package/dist/{components → styles/matte}/Calendar.svelte +7 -17
- package/dist/{components → styles/matte}/Calendar.svelte.d.ts +1 -1
- package/dist/{components → styles/matte}/Card.svelte +1 -1
- package/dist/{components → styles/matte}/Card.svelte.d.ts +1 -1
- package/dist/{components → styles/matte}/Checkbox.svelte +1 -1
- package/dist/{components → styles/matte}/Checkbox.svelte.d.ts +1 -1
- package/dist/{components → styles/matte}/CheckboxGroup.svelte +1 -1
- package/dist/{components → styles/matte}/CheckboxGroup.svelte.d.ts +1 -1
- package/dist/{components → styles/matte}/Combobox.svelte +31 -42
- package/dist/{components → styles/matte}/Combobox.svelte.d.ts +1 -1
- package/dist/{components → styles/matte}/CommandPalette.svelte +26 -41
- package/dist/{components → styles/matte}/CommandPalette.svelte.d.ts +1 -1
- package/dist/{components → styles/matte}/ConfirmDialog.svelte +2 -2
- package/dist/{components → styles/matte}/ConfirmDialog.svelte.d.ts +2 -2
- package/dist/{components → styles/matte}/ContextMenu.svelte +5 -7
- package/dist/{components → styles/matte}/DatePicker.svelte +7 -11
- package/dist/{components → styles/matte}/DatePicker.svelte.d.ts +1 -1
- package/dist/{components → styles/matte}/Drawer.svelte +4 -7
- package/dist/{components → styles/matte}/Dropdown.svelte +18 -36
- package/dist/{components → styles/matte}/Dropdown.svelte.d.ts +1 -1
- package/dist/{components → styles/matte}/Dropzone.svelte +11 -43
- package/dist/{components → styles/matte}/Dropzone.svelte.d.ts +2 -5
- package/dist/{components → styles/matte}/EmptyState.svelte +1 -1
- package/dist/{components → styles/matte}/EmptyState.svelte.d.ts +1 -1
- package/dist/styles/matte/FeatureCard.svelte +58 -0
- package/dist/styles/matte/FeatureCard.svelte.d.ts +16 -0
- package/dist/styles/matte/FeatureGrid.svelte +62 -0
- package/dist/styles/matte/FeatureGrid.svelte.d.ts +16 -0
- package/dist/{components → styles/matte}/Footer.svelte +1 -1
- package/dist/{components → styles/matte}/Footer.svelte.d.ts +1 -1
- package/dist/styles/matte/HeroSection.svelte +80 -0
- package/dist/styles/matte/HeroSection.svelte.d.ts +21 -0
- package/dist/{components → styles/matte}/Input.svelte +1 -1
- package/dist/{components → styles/matte}/Input.svelte.d.ts +1 -1
- package/dist/{components → styles/matte}/Link.svelte +1 -1
- package/dist/{components → styles/matte}/Link.svelte.d.ts +1 -1
- package/dist/{components → styles/matte}/LocaleProvider.svelte +1 -1
- package/dist/{components → styles/matte}/LocaleProvider.svelte.d.ts +1 -1
- package/dist/styles/matte/LogoCloud.svelte +61 -0
- package/dist/styles/matte/LogoCloud.svelte.d.ts +19 -0
- package/dist/{components → styles/matte}/MenuItem.svelte +1 -1
- package/dist/{components → styles/matte}/MenuItem.svelte.d.ts +1 -1
- package/dist/{components → styles/matte}/Modal.svelte +4 -7
- package/dist/{components → styles/matte}/MultiSelect.svelte +18 -37
- package/dist/{components → styles/matte}/MultiSelect.svelte.d.ts +1 -1
- package/dist/{components → styles/matte}/Navbar.svelte +2 -2
- package/dist/{components → styles/matte}/Navbar.svelte.d.ts +1 -1
- package/dist/{components → styles/matte}/NumberInput.svelte +4 -8
- package/dist/{components → styles/matte}/NumberInput.svelte.d.ts +1 -1
- package/dist/{components → styles/matte}/Pagination.svelte +4 -15
- package/dist/{components → styles/matte}/Pagination.svelte.d.ts +1 -1
- package/dist/{components → styles/matte}/PasswordInput.svelte +4 -13
- package/dist/{components → styles/matte}/PasswordInput.svelte.d.ts +1 -1
- package/dist/{components → styles/matte}/Popover.svelte +16 -41
- package/dist/{components → styles/matte}/Popover.svelte.d.ts +1 -1
- package/dist/styles/matte/PricingCard.svelte +107 -0
- package/dist/styles/matte/PricingCard.svelte.d.ts +25 -0
- package/dist/{components → styles/matte}/Progress.svelte +3 -2
- package/dist/{components → styles/matte}/Progress.svelte.d.ts +1 -1
- package/dist/{components → styles/matte}/Radio.svelte +1 -1
- package/dist/{components → styles/matte}/Radio.svelte.d.ts +1 -1
- package/dist/{components → styles/matte}/RadioGroup.svelte +1 -1
- package/dist/{components → styles/matte}/RadioGroup.svelte.d.ts +1 -1
- package/dist/{components → styles/matte}/SegmentedControl.svelte +1 -1
- package/dist/{components → styles/matte}/SegmentedControl.svelte.d.ts +1 -1
- package/dist/{components → styles/matte}/Select.svelte +1 -1
- package/dist/{components → styles/matte}/Select.svelte.d.ts +1 -1
- package/dist/{components → styles/matte}/Sidebar.svelte +2 -2
- package/dist/{components → styles/matte}/Sidebar.svelte.d.ts +1 -1
- package/dist/{components → styles/matte}/Slider.svelte +3 -2
- package/dist/{components → styles/matte}/Slider.svelte.d.ts +1 -1
- package/dist/{components → styles/matte}/Spinner.svelte +2 -2
- package/dist/{components → styles/matte}/Spinner.svelte.d.ts +1 -1
- package/dist/{components → styles/matte}/Stat.svelte +1 -1
- package/dist/{components → styles/matte}/Stat.svelte.d.ts +1 -1
- package/dist/styles/matte/StatsBand.svelte +57 -0
- package/dist/styles/matte/StatsBand.svelte.d.ts +20 -0
- package/dist/{components → styles/matte}/Steps.svelte +1 -1
- package/dist/{components → styles/matte}/Steps.svelte.d.ts +1 -1
- package/dist/{components → styles/matte}/Switch.svelte +1 -1
- package/dist/{components → styles/matte}/Switch.svelte.d.ts +1 -1
- package/dist/{components → styles/matte}/Table.svelte +6 -28
- package/dist/{components → styles/matte}/Table.svelte.d.ts +2 -6
- package/dist/{components → styles/matte}/Tabs.svelte +8 -1
- package/dist/{components → styles/matte}/Tabs.svelte.d.ts +1 -1
- package/dist/styles/matte/Testimonial.svelte +67 -0
- package/dist/styles/matte/Testimonial.svelte.d.ts +19 -0
- package/dist/{components → styles/matte}/Textarea.svelte +1 -1
- package/dist/{components → styles/matte}/Textarea.svelte.d.ts +1 -1
- package/dist/{components → styles/matte}/ThemeToggle.svelte +14 -27
- package/dist/{components → styles/matte}/ThemeToggle.svelte.d.ts +2 -6
- package/dist/{components → styles/matte}/Toaster.svelte +4 -4
- package/dist/styles/matte/index.d.ts +109 -0
- package/dist/styles/matte/index.js +83 -0
- package/dist/styles/paper/Accordion.svelte +17 -0
- package/dist/styles/paper/Accordion.svelte.d.ts +8 -0
- package/dist/styles/paper/AccordionItem.svelte +46 -0
- package/dist/styles/paper/AccordionItem.svelte.d.ts +14 -0
- package/dist/styles/paper/Alert.svelte +99 -0
- package/dist/styles/paper/Alert.svelte.d.ts +18 -0
- package/dist/styles/paper/Avatar.svelte +60 -0
- package/dist/styles/paper/Avatar.svelte.d.ts +16 -0
- package/dist/styles/paper/AvatarGroup.svelte +17 -0
- package/dist/styles/paper/AvatarGroup.svelte.d.ts +8 -0
- package/dist/styles/paper/Badge.svelte +58 -0
- package/dist/styles/paper/Badge.svelte.d.ts +16 -0
- package/dist/styles/paper/Breadcrumb.svelte +49 -0
- package/dist/styles/paper/Breadcrumb.svelte.d.ts +14 -0
- package/dist/styles/paper/Button.svelte +96 -0
- package/dist/styles/paper/Button.svelte.d.ts +21 -0
- package/dist/styles/paper/CTASection.svelte +76 -0
- package/dist/styles/paper/CTASection.svelte.d.ts +18 -0
- package/dist/styles/paper/Calendar.svelte +202 -0
- package/dist/styles/paper/Calendar.svelte.d.ts +22 -0
- package/dist/styles/paper/Card.svelte +78 -0
- package/dist/styles/paper/Card.svelte.d.ts +21 -0
- package/dist/styles/paper/Checkbox.svelte +75 -0
- package/dist/styles/paper/Checkbox.svelte.d.ts +15 -0
- package/dist/styles/paper/CheckboxGroup.svelte +111 -0
- package/dist/styles/paper/CheckboxGroup.svelte.d.ts +30 -0
- package/dist/styles/paper/Combobox.svelte +266 -0
- package/dist/styles/paper/Combobox.svelte.d.ts +29 -0
- package/dist/styles/paper/CommandPalette.svelte +209 -0
- package/dist/styles/paper/CommandPalette.svelte.d.ts +32 -0
- package/dist/styles/paper/ConfirmDialog.svelte +97 -0
- package/dist/styles/paper/ConfirmDialog.svelte.d.ts +22 -0
- package/dist/styles/paper/ContextMenu.svelte +88 -0
- package/dist/styles/paper/ContextMenu.svelte.d.ts +15 -0
- package/dist/styles/paper/DatePicker.svelte +192 -0
- package/dist/styles/paper/DatePicker.svelte.d.ts +23 -0
- package/dist/styles/paper/Divider.svelte +30 -0
- package/dist/styles/paper/Divider.svelte.d.ts +10 -0
- package/dist/styles/paper/Drawer.svelte +165 -0
- package/dist/styles/paper/Drawer.svelte.d.ts +20 -0
- package/dist/styles/paper/Dropdown.svelte +94 -0
- package/dist/styles/paper/Dropdown.svelte.d.ts +23 -0
- package/dist/styles/paper/Dropzone.svelte +210 -0
- package/dist/styles/paper/Dropzone.svelte.d.ts +29 -0
- package/dist/styles/paper/EmptyState.svelte +71 -0
- package/dist/styles/paper/EmptyState.svelte.d.ts +18 -0
- package/dist/styles/paper/FeatureCard.svelte +58 -0
- package/dist/styles/paper/FeatureCard.svelte.d.ts +16 -0
- package/dist/styles/paper/FeatureGrid.svelte +62 -0
- package/dist/styles/paper/FeatureGrid.svelte.d.ts +16 -0
- package/dist/styles/paper/Field.svelte +42 -0
- package/dist/styles/paper/Field.svelte.d.ts +16 -0
- package/dist/styles/paper/Footer.svelte +91 -0
- package/dist/styles/paper/Footer.svelte.d.ts +25 -0
- package/dist/styles/paper/HeroSection.svelte +86 -0
- package/dist/styles/paper/HeroSection.svelte.d.ts +21 -0
- package/dist/styles/paper/Input.svelte +60 -0
- package/dist/styles/paper/Input.svelte.d.ts +18 -0
- package/dist/styles/paper/InputGroup.svelte +20 -0
- package/dist/styles/paper/InputGroup.svelte.d.ts +11 -0
- package/dist/styles/paper/Kbd.svelte +17 -0
- package/dist/styles/paper/Kbd.svelte.d.ts +8 -0
- package/dist/styles/paper/Label.svelte +16 -0
- package/dist/styles/paper/Label.svelte.d.ts +9 -0
- package/dist/styles/paper/Link.svelte +44 -0
- package/dist/styles/paper/Link.svelte.d.ts +16 -0
- package/dist/styles/paper/LocaleProvider.svelte +24 -0
- package/dist/styles/paper/LocaleProvider.svelte.d.ts +10 -0
- package/dist/styles/paper/LogoCloud.svelte +61 -0
- package/dist/styles/paper/LogoCloud.svelte.d.ts +19 -0
- package/dist/styles/paper/MenuItem.svelte +64 -0
- package/dist/styles/paper/MenuItem.svelte.d.ts +18 -0
- package/dist/styles/paper/MenuSeparator.svelte +22 -0
- package/dist/styles/paper/MenuSeparator.svelte.d.ts +8 -0
- package/dist/styles/paper/Modal.svelte +118 -0
- package/dist/styles/paper/Modal.svelte.d.ts +21 -0
- package/dist/styles/paper/MultiSelect.svelte +287 -0
- package/dist/styles/paper/MultiSelect.svelte.d.ts +31 -0
- package/dist/styles/paper/Navbar.svelte +201 -0
- package/dist/styles/paper/Navbar.svelte.d.ts +33 -0
- package/dist/styles/paper/NumberInput.svelte +96 -0
- package/dist/styles/paper/NumberInput.svelte.d.ts +15 -0
- package/dist/styles/paper/Pagination.svelte +83 -0
- package/dist/styles/paper/Pagination.svelte.d.ts +15 -0
- package/dist/styles/paper/PasswordInput.svelte +100 -0
- package/dist/styles/paper/PasswordInput.svelte.d.ts +12 -0
- package/dist/styles/paper/Popover.svelte +89 -0
- package/dist/styles/paper/Popover.svelte.d.ts +19 -0
- package/dist/styles/paper/PricingCard.svelte +108 -0
- package/dist/styles/paper/PricingCard.svelte.d.ts +25 -0
- package/dist/styles/paper/Progress.svelte +59 -0
- package/dist/styles/paper/Progress.svelte.d.ts +17 -0
- package/dist/styles/paper/Radio.svelte +55 -0
- package/dist/styles/paper/Radio.svelte.d.ts +15 -0
- package/dist/styles/paper/RadioGroup.svelte +95 -0
- package/dist/styles/paper/RadioGroup.svelte.d.ts +27 -0
- package/dist/styles/paper/SegmentedControl.svelte +88 -0
- package/dist/styles/paper/SegmentedControl.svelte.d.ts +25 -0
- package/dist/styles/paper/Select.svelte +61 -0
- package/dist/styles/paper/Select.svelte.d.ts +20 -0
- package/dist/styles/paper/Sidebar.svelte +206 -0
- package/dist/styles/paper/Sidebar.svelte.d.ts +36 -0
- package/dist/styles/paper/Skeleton.svelte +46 -0
- package/dist/styles/paper/Skeleton.svelte.d.ts +13 -0
- package/dist/styles/paper/Slider.svelte +80 -0
- package/dist/styles/paper/Slider.svelte.d.ts +19 -0
- package/dist/styles/paper/Spinner.svelte +34 -0
- package/dist/styles/paper/Spinner.svelte.d.ts +13 -0
- package/dist/styles/paper/Stat.svelte +51 -0
- package/dist/styles/paper/Stat.svelte.d.ts +16 -0
- package/dist/styles/paper/StatsBand.svelte +63 -0
- package/dist/styles/paper/StatsBand.svelte.d.ts +20 -0
- package/dist/styles/paper/Steps.svelte +141 -0
- package/dist/styles/paper/Steps.svelte.d.ts +24 -0
- package/dist/styles/paper/Switch.svelte +64 -0
- package/dist/styles/paper/Switch.svelte.d.ts +14 -0
- package/dist/styles/paper/Table.svelte +217 -0
- package/dist/styles/paper/Table.svelte.d.ts +66 -0
- package/dist/styles/paper/Tabs.svelte +102 -0
- package/dist/styles/paper/Tabs.svelte.d.ts +23 -0
- package/dist/styles/paper/Testimonial.svelte +64 -0
- package/dist/styles/paper/Testimonial.svelte.d.ts +19 -0
- package/dist/styles/paper/Textarea.svelte +54 -0
- package/dist/styles/paper/Textarea.svelte.d.ts +12 -0
- package/dist/styles/paper/ThemeToggle.svelte +130 -0
- package/dist/styles/paper/ThemeToggle.svelte.d.ts +16 -0
- package/dist/styles/paper/Toaster.svelte +132 -0
- package/dist/styles/paper/Toaster.svelte.d.ts +8 -0
- package/dist/styles/paper/Tooltip.svelte +33 -0
- package/dist/styles/paper/Tooltip.svelte.d.ts +12 -0
- package/dist/styles/paper/fonts.css +46 -0
- package/dist/styles/paper/index.d.ts +109 -0
- package/dist/styles/paper/index.js +83 -0
- package/dist/styles/paper/theme.css +222 -0
- package/package.json +531 -201
- package/registry.json +658 -339
- /package/dist/{actions → core/actions}/anchor.d.ts +0 -0
- /package/dist/{actions → core/actions}/anchor.js +0 -0
- /package/dist/{actions → core/actions}/dismissable.d.ts +0 -0
- /package/dist/{actions → core/actions}/dismissable.js +0 -0
- /package/dist/{date.d.ts → core/date.d.ts} +0 -0
- /package/dist/{date.js → core/date.js} +0 -0
- /package/dist/{locale.svelte.d.ts → core/locale.svelte.d.ts} +0 -0
- /package/dist/{locale.svelte.js → core/locale.svelte.js} +0 -0
- /package/dist/{toast.svelte.d.ts → core/toast.svelte.d.ts} +0 -0
- /package/dist/{toast.svelte.js → core/toast.svelte.js} +0 -0
- /package/dist/{tones.d.ts → core/tones.d.ts} +0 -0
- /package/dist/{tones.js → core/tones.js} +0 -0
- /package/dist/{components → styles/matte}/Accordion.svelte +0 -0
- /package/dist/{components → styles/matte}/Accordion.svelte.d.ts +0 -0
- /package/dist/{components → styles/matte}/AvatarGroup.svelte +0 -0
- /package/dist/{components → styles/matte}/AvatarGroup.svelte.d.ts +0 -0
- /package/dist/{components → styles/matte}/ContextMenu.svelte.d.ts +0 -0
- /package/dist/{components → styles/matte}/Divider.svelte +0 -0
- /package/dist/{components → styles/matte}/Divider.svelte.d.ts +0 -0
- /package/dist/{components → styles/matte}/Drawer.svelte.d.ts +0 -0
- /package/dist/{components → styles/matte}/Field.svelte +0 -0
- /package/dist/{components → styles/matte}/Field.svelte.d.ts +0 -0
- /package/dist/{components → styles/matte}/InputGroup.svelte +0 -0
- /package/dist/{components → styles/matte}/InputGroup.svelte.d.ts +0 -0
- /package/dist/{components → styles/matte}/Kbd.svelte +0 -0
- /package/dist/{components → styles/matte}/Kbd.svelte.d.ts +0 -0
- /package/dist/{components → styles/matte}/Label.svelte +0 -0
- /package/dist/{components → styles/matte}/Label.svelte.d.ts +0 -0
- /package/dist/{components → styles/matte}/MenuSeparator.svelte +0 -0
- /package/dist/{components → styles/matte}/MenuSeparator.svelte.d.ts +0 -0
- /package/dist/{components → styles/matte}/Modal.svelte.d.ts +0 -0
- /package/dist/{components → styles/matte}/Skeleton.svelte +0 -0
- /package/dist/{components → styles/matte}/Skeleton.svelte.d.ts +0 -0
- /package/dist/{components → styles/matte}/Toaster.svelte.d.ts +0 -0
- /package/dist/{components → styles/matte}/Tooltip.svelte +0 -0
- /package/dist/{components → styles/matte}/Tooltip.svelte.d.ts +0 -0
- /package/dist/{fonts.css → styles/matte/fonts.css} +0 -0
- /package/dist/{theme.css → styles/matte/theme.css} +0 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
|
+
|
|
4
|
+
export type DividerOrientation = 'horizontal' | 'vertical';
|
|
5
|
+
|
|
6
|
+
interface Props extends HTMLAttributes<HTMLDivElement> {
|
|
7
|
+
orientation?: DividerOrientation;
|
|
8
|
+
/** Centred caption drawn over a horizontal rule. */
|
|
9
|
+
label?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
let { orientation = 'horizontal', label, class: className = '', ...rest }: Props = $props();
|
|
13
|
+
</script>
|
|
14
|
+
|
|
15
|
+
{#if orientation === 'vertical'}
|
|
16
|
+
<div
|
|
17
|
+
role="separator"
|
|
18
|
+
aria-orientation="vertical"
|
|
19
|
+
class="w-px self-stretch bg-hairline {className}"
|
|
20
|
+
{...rest}
|
|
21
|
+
></div>
|
|
22
|
+
{:else if label}
|
|
23
|
+
<div class="flex items-center gap-3 {className}" {...rest}>
|
|
24
|
+
<span class="h-px flex-1 bg-hairline"></span>
|
|
25
|
+
<span class="font-sans text-xs font-medium text-text-muted">{label}</span>
|
|
26
|
+
<span class="h-px flex-1 bg-hairline"></span>
|
|
27
|
+
</div>
|
|
28
|
+
{:else}
|
|
29
|
+
<div role="separator" class="h-px w-full bg-hairline {className}" {...rest}></div>
|
|
30
|
+
{/if}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
2
|
+
export type DividerOrientation = 'horizontal' | 'vertical';
|
|
3
|
+
interface Props extends HTMLAttributes<HTMLDivElement> {
|
|
4
|
+
orientation?: DividerOrientation;
|
|
5
|
+
/** Centred caption drawn over a horizontal rule. */
|
|
6
|
+
label?: string;
|
|
7
|
+
}
|
|
8
|
+
declare const Divider: import("svelte").Component<Props, {}, "">;
|
|
9
|
+
type Divider = ReturnType<typeof Divider>;
|
|
10
|
+
export default Divider;
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import type { HTMLDialogAttributes } from 'svelte/elements';
|
|
4
|
+
import { useLocale } from '../../core/locale.svelte.js';
|
|
5
|
+
import { isBackdropClick, syncDialog } from '../../core/dialog.svelte.js';
|
|
6
|
+
|
|
7
|
+
export type DrawerSide = 'left' | 'right' | 'top' | 'bottom';
|
|
8
|
+
export type DrawerSize = 'sm' | 'md' | 'lg' | 'full';
|
|
9
|
+
|
|
10
|
+
interface Props extends HTMLDialogAttributes {
|
|
11
|
+
/** Bindable. Built on the native `<dialog>`, so it gets the top layer,
|
|
12
|
+
* the backdrop, Escape handling and focus containment for free. */
|
|
13
|
+
open?: boolean;
|
|
14
|
+
side?: DrawerSide;
|
|
15
|
+
size?: DrawerSize;
|
|
16
|
+
title?: string;
|
|
17
|
+
description?: string;
|
|
18
|
+
dismissible?: boolean;
|
|
19
|
+
footer?: Snippet;
|
|
20
|
+
onclose?: () => void;
|
|
21
|
+
children: Snippet;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
let {
|
|
25
|
+
open = $bindable(false),
|
|
26
|
+
side = 'right',
|
|
27
|
+
size = 'md',
|
|
28
|
+
title,
|
|
29
|
+
description,
|
|
30
|
+
dismissible = true,
|
|
31
|
+
footer,
|
|
32
|
+
onclose,
|
|
33
|
+
class: className = '',
|
|
34
|
+
children,
|
|
35
|
+
...rest
|
|
36
|
+
}: Props = $props();
|
|
37
|
+
|
|
38
|
+
const t = useLocale();
|
|
39
|
+
let dialog: HTMLDialogElement | null = $state(null);
|
|
40
|
+
|
|
41
|
+
const horizontal = $derived(side === 'left' || side === 'right');
|
|
42
|
+
|
|
43
|
+
const widths: Record<DrawerSize, string> = {
|
|
44
|
+
sm: 'w-[min(20rem,100vw)]',
|
|
45
|
+
md: 'w-[min(28rem,100vw)]',
|
|
46
|
+
lg: 'w-[min(40rem,100vw)]',
|
|
47
|
+
full: 'w-screen'
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const heights: Record<DrawerSize, string> = {
|
|
51
|
+
sm: 'h-[min(16rem,100vh)]',
|
|
52
|
+
md: 'h-[min(24rem,100vh)]',
|
|
53
|
+
lg: 'h-[min(36rem,100vh)]',
|
|
54
|
+
full: 'h-screen'
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const sides: Record<DrawerSide, string> = {
|
|
58
|
+
left: 'mr-auto ml-0 h-screen max-h-screen border-r',
|
|
59
|
+
right: 'mr-0 ml-auto h-screen max-h-screen border-l',
|
|
60
|
+
top: 'mt-0 mb-auto w-screen max-w-screen border-b',
|
|
61
|
+
bottom: 'mt-auto mb-0 w-screen max-w-screen border-t'
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const slide: Record<DrawerSide, string> = {
|
|
65
|
+
left: 'animate-[drawer-left_220ms_cubic-bezier(0,0,0.2,1)]',
|
|
66
|
+
right: 'animate-[drawer-right_220ms_cubic-bezier(0,0,0.2,1)]',
|
|
67
|
+
top: 'animate-[drawer-top_220ms_cubic-bezier(0,0,0.2,1)]',
|
|
68
|
+
bottom: 'animate-[drawer-bottom_220ms_cubic-bezier(0,0,0.2,1)]'
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
$effect(() => syncDialog(dialog, open));
|
|
72
|
+
|
|
73
|
+
function close() {
|
|
74
|
+
open = false;
|
|
75
|
+
onclose?.();
|
|
76
|
+
}
|
|
77
|
+
</script>
|
|
78
|
+
|
|
79
|
+
<dialog
|
|
80
|
+
bind:this={dialog}
|
|
81
|
+
onclose={close}
|
|
82
|
+
onclick={(event) => {
|
|
83
|
+
if (dismissible && isBackdropClick(event, dialog)) close();
|
|
84
|
+
}}
|
|
85
|
+
oncancel={(event) => {
|
|
86
|
+
if (!dismissible) event.preventDefault();
|
|
87
|
+
}}
|
|
88
|
+
class="max-h-none max-w-none border-hairline bg-bg p-0 text-text shadow-2xl backdrop:bg-text/30 backdrop:backdrop-blur-[1px]
|
|
89
|
+
{sides[side]} {horizontal ? widths[size] : heights[size]} {slide[side]} {className}"
|
|
90
|
+
{...rest}
|
|
91
|
+
>
|
|
92
|
+
<div class="flex h-full flex-col">
|
|
93
|
+
{#if title || dismissible}
|
|
94
|
+
<div class="flex items-start gap-4 border-b border-hairline px-5 py-4">
|
|
95
|
+
<div class="flex min-w-0 flex-1 flex-col gap-1">
|
|
96
|
+
{#if title}
|
|
97
|
+
<h2 class="font-heading text-base font-semibold text-text">{title}</h2>
|
|
98
|
+
{/if}
|
|
99
|
+
{#if description}
|
|
100
|
+
<p class="font-sans text-[13px] text-text-muted">{description}</p>
|
|
101
|
+
{/if}
|
|
102
|
+
</div>
|
|
103
|
+
{#if dismissible}
|
|
104
|
+
<button
|
|
105
|
+
type="button"
|
|
106
|
+
onclick={close}
|
|
107
|
+
aria-label={t.current.close}
|
|
108
|
+
class="-m-1 shrink-0 rounded-md p-1 text-text-muted transition-colors duration-150 hover:bg-bg-inset hover:text-text focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-brand"
|
|
109
|
+
>
|
|
110
|
+
<svg class="size-4" viewBox="0 0 14 14" fill="none" aria-hidden="true">
|
|
111
|
+
<path
|
|
112
|
+
d="m2 2 10 10M12 2 2 12"
|
|
113
|
+
stroke="currentColor"
|
|
114
|
+
stroke-width="1.6"
|
|
115
|
+
stroke-linecap="round"
|
|
116
|
+
/>
|
|
117
|
+
</svg>
|
|
118
|
+
</button>
|
|
119
|
+
{/if}
|
|
120
|
+
</div>
|
|
121
|
+
{/if}
|
|
122
|
+
|
|
123
|
+
<div
|
|
124
|
+
class="flex-1 overflow-y-auto px-5 py-4 font-sans text-sm leading-relaxed text-text-secondary"
|
|
125
|
+
>
|
|
126
|
+
{@render children()}
|
|
127
|
+
</div>
|
|
128
|
+
|
|
129
|
+
{#if footer}
|
|
130
|
+
<div
|
|
131
|
+
class="flex items-center justify-end gap-2 border-t border-hairline bg-bg-alt px-5 py-3.5"
|
|
132
|
+
>
|
|
133
|
+
{@render footer()}
|
|
134
|
+
</div>
|
|
135
|
+
{/if}
|
|
136
|
+
</div>
|
|
137
|
+
</dialog>
|
|
138
|
+
|
|
139
|
+
<style>
|
|
140
|
+
@keyframes -global-drawer-right {
|
|
141
|
+
from {
|
|
142
|
+
transform: translateX(100%);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
@keyframes -global-drawer-left {
|
|
146
|
+
from {
|
|
147
|
+
transform: translateX(-100%);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
@keyframes -global-drawer-top {
|
|
151
|
+
from {
|
|
152
|
+
transform: translateY(-100%);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
@keyframes -global-drawer-bottom {
|
|
156
|
+
from {
|
|
157
|
+
transform: translateY(100%);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
@media (prefers-reduced-motion: reduce) {
|
|
161
|
+
dialog {
|
|
162
|
+
animation: none;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
</style>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { HTMLDialogAttributes } from 'svelte/elements';
|
|
3
|
+
export type DrawerSide = 'left' | 'right' | 'top' | 'bottom';
|
|
4
|
+
export type DrawerSize = 'sm' | 'md' | 'lg' | 'full';
|
|
5
|
+
interface Props extends HTMLDialogAttributes {
|
|
6
|
+
/** Bindable. Built on the native `<dialog>`, so it gets the top layer,
|
|
7
|
+
* the backdrop, Escape handling and focus containment for free. */
|
|
8
|
+
open?: boolean;
|
|
9
|
+
side?: DrawerSide;
|
|
10
|
+
size?: DrawerSize;
|
|
11
|
+
title?: string;
|
|
12
|
+
description?: string;
|
|
13
|
+
dismissible?: boolean;
|
|
14
|
+
footer?: Snippet;
|
|
15
|
+
onclose?: () => void;
|
|
16
|
+
children: Snippet;
|
|
17
|
+
}
|
|
18
|
+
declare const Drawer: import("svelte").Component<Props, {}, "open">;
|
|
19
|
+
type Drawer = ReturnType<typeof Drawer>;
|
|
20
|
+
export default Drawer;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import { anchored, type Placement } from '../../core/actions/anchor.js';
|
|
4
|
+
import { clickOutside, focusTrap, navigateList, portal } from '../../core/actions/dismissable.js';
|
|
5
|
+
import { describeTrigger, menuItems, popupTrigger } from '../../core/trigger.js';
|
|
6
|
+
import { useLocale } from '../../core/locale.svelte.js';
|
|
7
|
+
|
|
8
|
+
interface Props {
|
|
9
|
+
/** The control that opens the menu — put a `Button` in here. */
|
|
10
|
+
trigger: Snippet;
|
|
11
|
+
/** Menu contents: `MenuItem`, `MenuSeparator`, or anything else. */
|
|
12
|
+
children: Snippet;
|
|
13
|
+
open?: boolean;
|
|
14
|
+
placement?: Placement;
|
|
15
|
+
offset?: number;
|
|
16
|
+
/** Stretch the menu to the trigger's width. */
|
|
17
|
+
matchWidth?: boolean;
|
|
18
|
+
/** Close as soon as an item inside is activated. Default true. */
|
|
19
|
+
closeOnSelect?: boolean;
|
|
20
|
+
/** Accessible name for the menu. Defaults to the locale's `menu`. */
|
|
21
|
+
label?: string;
|
|
22
|
+
class?: string;
|
|
23
|
+
/** Classes for the wrapper around the trigger. */
|
|
24
|
+
triggerClass?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
let {
|
|
28
|
+
trigger,
|
|
29
|
+
children,
|
|
30
|
+
open = $bindable(false),
|
|
31
|
+
placement = 'bottom-start',
|
|
32
|
+
offset = 6,
|
|
33
|
+
matchWidth = false,
|
|
34
|
+
closeOnSelect = true,
|
|
35
|
+
label,
|
|
36
|
+
class: className = '',
|
|
37
|
+
triggerClass = ''
|
|
38
|
+
}: Props = $props();
|
|
39
|
+
|
|
40
|
+
const t = useLocale();
|
|
41
|
+
|
|
42
|
+
let anchorEl: HTMLElement | null = $state(null);
|
|
43
|
+
let menuEl: HTMLElement | null = $state(null);
|
|
44
|
+
|
|
45
|
+
// The ARIA state goes on the consumer's own control, not on our wrapper.
|
|
46
|
+
$effect(() => describeTrigger(anchorEl, 'menu', open));
|
|
47
|
+
|
|
48
|
+
function onMenuKeydown(event: KeyboardEvent) {
|
|
49
|
+
if (event.key === 'Escape' || event.key === 'Tab') {
|
|
50
|
+
if (event.key === 'Escape') event.preventDefault();
|
|
51
|
+
event.stopPropagation();
|
|
52
|
+
open = false;
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
navigateList(event, menuItems(menuEl));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function onMenuClick(event: MouseEvent) {
|
|
59
|
+
if (!closeOnSelect) return;
|
|
60
|
+
if ((event.target as HTMLElement).closest('[role="menuitem"]:not([disabled])')) open = false;
|
|
61
|
+
}
|
|
62
|
+
</script>
|
|
63
|
+
|
|
64
|
+
<span
|
|
65
|
+
bind:this={anchorEl}
|
|
66
|
+
use:popupTrigger={{
|
|
67
|
+
toggle: () => (open = !open),
|
|
68
|
+
open: () => (open = true),
|
|
69
|
+
close: () => (open = false),
|
|
70
|
+
arrowOpens: true,
|
|
71
|
+
isOpen: () => open
|
|
72
|
+
}}
|
|
73
|
+
class="inline-flex {triggerClass}"
|
|
74
|
+
>
|
|
75
|
+
{@render trigger()}
|
|
76
|
+
</span>
|
|
77
|
+
|
|
78
|
+
{#if open}
|
|
79
|
+
<div
|
|
80
|
+
bind:this={menuEl}
|
|
81
|
+
use:portal
|
|
82
|
+
use:anchored={{ anchor: anchorEl, placement, offset, matchWidth }}
|
|
83
|
+
use:clickOutside={{ onoutside: () => (open = false), ignore: [anchorEl] }}
|
|
84
|
+
use:focusTrap={{ restore: true }}
|
|
85
|
+
onkeydown={onMenuKeydown}
|
|
86
|
+
onclick={onMenuClick}
|
|
87
|
+
role="menu"
|
|
88
|
+
aria-label={label ?? t.current.menu}
|
|
89
|
+
tabindex="-1"
|
|
90
|
+
class="z-50 flex max-h-[min(24rem,80vh)] min-w-48 flex-col overflow-y-auto rounded-lg border border-hairline bg-bg py-1 font-sans shadow-lg {className}"
|
|
91
|
+
>
|
|
92
|
+
{@render children()}
|
|
93
|
+
</div>
|
|
94
|
+
{/if}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import { type Placement } from '../../core/actions/anchor.js';
|
|
3
|
+
interface Props {
|
|
4
|
+
/** The control that opens the menu — put a `Button` in here. */
|
|
5
|
+
trigger: Snippet;
|
|
6
|
+
/** Menu contents: `MenuItem`, `MenuSeparator`, or anything else. */
|
|
7
|
+
children: Snippet;
|
|
8
|
+
open?: boolean;
|
|
9
|
+
placement?: Placement;
|
|
10
|
+
offset?: number;
|
|
11
|
+
/** Stretch the menu to the trigger's width. */
|
|
12
|
+
matchWidth?: boolean;
|
|
13
|
+
/** Close as soon as an item inside is activated. Default true. */
|
|
14
|
+
closeOnSelect?: boolean;
|
|
15
|
+
/** Accessible name for the menu. Defaults to the locale's `menu`. */
|
|
16
|
+
label?: string;
|
|
17
|
+
class?: string;
|
|
18
|
+
/** Classes for the wrapper around the trigger. */
|
|
19
|
+
triggerClass?: string;
|
|
20
|
+
}
|
|
21
|
+
declare const Dropdown: import("svelte").Component<Props, {}, "open">;
|
|
22
|
+
type Dropdown = ReturnType<typeof Dropdown>;
|
|
23
|
+
export default Dropdown;
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import { toneBorder, toneSoft, toneSurface, toneText, type Tone } from '../../core/tones.js';
|
|
4
|
+
import { useLocale } from '../../core/locale.svelte.js';
|
|
5
|
+
import { formatSize as toReadableSize, sortFiles, type RejectedFile } from '../../core/files.js';
|
|
6
|
+
|
|
7
|
+
interface Props {
|
|
8
|
+
/** Bindable list of accepted files. */
|
|
9
|
+
files?: File[];
|
|
10
|
+
/** Same syntax as the input's `accept` — `'image/*,.pdf'`. */
|
|
11
|
+
accept?: string;
|
|
12
|
+
multiple?: boolean;
|
|
13
|
+
/** Per-file ceiling in bytes. `0` means no limit. */
|
|
14
|
+
maxSize?: number;
|
|
15
|
+
/** Cap on how many files may be held at once. `0` means no limit. */
|
|
16
|
+
maxFiles?: number;
|
|
17
|
+
disabled?: boolean;
|
|
18
|
+
tone?: Tone;
|
|
19
|
+
/** Headline inside the zone. Defaults to the locale's `dropFiles`. */
|
|
20
|
+
label?: string;
|
|
21
|
+
hint?: string;
|
|
22
|
+
/** Hide the built-in file list to render your own. */
|
|
23
|
+
showList?: boolean;
|
|
24
|
+
onaccept?: (files: File[]) => void;
|
|
25
|
+
onreject?: (rejected: RejectedFile[]) => void;
|
|
26
|
+
/** Replaces the glyph above the label. */
|
|
27
|
+
icon?: Snippet;
|
|
28
|
+
class?: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
let {
|
|
32
|
+
files = $bindable([]),
|
|
33
|
+
accept,
|
|
34
|
+
multiple = true,
|
|
35
|
+
maxSize = 0,
|
|
36
|
+
maxFiles = 0,
|
|
37
|
+
disabled = false,
|
|
38
|
+
tone = 'brand',
|
|
39
|
+
label,
|
|
40
|
+
hint,
|
|
41
|
+
showList = true,
|
|
42
|
+
onaccept,
|
|
43
|
+
onreject,
|
|
44
|
+
icon,
|
|
45
|
+
class: className = ''
|
|
46
|
+
}: Props = $props();
|
|
47
|
+
|
|
48
|
+
const t = useLocale();
|
|
49
|
+
|
|
50
|
+
let input: HTMLInputElement | null = $state(null);
|
|
51
|
+
let dragging = $state(false);
|
|
52
|
+
let rejected = $state<RejectedFile[]>([]);
|
|
53
|
+
/** Nested elements fire dragleave too, so count depth instead of toggling. */
|
|
54
|
+
let depth = 0;
|
|
55
|
+
|
|
56
|
+
const formatSize = (bytes: number) => toReadableSize(bytes, t.current.byteUnits);
|
|
57
|
+
|
|
58
|
+
const defaultHint = $derived.by(() => {
|
|
59
|
+
if (hint) return hint;
|
|
60
|
+
// Symbols rather than words, so the auto-hint needs no translation.
|
|
61
|
+
const parts: string[] = [];
|
|
62
|
+
if (accept) parts.push(accept.replace(/,\s*/g, ', '));
|
|
63
|
+
if (maxSize) parts.push(`≤ ${formatSize(maxSize)}`);
|
|
64
|
+
if (maxFiles) parts.push(`×${maxFiles}`);
|
|
65
|
+
return parts.join(' · ');
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
function add(incoming: FileList | File[] | null) {
|
|
69
|
+
if (!incoming || disabled) return;
|
|
70
|
+
|
|
71
|
+
const { accepted, rejected: bad } = sortFiles(incoming, {
|
|
72
|
+
accept,
|
|
73
|
+
maxSize,
|
|
74
|
+
maxFiles,
|
|
75
|
+
multiple,
|
|
76
|
+
held: files.length
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
rejected = bad;
|
|
80
|
+
if (bad.length) onreject?.(bad);
|
|
81
|
+
if (accepted.length === 0) return;
|
|
82
|
+
|
|
83
|
+
files = multiple ? [...files, ...accepted] : accepted.slice(0, 1);
|
|
84
|
+
onaccept?.(accepted);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function remove(index: number) {
|
|
88
|
+
files = files.filter((_, i) => i !== index);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function onDrop(event: DragEvent) {
|
|
92
|
+
event.preventDefault();
|
|
93
|
+
depth = 0;
|
|
94
|
+
dragging = false;
|
|
95
|
+
add(event.dataTransfer?.files ?? null);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const reasons = $derived({
|
|
99
|
+
type: t.current.rejectedType,
|
|
100
|
+
size: t.current.rejectedSize,
|
|
101
|
+
count: t.current.rejectedCount
|
|
102
|
+
});
|
|
103
|
+
</script>
|
|
104
|
+
|
|
105
|
+
<div class="flex w-full flex-col gap-3 {className}">
|
|
106
|
+
<button
|
|
107
|
+
type="button"
|
|
108
|
+
{disabled}
|
|
109
|
+
onclick={() => input?.click()}
|
|
110
|
+
ondragenter={(event) => {
|
|
111
|
+
event.preventDefault();
|
|
112
|
+
depth++;
|
|
113
|
+
dragging = true;
|
|
114
|
+
}}
|
|
115
|
+
ondragover={(event) => event.preventDefault()}
|
|
116
|
+
ondragleave={() => {
|
|
117
|
+
depth = Math.max(0, depth - 1);
|
|
118
|
+
if (depth === 0) dragging = false;
|
|
119
|
+
}}
|
|
120
|
+
ondrop={onDrop}
|
|
121
|
+
class="flex w-full flex-col items-center gap-3 rounded-lg border-2 border-dashed px-6 py-10 text-center font-sans transition-colors duration-150 ease-brand-out focus-visible:outline-2 focus-visible:outline-offset-2 disabled:pointer-events-none disabled:opacity-50
|
|
122
|
+
{dragging
|
|
123
|
+
? `${toneBorder[tone]} ${toneSurface[tone]}`
|
|
124
|
+
: 'border-hairline-strong bg-bg-alt/50 hover:bg-bg-alt'}
|
|
125
|
+
{toneText[tone]}"
|
|
126
|
+
>
|
|
127
|
+
<span class="flex size-12 items-center justify-center rounded-full {toneSoft[tone]}">
|
|
128
|
+
{#if icon}
|
|
129
|
+
{@render icon()}
|
|
130
|
+
{:else}
|
|
131
|
+
<svg class="size-5" viewBox="0 0 20 20" fill="none" aria-hidden="true">
|
|
132
|
+
<path
|
|
133
|
+
d="M10 14V3.5M6 7.5 10 3.5l4 4"
|
|
134
|
+
stroke="currentColor"
|
|
135
|
+
stroke-width="1.5"
|
|
136
|
+
stroke-linecap="round"
|
|
137
|
+
stroke-linejoin="round"
|
|
138
|
+
/>
|
|
139
|
+
<path
|
|
140
|
+
d="M3.5 13v3.5h13V13"
|
|
141
|
+
stroke="currentColor"
|
|
142
|
+
stroke-width="1.5"
|
|
143
|
+
stroke-linecap="round"
|
|
144
|
+
/>
|
|
145
|
+
</svg>
|
|
146
|
+
{/if}
|
|
147
|
+
</span>
|
|
148
|
+
<span class="flex flex-col gap-1">
|
|
149
|
+
<span class="text-sm font-semibold text-text">{label ?? t.current.dropFiles}</span>
|
|
150
|
+
<span class="text-[13px] text-text-secondary">
|
|
151
|
+
{t.current.or}
|
|
152
|
+
<span class="underline underline-offset-2">{t.current.browse}</span>
|
|
153
|
+
</span>
|
|
154
|
+
{#if defaultHint}
|
|
155
|
+
<span class="text-xs text-text-muted">{defaultHint}</span>
|
|
156
|
+
{/if}
|
|
157
|
+
</span>
|
|
158
|
+
</button>
|
|
159
|
+
|
|
160
|
+
<input
|
|
161
|
+
bind:this={input}
|
|
162
|
+
type="file"
|
|
163
|
+
{accept}
|
|
164
|
+
{multiple}
|
|
165
|
+
{disabled}
|
|
166
|
+
class="sr-only"
|
|
167
|
+
onchange={(event) => {
|
|
168
|
+
add(event.currentTarget.files);
|
|
169
|
+
event.currentTarget.value = '';
|
|
170
|
+
}}
|
|
171
|
+
/>
|
|
172
|
+
|
|
173
|
+
{#if rejected.length}
|
|
174
|
+
<ul class="flex flex-col gap-1">
|
|
175
|
+
{#each rejected as item (item.file.name + item.reason)}
|
|
176
|
+
<li class="font-sans text-[13px] text-danger">
|
|
177
|
+
{item.file.name} — {reasons[item.reason]}
|
|
178
|
+
</li>
|
|
179
|
+
{/each}
|
|
180
|
+
</ul>
|
|
181
|
+
{/if}
|
|
182
|
+
|
|
183
|
+
{#if showList && files.length}
|
|
184
|
+
<ul class="flex flex-col rounded-lg border border-hairline bg-bg shadow-xs">
|
|
185
|
+
{#each files as file, i (file.name + file.size + i)}
|
|
186
|
+
<li
|
|
187
|
+
class="flex items-center gap-3 border-b border-hairline px-4 py-2.5 font-sans last:border-b-0"
|
|
188
|
+
>
|
|
189
|
+
<span class="min-w-0 flex-1 truncate text-sm text-text">{file.name}</span>
|
|
190
|
+
<span class="shrink-0 text-xs text-text-muted tabular-nums">{formatSize(file.size)}</span>
|
|
191
|
+
<button
|
|
192
|
+
type="button"
|
|
193
|
+
onclick={() => remove(i)}
|
|
194
|
+
aria-label="{t.current.remove} {file.name}"
|
|
195
|
+
class="-m-1 shrink-0 rounded p-1 text-text-muted transition-colors duration-150 hover:bg-bg-inset hover:text-danger focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-danger"
|
|
196
|
+
>
|
|
197
|
+
<svg class="size-3.5" viewBox="0 0 14 14" fill="none" aria-hidden="true">
|
|
198
|
+
<path
|
|
199
|
+
d="m2 2 10 10M12 2 2 12"
|
|
200
|
+
stroke="currentColor"
|
|
201
|
+
stroke-width="1.6"
|
|
202
|
+
stroke-linecap="round"
|
|
203
|
+
/>
|
|
204
|
+
</svg>
|
|
205
|
+
</button>
|
|
206
|
+
</li>
|
|
207
|
+
{/each}
|
|
208
|
+
</ul>
|
|
209
|
+
{/if}
|
|
210
|
+
</div>
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import { type Tone } from '../../core/tones.js';
|
|
3
|
+
import { type RejectedFile } from '../../core/files.js';
|
|
4
|
+
interface Props {
|
|
5
|
+
/** Bindable list of accepted files. */
|
|
6
|
+
files?: File[];
|
|
7
|
+
/** Same syntax as the input's `accept` — `'image/*,.pdf'`. */
|
|
8
|
+
accept?: string;
|
|
9
|
+
multiple?: boolean;
|
|
10
|
+
/** Per-file ceiling in bytes. `0` means no limit. */
|
|
11
|
+
maxSize?: number;
|
|
12
|
+
/** Cap on how many files may be held at once. `0` means no limit. */
|
|
13
|
+
maxFiles?: number;
|
|
14
|
+
disabled?: boolean;
|
|
15
|
+
tone?: Tone;
|
|
16
|
+
/** Headline inside the zone. Defaults to the locale's `dropFiles`. */
|
|
17
|
+
label?: string;
|
|
18
|
+
hint?: string;
|
|
19
|
+
/** Hide the built-in file list to render your own. */
|
|
20
|
+
showList?: boolean;
|
|
21
|
+
onaccept?: (files: File[]) => void;
|
|
22
|
+
onreject?: (rejected: RejectedFile[]) => void;
|
|
23
|
+
/** Replaces the glyph above the label. */
|
|
24
|
+
icon?: Snippet;
|
|
25
|
+
class?: string;
|
|
26
|
+
}
|
|
27
|
+
declare const Dropzone: import("svelte").Component<Props, {}, "files">;
|
|
28
|
+
type Dropzone = ReturnType<typeof Dropzone>;
|
|
29
|
+
export default Dropzone;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
4
|
+
import { toneSoft, type Tone } from '../../core/tones.js';
|
|
5
|
+
|
|
6
|
+
interface Props extends HTMLAttributes<HTMLDivElement> {
|
|
7
|
+
title: string;
|
|
8
|
+
description?: string;
|
|
9
|
+
tone?: Tone;
|
|
10
|
+
/** Glyph inside the tinted circle. Defaults to a plain outline. */
|
|
11
|
+
icon?: Snippet;
|
|
12
|
+
/** Buttons or links under the copy. */
|
|
13
|
+
action?: Snippet;
|
|
14
|
+
/** Draws a dashed placeholder border around the block. */
|
|
15
|
+
bordered?: boolean;
|
|
16
|
+
children?: Snippet;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
let {
|
|
20
|
+
title,
|
|
21
|
+
description,
|
|
22
|
+
tone = 'neutral',
|
|
23
|
+
icon,
|
|
24
|
+
action,
|
|
25
|
+
bordered = true,
|
|
26
|
+
class: className = '',
|
|
27
|
+
children,
|
|
28
|
+
...rest
|
|
29
|
+
}: Props = $props();
|
|
30
|
+
</script>
|
|
31
|
+
|
|
32
|
+
<div
|
|
33
|
+
class="flex flex-col items-center gap-4 rounded-lg px-6 py-12 text-center font-sans
|
|
34
|
+
{bordered ? 'border border-dashed border-hairline-strong bg-bg-alt/50' : ''} {className}"
|
|
35
|
+
{...rest}
|
|
36
|
+
>
|
|
37
|
+
<span class="flex size-12 items-center justify-center rounded-full {toneSoft[tone]}">
|
|
38
|
+
{#if icon}
|
|
39
|
+
{@render icon()}
|
|
40
|
+
{:else}
|
|
41
|
+
<svg class="size-5" viewBox="0 0 20 20" fill="none" aria-hidden="true">
|
|
42
|
+
<rect
|
|
43
|
+
x="3"
|
|
44
|
+
y="3"
|
|
45
|
+
width="14"
|
|
46
|
+
height="14"
|
|
47
|
+
rx="2.5"
|
|
48
|
+
stroke="currentColor"
|
|
49
|
+
stroke-width="1.5"
|
|
50
|
+
/>
|
|
51
|
+
<path
|
|
52
|
+
d="M3 13.5 7 9.5l3 3 3-3 4 4"
|
|
53
|
+
stroke="currentColor"
|
|
54
|
+
stroke-width="1.5"
|
|
55
|
+
stroke-linecap="round"
|
|
56
|
+
stroke-linejoin="round"
|
|
57
|
+
/>
|
|
58
|
+
</svg>
|
|
59
|
+
{/if}
|
|
60
|
+
</span>
|
|
61
|
+
<div class="flex flex-col gap-1.5">
|
|
62
|
+
<p class="font-heading text-base font-semibold text-text">{title}</p>
|
|
63
|
+
{#if description}
|
|
64
|
+
<p class="max-w-sm text-sm leading-relaxed text-text-secondary">{description}</p>
|
|
65
|
+
{/if}
|
|
66
|
+
</div>
|
|
67
|
+
{#if children}{@render children()}{/if}
|
|
68
|
+
{#if action}
|
|
69
|
+
<div class="flex flex-wrap items-center justify-center gap-3 pt-1">{@render action()}</div>
|
|
70
|
+
{/if}
|
|
71
|
+
</div>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
|
+
import { type Tone } from '../../core/tones.js';
|
|
4
|
+
interface Props extends HTMLAttributes<HTMLDivElement> {
|
|
5
|
+
title: string;
|
|
6
|
+
description?: string;
|
|
7
|
+
tone?: Tone;
|
|
8
|
+
/** Glyph inside the tinted circle. Defaults to a plain outline. */
|
|
9
|
+
icon?: Snippet;
|
|
10
|
+
/** Buttons or links under the copy. */
|
|
11
|
+
action?: Snippet;
|
|
12
|
+
/** Draws a dashed placeholder border around the block. */
|
|
13
|
+
bordered?: boolean;
|
|
14
|
+
children?: Snippet;
|
|
15
|
+
}
|
|
16
|
+
declare const EmptyState: import("svelte").Component<Props, {}, "">;
|
|
17
|
+
type EmptyState = ReturnType<typeof EmptyState>;
|
|
18
|
+
export default EmptyState;
|