@thomasfosterau/components 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +817 -2
- package/dist/canvas/VectorCanvas.svelte +239 -0
- package/dist/canvas/VectorCanvas.svelte.d.ts +87 -0
- package/dist/canvas/history.d.ts +105 -0
- package/dist/canvas/history.js +147 -0
- package/dist/canvas/index.d.ts +15 -0
- package/dist/canvas/index.js +14 -0
- package/dist/canvas/types.d.ts +91 -0
- package/dist/canvas/types.js +13 -0
- package/dist/canvas/utils.d.ts +66 -0
- package/dist/canvas/utils.js +114 -0
- package/dist/charts/Chart.svelte +300 -0
- package/dist/charts/Chart.svelte.d.ts +50 -0
- package/dist/charts/index.d.ts +11 -0
- package/dist/charts/index.js +10 -0
- package/dist/charts/types.d.ts +28 -0
- package/dist/charts/types.js +6 -0
- package/dist/citations/BookCitation.svelte +53 -0
- package/dist/citations/BookCitation.svelte.d.ts +41 -0
- package/dist/citations/ChapterCitation.svelte +38 -0
- package/dist/citations/ChapterCitation.svelte.d.ts +50 -0
- package/dist/citations/Citation.svelte +47 -0
- package/dist/citations/Citation.svelte.d.ts +24 -0
- package/dist/citations/CreditList.svelte +69 -0
- package/dist/citations/CreditList.svelte.d.ts +47 -0
- package/dist/citations/DOILink.svelte +28 -0
- package/dist/citations/DOILink.svelte.d.ts +19 -0
- package/dist/citations/Footnote.svelte +39 -0
- package/dist/citations/Footnote.svelte.d.ts +17 -0
- package/dist/citations/FootnoteList.svelte +36 -0
- package/dist/citations/FootnoteList.svelte.d.ts +21 -0
- package/dist/citations/ISBNLink.svelte +29 -0
- package/dist/citations/ISBNLink.svelte.d.ts +19 -0
- package/dist/citations/ISSNLink.svelte +29 -0
- package/dist/citations/ISSNLink.svelte.d.ts +19 -0
- package/dist/citations/Publisher.svelte +34 -0
- package/dist/citations/Publisher.svelte.d.ts +14 -0
- package/dist/citations/ReferenceList.svelte +53 -0
- package/dist/citations/ReferenceList.svelte.d.ts +21 -0
- package/dist/citations/ReportCitation.svelte +41 -0
- package/dist/citations/ReportCitation.svelte.d.ts +36 -0
- package/dist/citations/ScholarlyArticleCitation.svelte +64 -0
- package/dist/citations/ScholarlyArticleCitation.svelte.d.ts +41 -0
- package/dist/citations/ThesisCitation.svelte +35 -0
- package/dist/citations/ThesisCitation.svelte.d.ts +29 -0
- package/dist/citations/bibliography.svelte.d.ts +55 -0
- package/dist/citations/bibliography.svelte.js +78 -0
- package/dist/citations/citation.d.ts +71 -0
- package/dist/citations/citation.js +121 -0
- package/dist/citations/csl.d.ts +102 -0
- package/dist/citations/csl.js +20 -0
- package/dist/citations/index.d.ts +31 -0
- package/dist/citations/index.js +36 -0
- package/dist/citations/list-format.d.ts +34 -0
- package/dist/citations/list-format.js +38 -0
- package/dist/context.svelte.d.ts +46 -0
- package/dist/context.svelte.js +51 -0
- package/dist/data/DataGrid.svelte +150 -0
- package/dist/data/DataGrid.svelte.d.ts +118 -0
- package/dist/data/DescriptionList.svelte +47 -0
- package/dist/data/DescriptionList.svelte.d.ts +26 -0
- package/dist/data/DescriptionRow.svelte +55 -0
- package/dist/data/DescriptionRow.svelte.d.ts +21 -0
- package/dist/data/DiffText.svelte +29 -0
- package/dist/data/DiffText.svelte.d.ts +19 -0
- package/dist/data/ItemList.svelte +80 -0
- package/dist/data/ItemList.svelte.d.ts +52 -0
- package/dist/data/LinkListItem.svelte +46 -0
- package/dist/data/LinkListItem.svelte.d.ts +21 -0
- package/dist/data/Pagination.svelte +136 -0
- package/dist/data/Pagination.svelte.d.ts +71 -0
- package/dist/data/PaginationFooter.svelte +41 -0
- package/dist/data/PaginationFooter.svelte.d.ts +59 -0
- package/dist/data/ResultCount.svelte +27 -0
- package/dist/data/ResultCount.svelte.d.ts +18 -0
- package/dist/data/Table.svelte +145 -0
- package/dist/data/Table.svelte.d.ts +75 -0
- package/dist/data/TreeList.svelte +41 -0
- package/dist/data/TreeList.svelte.d.ts +17 -0
- package/dist/data/TreeListItem.svelte +29 -0
- package/dist/data/TreeListItem.svelte.d.ts +10 -0
- package/dist/data/description-context.d.ts +33 -0
- package/dist/data/description-context.js +35 -0
- package/dist/data/diff.d.ts +14 -0
- package/dist/data/diff.js +10 -0
- package/dist/data/index.d.ts +35 -0
- package/dist/data/index.js +35 -0
- package/dist/data/list-item.d.ts +30 -0
- package/dist/data/list-item.js +19 -0
- package/dist/data/pagination.d.ts +81 -0
- package/dist/data/pagination.js +79 -0
- package/dist/data/table-types.d.ts +64 -0
- package/dist/data/table-types.js +35 -0
- package/dist/feedback/ConfirmDialog.svelte +79 -0
- package/dist/feedback/ConfirmDialog.svelte.d.ts +82 -0
- package/dist/feedback/Dialog.svelte +77 -0
- package/dist/feedback/Dialog.svelte.d.ts +37 -0
- package/dist/feedback/LoadingBoundary.svelte +61 -0
- package/dist/feedback/LoadingBoundary.svelte.d.ts +34 -0
- package/dist/feedback/ProgressBar.svelte +46 -0
- package/dist/feedback/ProgressBar.svelte.d.ts +28 -0
- package/dist/feedback/SegmentBar.svelte +83 -0
- package/dist/feedback/SegmentBar.svelte.d.ts +36 -0
- package/dist/feedback/StatTile.svelte +77 -0
- package/dist/feedback/StatTile.svelte.d.ts +49 -0
- package/dist/feedback/Stats.svelte +18 -0
- package/dist/feedback/Stats.svelte.d.ts +16 -0
- package/dist/feedback/Timeline.svelte +86 -0
- package/dist/feedback/Timeline.svelte.d.ts +52 -0
- package/dist/feedback/Toaster.svelte +78 -0
- package/dist/feedback/Toaster.svelte.d.ts +32 -0
- package/dist/feedback/index.d.ts +31 -0
- package/dist/feedback/index.js +27 -0
- package/dist/feedback/sizes.d.ts +27 -0
- package/dist/feedback/sizes.js +41 -0
- package/dist/feedback/toaster.d.ts +48 -0
- package/dist/feedback/toaster.js +29 -0
- package/dist/fields/Checkbox.svelte +33 -0
- package/dist/fields/Checkbox.svelte.d.ts +24 -0
- package/dist/fields/CheckboxGroup.svelte +41 -0
- package/dist/fields/CheckboxGroup.svelte.d.ts +40 -0
- package/dist/fields/Combobox.svelte +222 -0
- package/dist/fields/Combobox.svelte.d.ts +98 -0
- package/dist/fields/DateInput.svelte +31 -0
- package/dist/fields/DateInput.svelte.d.ts +29 -0
- package/dist/fields/FieldErrors.svelte +30 -0
- package/dist/fields/FieldErrors.svelte.d.ts +11 -0
- package/dist/fields/Fieldset.svelte +48 -0
- package/dist/fields/Fieldset.svelte.d.ts +26 -0
- package/dist/fields/FileInput.svelte +67 -0
- package/dist/fields/FileInput.svelte.d.ts +33 -0
- package/dist/fields/FormField.svelte +54 -0
- package/dist/fields/FormField.svelte.d.ts +29 -0
- package/dist/fields/ImageInput.svelte +87 -0
- package/dist/fields/ImageInput.svelte.d.ts +39 -0
- package/dist/fields/PushToggle.svelte +93 -0
- package/dist/fields/PushToggle.svelte.d.ts +55 -0
- package/dist/fields/RadioGroup.svelte +85 -0
- package/dist/fields/RadioGroup.svelte.d.ts +41 -0
- package/dist/fields/SearchInput.svelte +57 -0
- package/dist/fields/SearchInput.svelte.d.ts +33 -0
- package/dist/fields/Select.svelte +36 -0
- package/dist/fields/Select.svelte.d.ts +29 -0
- package/dist/fields/TextInput.svelte +26 -0
- package/dist/fields/TextInput.svelte.d.ts +30 -0
- package/dist/fields/Textarea.svelte +22 -0
- package/dist/fields/Textarea.svelte.d.ts +25 -0
- package/dist/fields/Toggle.svelte +72 -0
- package/dist/fields/Toggle.svelte.d.ts +32 -0
- package/dist/fields/VideoInput.svelte +35 -0
- package/dist/fields/VideoInput.svelte.d.ts +40 -0
- package/dist/fields/field-types.d.ts +51 -0
- package/dist/fields/field-types.js +60 -0
- package/dist/fields/index.d.ts +34 -0
- package/dist/fields/index.js +34 -0
- package/dist/formatting/DateTime.svelte +35 -0
- package/dist/formatting/DateTime.svelte.d.ts +26 -0
- package/dist/formatting/FormatList.svelte +31 -0
- package/dist/formatting/FormatList.svelte.d.ts +46 -0
- package/dist/formatting/Highlight.svelte +47 -0
- package/dist/formatting/Highlight.svelte.d.ts +15 -0
- package/dist/formatting/Number.svelte +35 -0
- package/dist/formatting/Number.svelte.d.ts +43 -0
- package/dist/formatting/RelativeTime.svelte +69 -0
- package/dist/formatting/RelativeTime.svelte.d.ts +26 -0
- package/dist/formatting/Year.svelte +39 -0
- package/dist/formatting/Year.svelte.d.ts +21 -0
- package/dist/formatting/index.d.ts +12 -0
- package/dist/formatting/index.js +14 -0
- package/dist/forms/EntityForm.svelte +177 -0
- package/dist/forms/EntityForm.svelte.d.ts +57 -0
- package/dist/forms/FormActions.svelte +29 -0
- package/dist/forms/FormActions.svelte.d.ts +37 -0
- package/dist/forms/FormErrors.svelte +21 -0
- package/dist/forms/FormErrors.svelte.d.ts +16 -0
- package/dist/forms/HiddenField.svelte +22 -0
- package/dist/forms/HiddenField.svelte.d.ts +11 -0
- package/dist/forms/SearchForm.svelte +43 -0
- package/dist/forms/SearchForm.svelte.d.ts +36 -0
- package/dist/forms/entity-form.d.ts +12 -0
- package/dist/forms/entity-form.js +12 -0
- package/dist/forms/index.d.ts +14 -0
- package/dist/forms/index.js +14 -0
- package/dist/hotkeys/HotkeyBadge.svelte +39 -0
- package/dist/hotkeys/HotkeyBadge.svelte.d.ts +25 -0
- package/dist/hotkeys/HotkeyHelpDialog.svelte +68 -0
- package/dist/hotkeys/HotkeyHelpDialog.svelte.d.ts +35 -0
- package/dist/hotkeys/HotkeyProvider.svelte +92 -0
- package/dist/hotkeys/HotkeyProvider.svelte.d.ts +4 -0
- package/dist/hotkeys/context.svelte.d.ts +47 -0
- package/dist/hotkeys/context.svelte.js +72 -0
- package/dist/hotkeys/index.d.ts +23 -0
- package/dist/hotkeys/index.js +22 -0
- package/dist/hotkeys/match.d.ts +65 -0
- package/dist/hotkeys/match.js +133 -0
- package/dist/hotkeys/registry.svelte.d.ts +55 -0
- package/dist/hotkeys/registry.svelte.js +110 -0
- package/dist/hotkeys/types.d.ts +72 -0
- package/dist/hotkeys/types.js +18 -0
- package/dist/index.d.ts +36 -0
- package/dist/index.js +36 -0
- package/dist/infobox/Infobox.svelte +79 -0
- package/dist/infobox/Infobox.svelte.d.ts +26 -0
- package/dist/infobox/InfoboxHeader.svelte +27 -0
- package/dist/infobox/InfoboxHeader.svelte.d.ts +11 -0
- package/dist/infobox/InfoboxImage.svelte +38 -0
- package/dist/infobox/InfoboxImage.svelte.d.ts +21 -0
- package/dist/infobox/InfoboxRow.svelte +50 -0
- package/dist/infobox/InfoboxRow.svelte.d.ts +25 -0
- package/dist/infobox/InfoboxSection.svelte +24 -0
- package/dist/infobox/InfoboxSection.svelte.d.ts +12 -0
- package/dist/infobox/index.d.ts +21 -0
- package/dist/infobox/index.js +20 -0
- package/dist/infobox/types.d.ts +86 -0
- package/dist/infobox/types.js +27 -0
- package/dist/layout/Breadcrumbs.svelte +52 -0
- package/dist/layout/Breadcrumbs.svelte.d.ts +18 -0
- package/dist/layout/DetailLayout.svelte +50 -0
- package/dist/layout/DetailLayout.svelte.d.ts +19 -0
- package/dist/layout/FilterBar.svelte +40 -0
- package/dist/layout/FilterBar.svelte.d.ts +12 -0
- package/dist/layout/Footer.svelte +32 -0
- package/dist/layout/Footer.svelte.d.ts +12 -0
- package/dist/layout/Main.svelte +30 -0
- package/dist/layout/Main.svelte.d.ts +15 -0
- package/dist/layout/Navbar.svelte +68 -0
- package/dist/layout/Navbar.svelte.d.ts +31 -0
- package/dist/layout/PageHeader.svelte +77 -0
- package/dist/layout/PageHeader.svelte.d.ts +33 -0
- package/dist/layout/Section.svelte +72 -0
- package/dist/layout/Section.svelte.d.ts +40 -0
- package/dist/layout/Sidebar.svelte +54 -0
- package/dist/layout/Sidebar.svelte.d.ts +27 -0
- package/dist/layout/Tabs.svelte +85 -0
- package/dist/layout/Tabs.svelte.d.ts +42 -0
- package/dist/layout/index.d.ts +29 -0
- package/dist/layout/index.js +29 -0
- package/dist/layout/types.d.ts +52 -0
- package/dist/layout/types.js +27 -0
- package/dist/links/ContextLinks.svelte +28 -0
- package/dist/links/ContextLinks.svelte.d.ts +20 -0
- package/dist/links/EmailLink.svelte +26 -0
- package/dist/links/EmailLink.svelte.d.ts +20 -0
- package/dist/links/index.d.ts +7 -0
- package/dist/links/index.js +7 -0
- package/dist/media/Figure.svelte +26 -0
- package/dist/media/Figure.svelte.d.ts +12 -0
- package/dist/media/Image.svelte +76 -0
- package/dist/media/Image.svelte.d.ts +41 -0
- package/dist/media/Video.svelte +65 -0
- package/dist/media/Video.svelte.d.ts +29 -0
- package/dist/media/image-dimensions.d.ts +28 -0
- package/dist/media/image-dimensions.js +34 -0
- package/dist/media/index.d.ts +10 -0
- package/dist/media/index.js +10 -0
- package/dist/search/CommandPalette.svelte +257 -0
- package/dist/search/CommandPalette.svelte.d.ts +59 -0
- package/dist/search/index.d.ts +13 -0
- package/dist/search/index.js +13 -0
- package/dist/search/types.d.ts +37 -0
- package/dist/search/types.js +20 -0
- package/dist/seo/Head.svelte +190 -0
- package/dist/seo/Head.svelte.d.ts +86 -0
- package/dist/seo/JsonLd.svelte +30 -0
- package/dist/seo/JsonLd.svelte.d.ts +20 -0
- package/dist/seo/index.d.ts +18 -0
- package/dist/seo/index.js +17 -0
- package/dist/seo/json-ld.d.ts +33 -0
- package/dist/seo/json-ld.js +29 -0
- package/dist/seo/og-image.d.ts +71 -0
- package/dist/seo/og-image.js +81 -0
- package/dist/seo/types.d.ts +43 -0
- package/dist/seo/types.js +6 -0
- package/dist/theme.css +161 -0
- package/dist/tips/PopoverTip.svelte +80 -0
- package/dist/tips/PopoverTip.svelte.d.ts +33 -0
- package/dist/tips/Tip.svelte +91 -0
- package/dist/tips/Tip.svelte.d.ts +25 -0
- package/dist/tips/index.d.ts +15 -0
- package/dist/tips/index.js +14 -0
- package/dist/tips/tip-stack.d.ts +25 -0
- package/dist/tips/tip-stack.js +37 -0
- package/dist/tips/tip-store.svelte.d.ts +74 -0
- package/dist/tips/tip-store.svelte.js +158 -0
- package/dist/tips/types.d.ts +46 -0
- package/dist/tips/types.js +6 -0
- package/dist/ui/Alert.svelte +61 -0
- package/dist/ui/Alert.svelte.d.ts +37 -0
- package/dist/ui/Badge.svelte +39 -0
- package/dist/ui/Badge.svelte.d.ts +32 -0
- package/dist/ui/Button.svelte +43 -0
- package/dist/ui/Button.svelte.d.ts +29 -0
- package/dist/ui/Card.svelte +49 -0
- package/dist/ui/Card.svelte.d.ts +44 -0
- package/dist/ui/Chip.svelte +58 -0
- package/dist/ui/Chip.svelte.d.ts +21 -0
- package/dist/ui/ConfirmButton.svelte +75 -0
- package/dist/ui/ConfirmButton.svelte.d.ts +48 -0
- package/dist/ui/Disclosure.svelte +39 -0
- package/dist/ui/Disclosure.svelte.d.ts +22 -0
- package/dist/ui/EmptyState.svelte +37 -0
- package/dist/ui/EmptyState.svelte.d.ts +22 -0
- package/dist/ui/ExternalLink.svelte +38 -0
- package/dist/ui/ExternalLink.svelte.d.ts +34 -0
- package/dist/ui/Heading.svelte +58 -0
- package/dist/ui/Heading.svelte.d.ts +22 -0
- package/dist/ui/Link.svelte +34 -0
- package/dist/ui/Link.svelte.d.ts +20 -0
- package/dist/ui/LinkButton.svelte +51 -0
- package/dist/ui/LinkButton.svelte.d.ts +33 -0
- package/dist/ui/Menu.svelte +54 -0
- package/dist/ui/Menu.svelte.d.ts +34 -0
- package/dist/ui/MenuItem.svelte +75 -0
- package/dist/ui/MenuItem.svelte.d.ts +21 -0
- package/dist/ui/MenuSeparator.svelte +26 -0
- package/dist/ui/MenuSeparator.svelte.d.ts +8 -0
- package/dist/ui/Popover.svelte +63 -0
- package/dist/ui/Popover.svelte.d.ts +40 -0
- package/dist/ui/ShareButton.svelte +64 -0
- package/dist/ui/ShareButton.svelte.d.ts +39 -0
- package/dist/ui/Skeleton.svelte +43 -0
- package/dist/ui/Skeleton.svelte.d.ts +22 -0
- package/dist/ui/ThemeToggle.svelte +65 -0
- package/dist/ui/ThemeToggle.svelte.d.ts +24 -0
- package/dist/ui/Tooltip.svelte +54 -0
- package/dist/ui/Tooltip.svelte.d.ts +35 -0
- package/dist/ui/VisuallyHidden.svelte +23 -0
- package/dist/ui/VisuallyHidden.svelte.d.ts +10 -0
- package/dist/ui/colour-token.d.ts +41 -0
- package/dist/ui/colour-token.js +49 -0
- package/dist/ui/index.d.ts +44 -0
- package/dist/ui/index.js +44 -0
- package/dist/ui/menu-context.d.ts +22 -0
- package/dist/ui/menu-context.js +26 -0
- package/dist/ui/slug.d.ts +19 -0
- package/dist/ui/slug.js +25 -0
- package/dist/ui/tones.d.ts +68 -0
- package/dist/ui/tones.js +150 -0
- package/package.json +101 -3
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
<!-- @component
|
|
2
|
+
A colour swatch beside a short code, for legends, result rows and filter lists.
|
|
3
|
+
-->
|
|
4
|
+
<!--
|
|
5
|
+
Chip — a colour swatch beside a short code: the unit that appears in a
|
|
6
|
+
legend, a result row, a filter list. Denser than a `Badge` and always
|
|
7
|
+
paired with a colour, where a badge is usually paired with a tone.
|
|
8
|
+
|
|
9
|
+
ARIA contract: when `title` is given the code is wrapped in `<abbr title>`,
|
|
10
|
+
so the expansion is available to hover AND to assistive technology — which
|
|
11
|
+
is the whole reason a three-letter code is acceptable here at all. The
|
|
12
|
+
swatch is `aria-hidden`: it repeats the colour, which conveys nothing on
|
|
13
|
+
its own. With `href` the chip is a real link and the code is its accessible
|
|
14
|
+
name.
|
|
15
|
+
|
|
16
|
+
The colour arrives as a token name, never a value — see `colour-token.ts`.
|
|
17
|
+
-->
|
|
18
|
+
<script lang="ts">import { getComponentsContext } from "../context.svelte.js";
|
|
19
|
+
import { CHIP_COLOUR_PROPERTY, colourTokenStyle } from "./colour-token.js";
|
|
20
|
+
import { chipSize, chipSwatchSize } from "./tones.js";
|
|
21
|
+
let { label, title, href, colourToken, size = "md", class: className, ...rest } = $props();
|
|
22
|
+
const context = getComponentsContext();
|
|
23
|
+
const style = $derived(colourTokenStyle(CHIP_COLOUR_PROPERTY, colourToken));
|
|
24
|
+
</script>
|
|
25
|
+
|
|
26
|
+
{#snippet body()}
|
|
27
|
+
<span
|
|
28
|
+
aria-hidden="true"
|
|
29
|
+
class={[
|
|
30
|
+
'ring-base-content/20 inline-block shrink-0 rounded-full bg-(--chip-colour) ring-1',
|
|
31
|
+
chipSwatchSize[size],
|
|
32
|
+
]}
|
|
33
|
+
></span>
|
|
34
|
+
{#if title !== undefined}
|
|
35
|
+
<abbr {title} class="no-underline">{label}</abbr>
|
|
36
|
+
{:else}
|
|
37
|
+
<span>{label}</span>
|
|
38
|
+
{/if}
|
|
39
|
+
{/snippet}
|
|
40
|
+
|
|
41
|
+
{#if href !== undefined}
|
|
42
|
+
<a
|
|
43
|
+
{...rest}
|
|
44
|
+
href={context.href(href)}
|
|
45
|
+
{style}
|
|
46
|
+
class={['link link-hover inline-flex items-center font-medium', chipSize[size], className]}
|
|
47
|
+
>
|
|
48
|
+
{@render body()}
|
|
49
|
+
</a>
|
|
50
|
+
{:else}
|
|
51
|
+
<span
|
|
52
|
+
{...rest}
|
|
53
|
+
{style}
|
|
54
|
+
class={['inline-flex items-center font-medium', chipSize[size], className]}
|
|
55
|
+
>
|
|
56
|
+
{@render body()}
|
|
57
|
+
</span>
|
|
58
|
+
{/if}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
2
|
+
import { type ChipSize } from './tones.ts';
|
|
3
|
+
interface Props extends Omit<HTMLAttributes<HTMLElement>, 'title'> {
|
|
4
|
+
/** The short code shown in the chip. */
|
|
5
|
+
label: string;
|
|
6
|
+
/** The expansion, rendered as the `<abbr>` title. */
|
|
7
|
+
title?: string | undefined;
|
|
8
|
+
/** When set, the chip is a link to the subject's page. */
|
|
9
|
+
href?: string | undefined;
|
|
10
|
+
/** A CSS custom-property name the app's theme declares (`--party-alp`). */
|
|
11
|
+
colourToken?: string | undefined;
|
|
12
|
+
/**
|
|
13
|
+
* daisyUI size step.
|
|
14
|
+
* @defaultValue `'md'`
|
|
15
|
+
*/
|
|
16
|
+
size?: ChipSize | undefined;
|
|
17
|
+
}
|
|
18
|
+
/** A colour swatch beside a short code, for legends, result rows and filter lists. */
|
|
19
|
+
declare const Chip: import("svelte").Component<Props, {}, "">;
|
|
20
|
+
type Chip = ReturnType<typeof Chip>;
|
|
21
|
+
export default Chip;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
<!-- @component
|
|
2
|
+
A destructive action behind a two-step inline confirmation.
|
|
3
|
+
-->
|
|
4
|
+
<!--
|
|
5
|
+
ConfirmButton — a destructive action behind a two-step inline confirm.
|
|
6
|
+
|
|
7
|
+
Inline rather than a dialog, deliberately: the confirmation replaces the
|
|
8
|
+
button in place, so nothing is torn out of the reading order, focus is not
|
|
9
|
+
trapped, and the page behind it does not need an overlay. Use
|
|
10
|
+
`ConfirmDialog` instead when the confirmation itself needs to carry content
|
|
11
|
+
(a form, a list of what will be destroyed).
|
|
12
|
+
|
|
13
|
+
ARIA contract: the failure surface is an `Alert` with `live="assertive"` —
|
|
14
|
+
the reader pressed a button and nothing visible happened, so the reason has
|
|
15
|
+
to interrupt. Both buttons are `disabled` while the action is in flight, and
|
|
16
|
+
the confirm button swaps its text to `pendingLabel` rather than emptying
|
|
17
|
+
itself, so its accessible name never disappears mid-press.
|
|
18
|
+
|
|
19
|
+
`action` is a plain promise-returning prop — no Effect in this library. A
|
|
20
|
+
rejection with an `Error` shows that error's message (the caller's own
|
|
21
|
+
wording, usually from the server); anything else shows `failureMessage`.
|
|
22
|
+
On success, `redirectTo` navigates through the components context, so an
|
|
23
|
+
app gets client-side routing and this component stays ignorant of it.
|
|
24
|
+
-->
|
|
25
|
+
<script lang="ts">import { getComponentsContext } from "../context.svelte.js";
|
|
26
|
+
import Alert from "./Alert.svelte";
|
|
27
|
+
import Button from "./Button.svelte";
|
|
28
|
+
let { action, idleLabel = "Delete", confirmLabel = "Confirm", cancelLabel = "Cancel", pendingLabel = "Working…", confirmMessage = "Are you sure? This cannot be undone.", failureMessage = "That didn’t work. Try again.", tone = "error", redirectTo, class: className } = $props();
|
|
29
|
+
const context = getComponentsContext();
|
|
30
|
+
let confirming = $state(false);
|
|
31
|
+
let pending = $state(false);
|
|
32
|
+
let errorMessage = $state(undefined);
|
|
33
|
+
const beginConfirmation = () => {
|
|
34
|
+
errorMessage = undefined;
|
|
35
|
+
confirming = true;
|
|
36
|
+
};
|
|
37
|
+
const cancelConfirmation = () => {
|
|
38
|
+
if (pending) return;
|
|
39
|
+
errorMessage = undefined;
|
|
40
|
+
confirming = false;
|
|
41
|
+
};
|
|
42
|
+
const confirmAction = async () => {
|
|
43
|
+
pending = true;
|
|
44
|
+
errorMessage = undefined;
|
|
45
|
+
try {
|
|
46
|
+
await action();
|
|
47
|
+
confirming = false;
|
|
48
|
+
if (redirectTo !== undefined) await context.navigate(context.href(redirectTo));
|
|
49
|
+
} catch (error) {
|
|
50
|
+
errorMessage = error instanceof Error ? error.message : failureMessage;
|
|
51
|
+
} finally {
|
|
52
|
+
pending = false;
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
</script>
|
|
56
|
+
|
|
57
|
+
<div class={['space-y-2', className]}>
|
|
58
|
+
{#if confirming}
|
|
59
|
+
<div class="flex flex-wrap items-center gap-2">
|
|
60
|
+
<p class="text-sm">{confirmMessage}</p>
|
|
61
|
+
<Button {tone} disabled={pending} loading={pending} onclick={confirmAction}>
|
|
62
|
+
{pending ? pendingLabel : confirmLabel}
|
|
63
|
+
</Button>
|
|
64
|
+
<Button variant="ghost" disabled={pending} onclick={cancelConfirmation}>
|
|
65
|
+
{cancelLabel}
|
|
66
|
+
</Button>
|
|
67
|
+
</div>
|
|
68
|
+
{:else}
|
|
69
|
+
<Button {tone} onclick={beginConfirmation}>{idleLabel}</Button>
|
|
70
|
+
{/if}
|
|
71
|
+
|
|
72
|
+
{#if errorMessage !== undefined}
|
|
73
|
+
<Alert tone="error" live="assertive">{errorMessage}</Alert>
|
|
74
|
+
{/if}
|
|
75
|
+
</div>
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { Tone } from './tones.ts';
|
|
2
|
+
interface Props {
|
|
3
|
+
/** The destructive action itself. */
|
|
4
|
+
action: () => Promise<unknown>;
|
|
5
|
+
/**
|
|
6
|
+
* The resting button's text.
|
|
7
|
+
* @defaultValue `'Delete'`
|
|
8
|
+
*/
|
|
9
|
+
idleLabel?: string | undefined;
|
|
10
|
+
/**
|
|
11
|
+
* The confirm button's text.
|
|
12
|
+
* @defaultValue `'Confirm'`
|
|
13
|
+
*/
|
|
14
|
+
confirmLabel?: string | undefined;
|
|
15
|
+
/**
|
|
16
|
+
* The cancel button's text.
|
|
17
|
+
* @defaultValue `'Cancel'`
|
|
18
|
+
*/
|
|
19
|
+
cancelLabel?: string | undefined;
|
|
20
|
+
/**
|
|
21
|
+
* The confirm button's text while the action is in flight.
|
|
22
|
+
* @defaultValue `'Working…'`
|
|
23
|
+
*/
|
|
24
|
+
pendingLabel?: string | undefined;
|
|
25
|
+
/**
|
|
26
|
+
* The question shown beside the confirm and cancel buttons.
|
|
27
|
+
* @defaultValue `'Are you sure? This cannot be undone.'`
|
|
28
|
+
*/
|
|
29
|
+
confirmMessage?: string | undefined;
|
|
30
|
+
/**
|
|
31
|
+
* Shown when the action rejects with something that is not an `Error`.
|
|
32
|
+
* @defaultValue `'That didn’t work. Try again.'`
|
|
33
|
+
*/
|
|
34
|
+
failureMessage?: string | undefined;
|
|
35
|
+
/**
|
|
36
|
+
* Tone of the idle and confirm buttons.
|
|
37
|
+
* @defaultValue `'error'`
|
|
38
|
+
*/
|
|
39
|
+
tone?: Tone | undefined;
|
|
40
|
+
/** Navigated to through the components context once `action` resolves. */
|
|
41
|
+
redirectTo?: string | undefined;
|
|
42
|
+
/** Extra classes, merged last. */
|
|
43
|
+
class?: string | undefined;
|
|
44
|
+
}
|
|
45
|
+
/** A destructive action behind a two-step inline confirmation. */
|
|
46
|
+
declare const ConfirmButton: import("svelte").Component<Props, {}, "">;
|
|
47
|
+
type ConfirmButton = ReturnType<typeof ConfirmButton>;
|
|
48
|
+
export default ConfirmButton;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
<!-- @component
|
|
2
|
+
A native `<details>` wearing daisyUI’s `collapse`.
|
|
3
|
+
-->
|
|
4
|
+
<!--
|
|
5
|
+
Disclosure — a native `<details>` wearing daisyUI's `collapse`.
|
|
6
|
+
|
|
7
|
+
Hand-rolled disclosure is one of the most commonly broken widgets on the
|
|
8
|
+
web, and it does not need to be built: `<details>`/`<summary>` already give
|
|
9
|
+
a focusable trigger, the expanded/collapsed state, keyboard activation and
|
|
10
|
+
— on a modern browser — find-in-page inside the collapsed content, which no
|
|
11
|
+
JavaScript implementation matches. So this is NOT on bits-ui: there is
|
|
12
|
+
nothing for a primitive to add.
|
|
13
|
+
|
|
14
|
+
ARIA contract: the platform's. `<summary>` is the button, `open` is the
|
|
15
|
+
state, and no `aria-expanded` is written by hand (the browser maps it, and
|
|
16
|
+
a second copy can only be wrong). `open` is bindable, so a caller can drive
|
|
17
|
+
the state, and the four apps' "open this section because its form failed"
|
|
18
|
+
case is a one-line binding.
|
|
19
|
+
-->
|
|
20
|
+
<script lang="ts">import { collapseVariant } from "./tones.js";
|
|
21
|
+
let { open = $bindable(false), title, variant = "arrow", contentClass, children, class: className, ...rest } = $props();
|
|
22
|
+
</script>
|
|
23
|
+
|
|
24
|
+
<details
|
|
25
|
+
{...rest}
|
|
26
|
+
bind:open
|
|
27
|
+
class={['collapse border-base-300 bg-base-100 border', collapseVariant[variant], className]}
|
|
28
|
+
>
|
|
29
|
+
<summary class="collapse-title font-medium">
|
|
30
|
+
{#if typeof title === 'string'}
|
|
31
|
+
{title}
|
|
32
|
+
{:else}
|
|
33
|
+
{@render title()}
|
|
34
|
+
{/if}
|
|
35
|
+
</summary>
|
|
36
|
+
<div class={['collapse-content', contentClass]}>
|
|
37
|
+
{@render children()}
|
|
38
|
+
</div>
|
|
39
|
+
</details>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
|
+
import { type DisclosureVariant } from './tones.ts';
|
|
4
|
+
interface Props extends Omit<HTMLAttributes<HTMLDetailsElement>, 'title'> {
|
|
5
|
+
/** Whether the section is expanded. Bindable. */
|
|
6
|
+
open?: boolean | undefined;
|
|
7
|
+
/** The summary — its own text, or a snippet for richer markup. */
|
|
8
|
+
title: string | Snippet<[]>;
|
|
9
|
+
/**
|
|
10
|
+
* The expand indicator: an arrow, a plus, or none.
|
|
11
|
+
* @defaultValue `'arrow'`
|
|
12
|
+
*/
|
|
13
|
+
variant?: DisclosureVariant | undefined;
|
|
14
|
+
/** Extra classes on the `collapse-content` wrapper. */
|
|
15
|
+
contentClass?: string | undefined;
|
|
16
|
+
/** The content revealed when open. */
|
|
17
|
+
children: Snippet<[]>;
|
|
18
|
+
}
|
|
19
|
+
/** A native `<details>` wearing daisyUI’s `collapse`. */
|
|
20
|
+
declare const Disclosure: import("svelte").Component<Props, {}, "open">;
|
|
21
|
+
type Disclosure = ReturnType<typeof Disclosure>;
|
|
22
|
+
export default Disclosure;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
<!-- @component
|
|
2
|
+
The quiet card an index or result list shows instead of a bare empty list: a heading, optional copy, icon and call to action.
|
|
3
|
+
-->
|
|
4
|
+
<!--
|
|
5
|
+
EmptyState — the quiet card an index or a result list shows instead of a
|
|
6
|
+
bare empty `<ul>`: a heading, optional supporting copy, an optional icon
|
|
7
|
+
above and an optional call to action below.
|
|
8
|
+
|
|
9
|
+
Content-first, not a hero. "Nothing here" is a normal state of a healthy
|
|
10
|
+
page, and the surface it is drawn on is the same `Card` every other block
|
|
11
|
+
on the page uses, so an empty list does not look like a failure.
|
|
12
|
+
|
|
13
|
+
ARIA contract: `title` is a real heading at `headingLevel` (`h2` by
|
|
14
|
+
default), so a reader navigating by headings finds out the section is empty
|
|
15
|
+
instead of finding nothing at all. The icon is decorative and hidden.
|
|
16
|
+
-->
|
|
17
|
+
<script lang="ts">import Card from "./Card.svelte";
|
|
18
|
+
let { title, description, headingLevel = 2, icon, action, class: className, ...rest } = $props();
|
|
19
|
+
</script>
|
|
20
|
+
|
|
21
|
+
<Card
|
|
22
|
+
{...rest}
|
|
23
|
+
as="div"
|
|
24
|
+
class={className}
|
|
25
|
+
bodyClass="items-center gap-2 p-8 text-center sm:p-10"
|
|
26
|
+
>
|
|
27
|
+
{#if icon}
|
|
28
|
+
<div class="text-base-content/50 mb-2" aria-hidden="true">{@render icon()}</div>
|
|
29
|
+
{/if}
|
|
30
|
+
<svelte:element this={`h${headingLevel}`} class="text-2xl font-semibold">{title}</svelte:element>
|
|
31
|
+
{#if description !== undefined}
|
|
32
|
+
<p class="text-base-content/70 max-w-prose leading-7">{description}</p>
|
|
33
|
+
{/if}
|
|
34
|
+
{#if action}
|
|
35
|
+
<div class="mt-2">{@render action()}</div>
|
|
36
|
+
{/if}
|
|
37
|
+
</Card>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
|
+
import type { HeadingLevel } from './tones.ts';
|
|
4
|
+
interface Props extends HTMLAttributes<HTMLElement> {
|
|
5
|
+
/** Short, direct heading for the empty surface ("No results yet"). */
|
|
6
|
+
title: string;
|
|
7
|
+
/** Supporting copy explaining the state or the next step. */
|
|
8
|
+
description?: string | undefined;
|
|
9
|
+
/**
|
|
10
|
+
* The heading level, so the page outline stays intact.
|
|
11
|
+
* @defaultValue `2`
|
|
12
|
+
*/
|
|
13
|
+
headingLevel?: HeadingLevel | undefined;
|
|
14
|
+
/** Decorative icon rendered above the heading. */
|
|
15
|
+
icon?: Snippet<[]> | undefined;
|
|
16
|
+
/** Call to action — a button or a link — rendered under the copy. */
|
|
17
|
+
action?: Snippet<[]> | undefined;
|
|
18
|
+
}
|
|
19
|
+
/** The quiet card an index or result list shows instead of a bare empty list: a heading, optional copy, icon and call to action. */
|
|
20
|
+
declare const EmptyState: import("svelte").Component<Props, {}, "">;
|
|
21
|
+
type EmptyState = ReturnType<typeof EmptyState>;
|
|
22
|
+
export default EmptyState;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
<!-- @component
|
|
2
|
+
A link off the site that opens in a new tab with the right `rel`, and says so to assistive technology.
|
|
3
|
+
-->
|
|
4
|
+
<!--
|
|
5
|
+
ExternalLink — a link off the site, with the `rel` decision made for the
|
|
6
|
+
call site.
|
|
7
|
+
|
|
8
|
+
The `rel` is the reason this is a component and not four lines of markup.
|
|
9
|
+
An address a member typed into their own profile is untrusted outbound
|
|
10
|
+
linkage and carries `nofollow`; a resolver URL the site itself composed
|
|
11
|
+
from a recorded identifier (an ORCID, a DOI) is not, and carries only
|
|
12
|
+
`noreferrer`. The distinction is invisible when it is wrong, which is
|
|
13
|
+
exactly the kind of thing that should not be retyped per page. Hence
|
|
14
|
+
`untrusted` defaults to `true`: the safe answer is the one you get by not
|
|
15
|
+
thinking about it.
|
|
16
|
+
|
|
17
|
+
ARIA contract: `target="_blank"` always pairs with `noreferrer`, and the
|
|
18
|
+
new tab is ANNOUNCED — a visually hidden "(opens in a new tab)" after the
|
|
19
|
+
text, so a screen-reader user learns what a sighted user learns from the
|
|
20
|
+
icon. The icon itself is `aria-hidden`; it says nothing the text does not.
|
|
21
|
+
-->
|
|
22
|
+
<script lang="ts">import { ExternalLink as ExternalLinkIcon } from "@lucide/svelte";
|
|
23
|
+
import { linkVariant } from "./tones.js";
|
|
24
|
+
import VisuallyHidden from "./VisuallyHidden.svelte";
|
|
25
|
+
let { href, variant = "muted", untrusted = true, showIcon = true, newTabLabel = "(opens in a new tab)", children, class: className, ...rest } = $props();
|
|
26
|
+
</script>
|
|
27
|
+
|
|
28
|
+
<a
|
|
29
|
+
{...rest}
|
|
30
|
+
{href}
|
|
31
|
+
target="_blank"
|
|
32
|
+
rel={untrusted ? 'noreferrer nofollow' : 'noreferrer'}
|
|
33
|
+
class={['inline-flex items-center gap-1', linkVariant[variant], className]}
|
|
34
|
+
>
|
|
35
|
+
{@render children()}
|
|
36
|
+
<VisuallyHidden>{newTabLabel}</VisuallyHidden>
|
|
37
|
+
{#if showIcon}<ExternalLinkIcon class="size-3.5 shrink-0" aria-hidden="true" />{/if}
|
|
38
|
+
</a>
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { HTMLAnchorAttributes } from 'svelte/elements';
|
|
3
|
+
import { type LinkVariant } from './tones.ts';
|
|
4
|
+
interface Props extends Omit<HTMLAnchorAttributes, 'href'> {
|
|
5
|
+
/** The absolute URL. */
|
|
6
|
+
href: string;
|
|
7
|
+
/**
|
|
8
|
+
* The link weight.
|
|
9
|
+
* @defaultValue `'muted'`
|
|
10
|
+
*/
|
|
11
|
+
variant?: LinkVariant | undefined;
|
|
12
|
+
/**
|
|
13
|
+
* Whether the destination is user-supplied. `true` (the default) adds
|
|
14
|
+
* `nofollow`; pass `false` only for an address this site composed itself.
|
|
15
|
+
* @defaultValue `true`
|
|
16
|
+
*/
|
|
17
|
+
untrusted?: boolean | undefined;
|
|
18
|
+
/**
|
|
19
|
+
* Show the trailing new-tab icon.
|
|
20
|
+
* @defaultValue `true`
|
|
21
|
+
*/
|
|
22
|
+
showIcon?: boolean | undefined;
|
|
23
|
+
/**
|
|
24
|
+
* The announcement appended to the link text, for assistive technology.
|
|
25
|
+
* @defaultValue `'(opens in a new tab)'`
|
|
26
|
+
*/
|
|
27
|
+
newTabLabel?: string | undefined;
|
|
28
|
+
/** The link text; the new-tab notice is appended for assistive technology. */
|
|
29
|
+
children: Snippet<[]>;
|
|
30
|
+
}
|
|
31
|
+
/** A link off the site that opens in a new tab with the right `rel`, and says so to assistive technology. */
|
|
32
|
+
declare const ExternalLink: import("svelte").Component<Props, {}, "">;
|
|
33
|
+
type ExternalLink = ReturnType<typeof ExternalLink>;
|
|
34
|
+
export default ExternalLink;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
<!-- @component
|
|
2
|
+
A heading at any level with a stable `id`, optionally with a copyable anchor link.
|
|
3
|
+
-->
|
|
4
|
+
<!--
|
|
5
|
+
Heading — a section heading at any level, with a stable `id` so the section
|
|
6
|
+
is addressable: a table of contents, a "copy link to this section" anchor,
|
|
7
|
+
a URL fragment someone pasted into a message.
|
|
8
|
+
|
|
9
|
+
ARIA contract: a real `<h1>`–`<h6>` at the level the caller names. The level
|
|
10
|
+
is a PROP and not a consequence of the styling, because the outline is a
|
|
11
|
+
document structure and the type scale is a visual one; `class` overrides the
|
|
12
|
+
latter without touching the former. `scroll-mt` keeps a hash target clear of
|
|
13
|
+
a sticky header. The anchor link, when shown, has a real accessible name
|
|
14
|
+
(`anchorLabel`); the `#` glyph beside it is decorative.
|
|
15
|
+
|
|
16
|
+
The id is derived from `children` AT RENDER when `children` is a string —
|
|
17
|
+
not measured from the mounted element in an `$effect`, which is how the
|
|
18
|
+
original did it and which means the server renders a heading with
|
|
19
|
+
no id at all and every in-page link is dead until hydration. A snippet's
|
|
20
|
+
text is not knowable before render, so a snippet heading that wants an
|
|
21
|
+
anchor must be given an explicit `id`; without one the anchor is omitted
|
|
22
|
+
rather than pointing at nothing.
|
|
23
|
+
-->
|
|
24
|
+
<script lang="ts">import { slugify } from "./slug.js";
|
|
25
|
+
import VisuallyHidden from "./VisuallyHidden.svelte";
|
|
26
|
+
let { level, id, anchor = false, anchorLabel = "Link to this section", children, class: className, ...rest } = $props();
|
|
27
|
+
const headingId = $derived(id ?? (typeof children === "string" ? slugify(children) : undefined));
|
|
28
|
+
const levelClasses = {
|
|
29
|
+
1: "mt-8 mb-6 text-3xl sm:text-4xl",
|
|
30
|
+
2: "mt-6 mb-4 text-2xl sm:text-3xl",
|
|
31
|
+
3: "mt-5 mb-3 text-xl sm:text-2xl",
|
|
32
|
+
4: "mt-4 mb-3 text-lg sm:text-xl",
|
|
33
|
+
5: "mt-4 mb-2 text-base sm:text-lg",
|
|
34
|
+
6: "mt-3 mb-2 text-sm sm:text-base"
|
|
35
|
+
};
|
|
36
|
+
</script>
|
|
37
|
+
|
|
38
|
+
<svelte:element
|
|
39
|
+
this={`h${level}`}
|
|
40
|
+
{...rest}
|
|
41
|
+
id={headingId}
|
|
42
|
+
class={['group scroll-mt-16 font-bold', levelClasses[level], className]}
|
|
43
|
+
>
|
|
44
|
+
{#if typeof children === 'string'}
|
|
45
|
+
{children}
|
|
46
|
+
{:else}
|
|
47
|
+
{@render children()}
|
|
48
|
+
{/if}
|
|
49
|
+
{#if anchor && headingId !== undefined}
|
|
50
|
+
<a
|
|
51
|
+
href={`#${headingId}`}
|
|
52
|
+
class="link link-hover text-base-content/40 ml-2 opacity-0 transition-opacity focus:opacity-100 group-hover:opacity-100"
|
|
53
|
+
>
|
|
54
|
+
<span aria-hidden="true">#</span>
|
|
55
|
+
<VisuallyHidden>{anchorLabel}</VisuallyHidden>
|
|
56
|
+
</a>
|
|
57
|
+
{/if}
|
|
58
|
+
</svelte:element>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
|
+
import type { HeadingLevel } from './tones.ts';
|
|
4
|
+
interface Props extends Omit<HTMLAttributes<HTMLHeadingElement>, 'children'> {
|
|
5
|
+
/** The heading level (`h1`–`h6`); choose it from the outline, not the size. */
|
|
6
|
+
level: HeadingLevel;
|
|
7
|
+
/** Explicit anchor id; derived from `children` when it is a string. */
|
|
8
|
+
id?: string | undefined;
|
|
9
|
+
/** Render a permalink beside the heading. Needs an id. */
|
|
10
|
+
anchor?: boolean | undefined;
|
|
11
|
+
/**
|
|
12
|
+
* Accessible name of the permalink.
|
|
13
|
+
* @defaultValue `'Link to this section'`
|
|
14
|
+
*/
|
|
15
|
+
anchorLabel?: string | undefined;
|
|
16
|
+
/** The heading text, as a string or a snippet. */
|
|
17
|
+
children: Snippet<[]> | string;
|
|
18
|
+
}
|
|
19
|
+
/** A heading at any level with a stable `id`, optionally with a copyable anchor link. */
|
|
20
|
+
declare const Heading: import("svelte").Component<Props, {}, "">;
|
|
21
|
+
type Heading = ReturnType<typeof Heading>;
|
|
22
|
+
export default Heading;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<!-- @component
|
|
2
|
+
An in-text anchor in one of three weights; the plain link of the library.
|
|
3
|
+
-->
|
|
4
|
+
<!--
|
|
5
|
+
Link — an in-text anchor in one of three weights. The plain `<a>` of the
|
|
6
|
+
library: everything that is a link and not an action goes through it, so
|
|
7
|
+
that "what a link looks like" is one decision.
|
|
8
|
+
|
|
9
|
+
ARIA contract: a native `<a href>`, so role, focus and activation are the
|
|
10
|
+
platform's. `external` adds `target="_blank"` and `rel="noreferrer"`
|
|
11
|
+
(which implies `noopener`) — the two always travel together here, because a
|
|
12
|
+
new tab handed the referrer is the leak. An external link that must also
|
|
13
|
+
announce the new tab wants `ExternalLink`, which carries the visually
|
|
14
|
+
hidden warning; this one stays silent so it can be used mid-sentence.
|
|
15
|
+
|
|
16
|
+
`href` resolves through the components context unless `external`: an
|
|
17
|
+
absolute off-site URL is not the app's to rewrite.
|
|
18
|
+
-->
|
|
19
|
+
<script lang="ts">import { getComponentsContext } from "../context.svelte.js";
|
|
20
|
+
import { linkVariant } from "./tones.js";
|
|
21
|
+
let { href, variant = "primary", external = false, children, class: className, ...rest } = $props();
|
|
22
|
+
const context = getComponentsContext();
|
|
23
|
+
const resolved = $derived(external ? href : context.href(href));
|
|
24
|
+
</script>
|
|
25
|
+
|
|
26
|
+
<a
|
|
27
|
+
{...rest}
|
|
28
|
+
href={resolved}
|
|
29
|
+
target={external ? '_blank' : undefined}
|
|
30
|
+
rel={external ? 'noreferrer' : undefined}
|
|
31
|
+
class={[linkVariant[variant], className]}
|
|
32
|
+
>
|
|
33
|
+
{@render children()}
|
|
34
|
+
</a>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { HTMLAnchorAttributes } from 'svelte/elements';
|
|
3
|
+
import { type LinkVariant } from './tones.ts';
|
|
4
|
+
interface Props extends Omit<HTMLAnchorAttributes, 'href'> {
|
|
5
|
+
/** The target; an app path is resolved through the components context. */
|
|
6
|
+
href: string;
|
|
7
|
+
/**
|
|
8
|
+
* The link weight.
|
|
9
|
+
* @defaultValue `'primary'`
|
|
10
|
+
*/
|
|
11
|
+
variant?: LinkVariant | undefined;
|
|
12
|
+
/** Open in a new tab with the safe `rel`. */
|
|
13
|
+
external?: boolean | undefined;
|
|
14
|
+
/** The link text; the anchor's accessible name. */
|
|
15
|
+
children: Snippet<[]>;
|
|
16
|
+
}
|
|
17
|
+
/** An in-text anchor in one of three weights; the plain link of the library. */
|
|
18
|
+
declare const Link: import("svelte").Component<Props, {}, "">;
|
|
19
|
+
type Link = ReturnType<typeof Link>;
|
|
20
|
+
export default Link;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
<!-- @component
|
|
2
|
+
An `<a>` styled as a daisyUI `btn`, for navigation that reads as an action; it keeps every link behaviour.
|
|
3
|
+
-->
|
|
4
|
+
<!--
|
|
5
|
+
LinkButton — an `<a>` wearing the daisyUI `btn`, for navigation that reads
|
|
6
|
+
as an action ("View results", "Edit"). It is a link, not a button: it
|
|
7
|
+
navigates, so it must be an anchor and must keep Ctrl-click, middle-click
|
|
8
|
+
and "copy link address".
|
|
9
|
+
|
|
10
|
+
ARIA contract: a disabled link keeps its text but drops its `href`, takes
|
|
11
|
+
`role="button"` + `aria-disabled="true"` and leaves the tab order with
|
|
12
|
+
`tabindex="-1"` — an `<a>` without `href` is not focusable and has no role,
|
|
13
|
+
so both have to be restored by hand for the disabled state to be announced
|
|
14
|
+
at all. `external` opens a new tab with `rel="noreferrer"`, which implies
|
|
15
|
+
`noopener`; the pairing is not something a call site should be able to
|
|
16
|
+
forget.
|
|
17
|
+
|
|
18
|
+
`href` resolves through the components context (base path, locale prefix)
|
|
19
|
+
unless the link is `external`, where the destination is an absolute URL the
|
|
20
|
+
app has no business rewriting.
|
|
21
|
+
-->
|
|
22
|
+
<script lang="ts">import { getComponentsContext } from "../context.svelte.js";
|
|
23
|
+
import { buttonSize, buttonTone, buttonVariant } from "./tones.js";
|
|
24
|
+
let { href, tone, variant = "solid", size = "md", disabled = false, block = false, external = false, icon, children, class: className, ...rest } = $props();
|
|
25
|
+
const context = getComponentsContext();
|
|
26
|
+
const resolved = $derived(external ? href : context.href(href));
|
|
27
|
+
</script>
|
|
28
|
+
|
|
29
|
+
<a
|
|
30
|
+
{...rest}
|
|
31
|
+
href={disabled ? undefined : resolved}
|
|
32
|
+
role={disabled ? 'button' : undefined}
|
|
33
|
+
aria-disabled={disabled ? 'true' : undefined}
|
|
34
|
+
tabindex={disabled ? -1 : undefined}
|
|
35
|
+
target={external ? '_blank' : undefined}
|
|
36
|
+
rel={external ? 'noreferrer' : undefined}
|
|
37
|
+
class={[
|
|
38
|
+
'btn',
|
|
39
|
+
tone !== undefined && buttonTone[tone],
|
|
40
|
+
buttonVariant[variant],
|
|
41
|
+
buttonSize[size],
|
|
42
|
+
block && 'btn-block',
|
|
43
|
+
disabled && 'btn-disabled',
|
|
44
|
+
className,
|
|
45
|
+
]}
|
|
46
|
+
>
|
|
47
|
+
{#if icon}
|
|
48
|
+
{@render icon()}
|
|
49
|
+
{/if}
|
|
50
|
+
{@render children()}
|
|
51
|
+
</a>
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { HTMLAnchorAttributes } from 'svelte/elements';
|
|
3
|
+
import { type ButtonVariant, type Size, type Tone } from './tones.ts';
|
|
4
|
+
interface Props extends Omit<HTMLAnchorAttributes, 'href'> {
|
|
5
|
+
/** The target; an app path is resolved through the components context. */
|
|
6
|
+
href: string;
|
|
7
|
+
/** Semantic colour. */
|
|
8
|
+
tone?: Tone | undefined;
|
|
9
|
+
/**
|
|
10
|
+
* daisyUI button style.
|
|
11
|
+
* @defaultValue `'solid'`
|
|
12
|
+
*/
|
|
13
|
+
variant?: ButtonVariant | undefined;
|
|
14
|
+
/**
|
|
15
|
+
* daisyUI size step.
|
|
16
|
+
* @defaultValue `'md'`
|
|
17
|
+
*/
|
|
18
|
+
size?: Size | undefined;
|
|
19
|
+
/** Renders the button as unavailable without removing its text. */
|
|
20
|
+
disabled?: boolean | undefined;
|
|
21
|
+
/** Full-width. */
|
|
22
|
+
block?: boolean | undefined;
|
|
23
|
+
/** Open in a new tab with the safe `rel`. */
|
|
24
|
+
external?: boolean | undefined;
|
|
25
|
+
/** Leading icon. */
|
|
26
|
+
icon?: Snippet<[]> | undefined;
|
|
27
|
+
/** The button text; the anchor's accessible name. */
|
|
28
|
+
children: Snippet<[]>;
|
|
29
|
+
}
|
|
30
|
+
/** An `<a>` styled as a daisyUI `btn`, for navigation that reads as an action; it keeps every link behaviour. */
|
|
31
|
+
declare const LinkButton: import("svelte").Component<Props, {}, "">;
|
|
32
|
+
type LinkButton = ReturnType<typeof LinkButton>;
|
|
33
|
+
export default LinkButton;
|