@nqmcreative/ui 0.1.0 → 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 +151 -439
- 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/{fonts.css → styles/matte/fonts.css} +4 -3
- package/dist/styles/matte/index.d.ts +109 -0
- package/dist/styles/matte/index.js +83 -0
- package/dist/{theme.css → styles/matte/theme.css} +0 -1
- 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
|
@@ -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;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
4
|
+
import { focusRing, toneHoverText, toneRing, toneSoft, type Tone } from '../../core/tones.js';
|
|
5
|
+
|
|
6
|
+
interface Props extends HTMLAttributes<HTMLDivElement> {
|
|
7
|
+
title: string;
|
|
8
|
+
description?: string;
|
|
9
|
+
tone?: Tone;
|
|
10
|
+
/** Turns the whole card into a link. */
|
|
11
|
+
href?: string;
|
|
12
|
+
/** Glyph inside the tinted square. */
|
|
13
|
+
icon?: Snippet;
|
|
14
|
+
children?: Snippet;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
let {
|
|
18
|
+
title,
|
|
19
|
+
description,
|
|
20
|
+
tone = 'brand',
|
|
21
|
+
href,
|
|
22
|
+
icon,
|
|
23
|
+
class: className = '',
|
|
24
|
+
children,
|
|
25
|
+
...rest
|
|
26
|
+
}: Props = $props();
|
|
27
|
+
</script>
|
|
28
|
+
|
|
29
|
+
<svelte:element
|
|
30
|
+
this={href ? 'a' : 'div'}
|
|
31
|
+
{href}
|
|
32
|
+
class="group flex flex-col gap-3 rounded-lg bg-bg p-5 {href
|
|
33
|
+
? `transition-shadow duration-150 ease-brand-out hover:shadow-md ${focusRing} ${toneRing[tone]}`
|
|
34
|
+
: ''} {className}"
|
|
35
|
+
{...rest}
|
|
36
|
+
>
|
|
37
|
+
{#if icon}
|
|
38
|
+
<span class="flex size-10 items-center justify-center rounded-lg {toneSoft[tone]}">
|
|
39
|
+
{@render icon()}
|
|
40
|
+
</span>
|
|
41
|
+
{/if}
|
|
42
|
+
|
|
43
|
+
<h3
|
|
44
|
+
class="font-heading text-base font-semibold text-text {href
|
|
45
|
+
? `transition-colors duration-150 ${toneHoverText[tone]}`
|
|
46
|
+
: ''}"
|
|
47
|
+
>
|
|
48
|
+
{title}
|
|
49
|
+
</h3>
|
|
50
|
+
|
|
51
|
+
{#if description}
|
|
52
|
+
<p class="font-sans text-sm leading-relaxed text-text-secondary">{description}</p>
|
|
53
|
+
{/if}
|
|
54
|
+
|
|
55
|
+
{#if children}
|
|
56
|
+
{@render children()}
|
|
57
|
+
{/if}
|
|
58
|
+
</svelte:element>
|
|
@@ -0,0 +1,16 @@
|
|
|
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
|
+
/** Turns the whole card into a link. */
|
|
9
|
+
href?: string;
|
|
10
|
+
/** Glyph inside the tinted square. */
|
|
11
|
+
icon?: Snippet;
|
|
12
|
+
children?: Snippet;
|
|
13
|
+
}
|
|
14
|
+
declare const FeatureCard: import("svelte").Component<Props, {}, "">;
|
|
15
|
+
type FeatureCard = ReturnType<typeof FeatureCard>;
|
|
16
|
+
export default FeatureCard;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
4
|
+
|
|
5
|
+
export type FeatureColumns = 2 | 3 | 4;
|
|
6
|
+
|
|
7
|
+
interface Props extends HTMLAttributes<HTMLElement> {
|
|
8
|
+
eyebrow?: string;
|
|
9
|
+
title?: string;
|
|
10
|
+
description?: string;
|
|
11
|
+
columns?: FeatureColumns;
|
|
12
|
+
/** Cards in a bordered grid rather than free-standing. */
|
|
13
|
+
bordered?: boolean;
|
|
14
|
+
/** `FeatureCard`s, or anything else. */
|
|
15
|
+
children: Snippet;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
let {
|
|
19
|
+
eyebrow,
|
|
20
|
+
title,
|
|
21
|
+
description,
|
|
22
|
+
columns = 3,
|
|
23
|
+
bordered = false,
|
|
24
|
+
class: className = '',
|
|
25
|
+
children,
|
|
26
|
+
...rest
|
|
27
|
+
}: Props = $props();
|
|
28
|
+
|
|
29
|
+
const cols: Record<FeatureColumns, string> = {
|
|
30
|
+
2: 'sm:grid-cols-2',
|
|
31
|
+
3: 'sm:grid-cols-2 lg:grid-cols-3',
|
|
32
|
+
4: 'sm:grid-cols-2 lg:grid-cols-4'
|
|
33
|
+
};
|
|
34
|
+
</script>
|
|
35
|
+
|
|
36
|
+
<section class="w-full px-6 py-20 {className}" {...rest}>
|
|
37
|
+
<div class="mx-auto flex w-full max-w-6xl flex-col gap-12">
|
|
38
|
+
{#if eyebrow || title || description}
|
|
39
|
+
<div class="flex max-w-xl flex-col gap-3">
|
|
40
|
+
{#if eyebrow}
|
|
41
|
+
<span class="font-sans text-[13px] font-semibold text-text-muted">{eyebrow}</span>
|
|
42
|
+
{/if}
|
|
43
|
+
{#if title}
|
|
44
|
+
<h2 class="font-heading text-3xl font-semibold tracking-tight text-balance text-text">
|
|
45
|
+
{title}
|
|
46
|
+
</h2>
|
|
47
|
+
{/if}
|
|
48
|
+
{#if description}
|
|
49
|
+
<p class="font-sans leading-relaxed text-pretty text-text-secondary">{description}</p>
|
|
50
|
+
{/if}
|
|
51
|
+
</div>
|
|
52
|
+
{/if}
|
|
53
|
+
|
|
54
|
+
<div
|
|
55
|
+
class="grid {cols[columns]} {bordered
|
|
56
|
+
? 'gap-px overflow-hidden rounded-lg border border-hairline bg-hairline'
|
|
57
|
+
: 'gap-6'}"
|
|
58
|
+
>
|
|
59
|
+
{@render children()}
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
</section>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
|
+
export type FeatureColumns = 2 | 3 | 4;
|
|
4
|
+
interface Props extends HTMLAttributes<HTMLElement> {
|
|
5
|
+
eyebrow?: string;
|
|
6
|
+
title?: string;
|
|
7
|
+
description?: string;
|
|
8
|
+
columns?: FeatureColumns;
|
|
9
|
+
/** Cards in a bordered grid rather than free-standing. */
|
|
10
|
+
bordered?: boolean;
|
|
11
|
+
/** `FeatureCard`s, or anything else. */
|
|
12
|
+
children: Snippet;
|
|
13
|
+
}
|
|
14
|
+
declare const FeatureGrid: import("svelte").Component<Props, {}, "">;
|
|
15
|
+
type FeatureGrid = ReturnType<typeof FeatureGrid>;
|
|
16
|
+
export default FeatureGrid;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
4
|
+
|
|
5
|
+
interface Props extends HTMLAttributes<HTMLDivElement> {
|
|
6
|
+
label?: string;
|
|
7
|
+
/** Helper text under the control. Hidden while `error` is set. */
|
|
8
|
+
hint?: string;
|
|
9
|
+
/** Replaces the hint and turns it red. */
|
|
10
|
+
error?: string;
|
|
11
|
+
required?: boolean;
|
|
12
|
+
/** `id` of the control — set the same value on the input inside. */
|
|
13
|
+
for?: string;
|
|
14
|
+
children: Snippet;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
let {
|
|
18
|
+
label,
|
|
19
|
+
hint,
|
|
20
|
+
error,
|
|
21
|
+
required = false,
|
|
22
|
+
for: htmlFor,
|
|
23
|
+
class: className = '',
|
|
24
|
+
children,
|
|
25
|
+
...rest
|
|
26
|
+
}: Props = $props();
|
|
27
|
+
</script>
|
|
28
|
+
|
|
29
|
+
<div class="flex w-full flex-col gap-1.5 {className}" {...rest}>
|
|
30
|
+
{#if label}
|
|
31
|
+
<label for={htmlFor} class="font-sans text-sm font-medium text-text">
|
|
32
|
+
{label}
|
|
33
|
+
{#if required}<span class="ml-0.5 text-danger" aria-hidden="true">*</span>{/if}
|
|
34
|
+
</label>
|
|
35
|
+
{/if}
|
|
36
|
+
{@render children()}
|
|
37
|
+
{#if error}
|
|
38
|
+
<p class="font-sans text-[13px] text-danger">{error}</p>
|
|
39
|
+
{:else if hint}
|
|
40
|
+
<p class="font-sans text-[13px] text-text-muted">{hint}</p>
|
|
41
|
+
{/if}
|
|
42
|
+
</div>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
|
+
interface Props extends HTMLAttributes<HTMLDivElement> {
|
|
4
|
+
label?: string;
|
|
5
|
+
/** Helper text under the control. Hidden while `error` is set. */
|
|
6
|
+
hint?: string;
|
|
7
|
+
/** Replaces the hint and turns it red. */
|
|
8
|
+
error?: string;
|
|
9
|
+
required?: boolean;
|
|
10
|
+
/** `id` of the control — set the same value on the input inside. */
|
|
11
|
+
for?: string;
|
|
12
|
+
children: Snippet;
|
|
13
|
+
}
|
|
14
|
+
declare const Field: import("svelte").Component<Props, {}, "">;
|
|
15
|
+
type Field = ReturnType<typeof Field>;
|
|
16
|
+
export default Field;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
export interface FooterLink {
|
|
3
|
+
label: string;
|
|
4
|
+
href?: string;
|
|
5
|
+
external?: boolean;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface FooterColumn {
|
|
9
|
+
title: string;
|
|
10
|
+
links: FooterLink[];
|
|
11
|
+
}
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
<script lang="ts">
|
|
15
|
+
import type { Snippet } from 'svelte';
|
|
16
|
+
import { toneHoverText, type Tone } from '../../core/tones.js';
|
|
17
|
+
|
|
18
|
+
interface Props {
|
|
19
|
+
columns?: FooterColumn[];
|
|
20
|
+
/** Logo, wordmark and a line of copy on the left. */
|
|
21
|
+
brand?: Snippet;
|
|
22
|
+
/** Right side of the bottom bar — social links, a locale picker. */
|
|
23
|
+
bottom?: Snippet;
|
|
24
|
+
/** Left side of the bottom bar. Ignored when `bottom` covers both. */
|
|
25
|
+
copyright?: string;
|
|
26
|
+
tone?: Tone;
|
|
27
|
+
class?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
let {
|
|
31
|
+
columns = [],
|
|
32
|
+
brand,
|
|
33
|
+
bottom,
|
|
34
|
+
copyright,
|
|
35
|
+
tone = 'brand',
|
|
36
|
+
class: className = ''
|
|
37
|
+
}: Props = $props();
|
|
38
|
+
</script>
|
|
39
|
+
|
|
40
|
+
<footer class="w-full border-t border-hairline bg-bg-alt {className}">
|
|
41
|
+
<div class="mx-auto flex w-full max-w-6xl flex-col gap-12 px-6 py-14">
|
|
42
|
+
{#if brand || columns.length}
|
|
43
|
+
<div class="flex flex-col gap-10 md:flex-row md:justify-between">
|
|
44
|
+
{#if brand}
|
|
45
|
+
<div class="flex max-w-xs flex-col gap-3">{@render brand()}</div>
|
|
46
|
+
{/if}
|
|
47
|
+
|
|
48
|
+
{#if columns.length}
|
|
49
|
+
<div class="grid grid-cols-2 gap-10 sm:grid-cols-3 md:gap-16">
|
|
50
|
+
{#each columns as column (column.title)}
|
|
51
|
+
<div class="flex flex-col gap-3">
|
|
52
|
+
<p class="font-sans text-[13px] font-semibold text-text">{column.title}</p>
|
|
53
|
+
<ul class="flex flex-col gap-2">
|
|
54
|
+
{#each column.links as item (item.label)}
|
|
55
|
+
<li>
|
|
56
|
+
<a
|
|
57
|
+
href={item.href}
|
|
58
|
+
target={item.external ? '_blank' : undefined}
|
|
59
|
+
rel={item.external ? 'noreferrer' : undefined}
|
|
60
|
+
class="font-sans text-[13px] text-text-muted transition-colors duration-150 ease-brand-out {toneHoverText[
|
|
61
|
+
tone
|
|
62
|
+
]}"
|
|
63
|
+
>
|
|
64
|
+
{item.label}
|
|
65
|
+
{#if item.external}<span aria-hidden="true" class="text-[0.85em]">↗</span
|
|
66
|
+
>{/if}
|
|
67
|
+
</a>
|
|
68
|
+
</li>
|
|
69
|
+
{/each}
|
|
70
|
+
</ul>
|
|
71
|
+
</div>
|
|
72
|
+
{/each}
|
|
73
|
+
</div>
|
|
74
|
+
{/if}
|
|
75
|
+
</div>
|
|
76
|
+
{/if}
|
|
77
|
+
|
|
78
|
+
{#if copyright || bottom}
|
|
79
|
+
<div
|
|
80
|
+
class="flex flex-col gap-4 border-t border-hairline pt-6 sm:flex-row sm:items-center sm:justify-between"
|
|
81
|
+
>
|
|
82
|
+
{#if copyright}
|
|
83
|
+
<p class="font-sans text-[13px] text-text-muted">{copyright}</p>
|
|
84
|
+
{/if}
|
|
85
|
+
{#if bottom}
|
|
86
|
+
<div class="flex items-center gap-4">{@render bottom()}</div>
|
|
87
|
+
{/if}
|
|
88
|
+
</div>
|
|
89
|
+
{/if}
|
|
90
|
+
</div>
|
|
91
|
+
</footer>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export interface FooterLink {
|
|
2
|
+
label: string;
|
|
3
|
+
href?: string;
|
|
4
|
+
external?: boolean;
|
|
5
|
+
}
|
|
6
|
+
export interface FooterColumn {
|
|
7
|
+
title: string;
|
|
8
|
+
links: FooterLink[];
|
|
9
|
+
}
|
|
10
|
+
import type { Snippet } from 'svelte';
|
|
11
|
+
import { type Tone } from '../../core/tones.js';
|
|
12
|
+
interface Props {
|
|
13
|
+
columns?: FooterColumn[];
|
|
14
|
+
/** Logo, wordmark and a line of copy on the left. */
|
|
15
|
+
brand?: Snippet;
|
|
16
|
+
/** Right side of the bottom bar — social links, a locale picker. */
|
|
17
|
+
bottom?: Snippet;
|
|
18
|
+
/** Left side of the bottom bar. Ignored when `bottom` covers both. */
|
|
19
|
+
copyright?: string;
|
|
20
|
+
tone?: Tone;
|
|
21
|
+
class?: string;
|
|
22
|
+
}
|
|
23
|
+
declare const Footer: import("svelte").Component<Props, {}, "">;
|
|
24
|
+
type Footer = ReturnType<typeof Footer>;
|
|
25
|
+
export default Footer;
|