@cl-light-ds/ui 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +20 -18
- package/src/components/alert/Alert.astro +8 -2
- package/src/components/alert/CHANGELOG.md +9 -0
- package/src/components/button/Button.astro +78 -19
- package/src/components/button/CHANGELOG.md +22 -0
- package/src/components/button-group/ButtonGroup.astro +14 -3
- package/src/components/button-group/CHANGELOG.md +17 -0
- package/src/components/callout/CHANGELOG.md +32 -0
- package/src/components/callout/Callout.astro +147 -0
- package/src/components/callout/CalloutDescription.astro +16 -0
- package/src/components/callout/CalloutTitle.astro +16 -0
- package/src/components/callout/index.ts +3 -0
- package/src/components/card/CHANGELOG.md +9 -0
- package/src/components/card/Card.astro +22 -0
- package/src/components/combobox/CHANGELOG.md +14 -0
- package/src/components/combobox/ComboboxTrigger.astro +31 -1
- package/src/components/command-search/CHANGELOG.md +27 -0
- package/src/components/command-search/CommandSearch.astro +515 -0
- package/src/components/command-search/CommandSearchGroup.astro +32 -0
- package/src/components/command-search/CommandSearchItem.astro +64 -0
- package/src/components/command-search/classes.ts +18 -0
- package/src/components/command-search/index.ts +12 -0
- package/src/components/command-search/nothing-matches.svg +42 -0
- package/src/components/dialog/CHANGELOG.md +13 -0
- package/src/components/dialog/DialogClose.astro +26 -12
- package/src/components/dropdown-menu/CHANGELOG.md +7 -0
- package/src/components/dropdown-menu/dropdown-menu-item-variants.ts +1 -1
- package/src/components/empty-state/CHANGELOG.md +18 -0
- package/src/components/empty-state/EmptyState.astro +157 -0
- package/src/components/empty-state/illustrations/AllCaughtUp.astro +52 -0
- package/src/components/empty-state/illustrations/EmptyWorkspaces.astro +56 -0
- package/src/components/empty-state/illustrations/LoadError.astro +61 -0
- package/src/components/empty-state/illustrations/NoResults.astro +60 -0
- package/src/components/empty-state/illustrations/NothingMatches.astro +66 -0
- package/src/components/empty-state/illustrations/index.ts +27 -0
- package/src/components/empty-state/index.ts +7 -0
- package/src/components/favorite-toggle/CHANGELOG.md +13 -0
- package/src/components/favorite-toggle/FavoriteToggle.astro +94 -0
- package/src/components/favorite-toggle/index.ts +1 -0
- package/src/components/input/CHANGELOG.md +15 -0
- package/src/components/input/InputOTP.astro +211 -0
- package/src/components/input/index.ts +1 -1
- package/src/components/input-group/CHANGELOG.md +7 -0
- package/src/components/input-group/InputGroup.astro +2 -0
- package/src/components/loading-overlay/CHANGELOG.md +25 -0
- package/src/components/loading-overlay/LoadingOverlay.astro +179 -0
- package/src/components/loading-overlay/index.ts +8 -0
- package/src/components/logo/CHANGELOG.md +17 -0
- package/src/components/logo/Logo.astro +90 -0
- package/src/components/logo/index.ts +3 -0
- package/src/components/logo/logo-paths.ts +57 -0
- package/src/components/notifications-menu/CHANGELOG.md +22 -0
- package/src/components/notifications-menu/NotificationsMenu.astro +374 -0
- package/src/components/notifications-menu/NotificationsMenuItem.astro +129 -0
- package/src/components/notifications-menu/all-caught-up.svg +28 -0
- package/src/components/notifications-menu/index.ts +3 -0
- package/src/components/notifications-menu/types.ts +32 -0
- package/src/components/option-card/CHANGELOG.md +16 -0
- package/src/components/option-card/OptionCard.astro +135 -0
- package/src/components/option-card/index.ts +1 -0
- package/src/components/organisms/top-bar/CHANGELOG.md +21 -0
- package/src/components/organisms/top-bar/TopBar.astro +264 -0
- package/src/components/organisms/top-bar/index.ts +2 -0
- package/src/components/organisms/top-bar/types.ts +30 -0
- package/src/components/organisms/workspace/CHANGELOG.md +73 -0
- package/src/components/organisms/workspace/OrganisationOverview.astro +62 -0
- package/src/components/organisms/workspace/RecentWorkspaceCard.astro +235 -0
- package/src/components/organisms/workspace/RecentWorkspaces.astro +93 -0
- package/src/components/organisms/workspace/WorkspaceAvatar.astro +144 -0
- package/src/components/organisms/workspace/WorkspaceCard.astro +172 -0
- package/src/components/organisms/workspace/WorkspaceGlyph.astro +62 -0
- package/src/components/organisms/workspace/WorkspaceIcon.astro +42 -0
- package/src/components/organisms/workspace/WorkspaceIconPicker.astro +288 -0
- package/src/components/organisms/workspace/WorkspaceIconPickerGlyph.astro +44 -0
- package/src/components/organisms/workspace/icons.ts +98 -0
- package/src/components/organisms/workspace/index.ts +35 -0
- package/src/components/organisms/workspace/labels.ts +54 -0
- package/src/components/organisms/workspace/recent-card-rings-strong.png +0 -0
- package/src/components/organisms/workspace/types.ts +30 -0
- package/src/components/profile-menu/CHANGELOG.md +22 -0
- package/src/components/profile-menu/ProfileMenu.astro +236 -0
- package/src/components/profile-menu/index.ts +2 -0
- package/src/components/profile-menu/types.ts +27 -0
- package/src/components/search-field/CHANGELOG.md +14 -0
- package/src/components/search-field/SearchField.astro +163 -0
- package/src/components/search-field/index.ts +1 -0
- package/src/components/table/CHANGELOG.md +9 -0
- package/src/components/table/TableCell.astro +6 -0
- package/src/components/table/TableHead.astro +5 -1
- package/src/components/table/TableRow.astro +3 -1
- package/src/components/table/table-cell-variants.ts +2 -0
- package/src/components/toast/CHANGELOG.md +32 -0
- package/src/components/toast/Toast.astro +145 -0
- package/src/components/toast/Toaster.astro +322 -0
- package/src/components/toast/index.ts +9 -0
- package/src/components/toggle/CHANGELOG.md +7 -0
- package/src/lib/command-search.ts +73 -0
- package/src/lib/loading-overlay.ts +174 -0
- package/src/lib/toast.ts +127 -0
- package/src/lib/toggle-shared.ts +4 -1
- package/src/lib/workspace-palette.ts +119 -0
- package/src/styles/CHANGELOG.md +22 -0
- package/src/styles/globals.css +100 -1
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* @component Toast
|
|
4
|
+
* @title Toast
|
|
5
|
+
* @description One toast: an icon, a title and a line of text, an optional action and a ✕. `Toaster` uses it as the template for every toast it shows; on its own it renders a static toast (docs, screenshots).
|
|
6
|
+
*
|
|
7
|
+
* @preview
|
|
8
|
+
*
|
|
9
|
+
* <ol class="flex w-96 max-w-full list-none flex-col gap-3 p-0">
|
|
10
|
+
* <Toast kind="info" title="Export started" description="We'll let you know when Acme Finance is ready." />
|
|
11
|
+
* <Toast kind="success" title="Export ready" description="Acme Finance export.zip" action="Download" />
|
|
12
|
+
* <Toast kind="error" title="Couldn't open workspace" description="Something went wrong opening Acme Finance." />
|
|
13
|
+
* </ol>
|
|
14
|
+
*
|
|
15
|
+
* @usage
|
|
16
|
+
*
|
|
17
|
+
* ```astro nocollapse
|
|
18
|
+
* ---
|
|
19
|
+
* import { Toast } from '@cl-light-ds/ui/components/toast';
|
|
20
|
+
* ---
|
|
21
|
+
*
|
|
22
|
+
* <ol class="list-none p-0">
|
|
23
|
+
* <Toast kind="success" title="Export ready" description="Acme.zip" action="Download" />
|
|
24
|
+
* </ol>
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* Kinds: `info` (blue icon), `success` (green icon), `error` (light red tint,
|
|
28
|
+
* red border, red icon and title). The icon and the ✕ sit at the top; the
|
|
29
|
+
* action (an outline button) is centred on the right (D168, D169).
|
|
30
|
+
*
|
|
31
|
+
* Inside a `Toaster`, `@data-slot/toast` fills `toast-title`,
|
|
32
|
+
* `toast-description` and `toast-action`, and sets `data-type` (the kind),
|
|
33
|
+
* `data-front`, `data-expanded` and `data-state` on the item. Once a toast is
|
|
34
|
+
* in, the Toaster sets `data-attention="on"` for 2.4s: a ring ripples out
|
|
35
|
+
* three times (info, success) or the card shakes (error) (D164, D175).
|
|
36
|
+
*/
|
|
37
|
+
import { CircleCheck, CircleX, Info, X } from "@lucide/astro";
|
|
38
|
+
import { cn } from "@cl-light-ds/ui/lib/utils";
|
|
39
|
+
import type { HTMLAttributes } from "astro/types";
|
|
40
|
+
|
|
41
|
+
import Button from "../button/Button.astro";
|
|
42
|
+
|
|
43
|
+
interface Props extends HTMLAttributes<"li"> {
|
|
44
|
+
kind?: "info" | "success" | "error";
|
|
45
|
+
title?: string;
|
|
46
|
+
description?: string;
|
|
47
|
+
/** The action button's label; no action when left out. */
|
|
48
|
+
action?: string;
|
|
49
|
+
/** Accessible name of the ✕. */
|
|
50
|
+
closeLabel?: string;
|
|
51
|
+
class?: string;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const {
|
|
55
|
+
kind = "info",
|
|
56
|
+
title = "",
|
|
57
|
+
description = "",
|
|
58
|
+
action = "",
|
|
59
|
+
closeLabel = "Close",
|
|
60
|
+
class: className = "",
|
|
61
|
+
...rest
|
|
62
|
+
} = Astro.props as Props;
|
|
63
|
+
|
|
64
|
+
const error = kind === "error";
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
<li
|
|
68
|
+
data-slot="toast-item"
|
|
69
|
+
data-type={kind}
|
|
70
|
+
role={error ? "alert" : "status"}
|
|
71
|
+
aria-live={error ? "assertive" : "polite"}
|
|
72
|
+
aria-atomic="true"
|
|
73
|
+
class={cn("group/toast list-none", className)}
|
|
74
|
+
{...rest}
|
|
75
|
+
>
|
|
76
|
+
{/* D164, D175 — the ripple: three rings in the kind's colour (not on errors). */}
|
|
77
|
+
<span
|
|
78
|
+
data-slot="toast-ripple"
|
|
79
|
+
aria-hidden="true"
|
|
80
|
+
class="ring-primary group-data-[type=success]/toast:ring-success pointer-events-none absolute inset-0 rounded-lg opacity-0 ring-2 group-data-[type=error]/toast:hidden group-data-[attention=on]/toast:animate-out group-data-[attention=on]/toast:fade-out group-data-[attention=on]/toast:zoom-out-105 group-data-[attention=on]/toast:fill-mode-forwards group-data-[attention=on]/toast:repeat-3 group-data-[attention=on]/toast:animation-duration-700 group-data-[attention=on]/toast:opacity-100 motion-reduce:hidden"
|
|
81
|
+
></span>
|
|
82
|
+
<div
|
|
83
|
+
data-slot="toast-card"
|
|
84
|
+
class="bg-popover text-popover-foreground group-data-[type=error]/toast:bg-callout-error group-data-[type=error]/toast:border-destructive/40 relative h-full overflow-hidden rounded-lg border p-4 pe-10 shadow-md transition-shadow group-data-[expanded=true]/toast:shadow-xs group-data-[attention=on]/toast:group-data-[type=error]/toast:animate-in group-data-[attention=on]/toast:group-data-[type=error]/toast:slide-in-from-left-3 group-data-[attention=on]/toast:group-data-[type=error]/toast:direction-alternate group-data-[attention=on]/toast:group-data-[type=error]/toast:repeat-7 group-data-[attention=on]/toast:group-data-[type=error]/toast:animation-duration-75 motion-reduce:animate-none!"
|
|
85
|
+
>
|
|
86
|
+
<div data-slot="toast-body" class="flex items-start gap-3">
|
|
87
|
+
<Info
|
|
88
|
+
aria-hidden="true"
|
|
89
|
+
class="text-primary mt-0.5 size-5 shrink-0 group-data-[type=error]/toast:hidden group-data-[type=success]/toast:hidden"
|
|
90
|
+
/>
|
|
91
|
+
<CircleCheck
|
|
92
|
+
aria-hidden="true"
|
|
93
|
+
class="text-success mt-0.5 hidden size-5 shrink-0 group-data-[type=success]/toast:block"
|
|
94
|
+
/>
|
|
95
|
+
<CircleX
|
|
96
|
+
aria-hidden="true"
|
|
97
|
+
class="text-destructive mt-0.5 hidden size-5 shrink-0 group-data-[type=error]/toast:block"
|
|
98
|
+
/>
|
|
99
|
+
<div class="flex min-w-0 flex-1 flex-col gap-1">
|
|
100
|
+
<p
|
|
101
|
+
data-slot="toast-title"
|
|
102
|
+
class="text-sm font-semibold group-data-[type=error]/toast:text-destructive"
|
|
103
|
+
>
|
|
104
|
+
{title}
|
|
105
|
+
</p>
|
|
106
|
+
<p
|
|
107
|
+
data-slot="toast-description"
|
|
108
|
+
class="text-muted-foreground text-sm group-data-[type=error]/toast:text-foreground"
|
|
109
|
+
hidden={!description}
|
|
110
|
+
>
|
|
111
|
+
{description}
|
|
112
|
+
</p>
|
|
113
|
+
</div>
|
|
114
|
+
<Button
|
|
115
|
+
data-slot="toast-action"
|
|
116
|
+
type="button"
|
|
117
|
+
size="sm"
|
|
118
|
+
variant="outline"
|
|
119
|
+
class="shrink-0 self-center"
|
|
120
|
+
hidden={!action}
|
|
121
|
+
>
|
|
122
|
+
{action}
|
|
123
|
+
</Button>
|
|
124
|
+
</div>
|
|
125
|
+
<Button
|
|
126
|
+
data-slot="toast-close"
|
|
127
|
+
type="button"
|
|
128
|
+
variant="ghost"
|
|
129
|
+
size="icon-sm"
|
|
130
|
+
aria-label={closeLabel}
|
|
131
|
+
class="group-data-[type=error]/toast:hover:bg-destructive/10 absolute top-2 right-2"
|
|
132
|
+
>
|
|
133
|
+
<X />
|
|
134
|
+
</Button>
|
|
135
|
+
</div>
|
|
136
|
+
</li>
|
|
137
|
+
|
|
138
|
+
<style is:global>
|
|
139
|
+
/* In a layer, so the Toaster's stack rules (more specific) take over inside it. */
|
|
140
|
+
@layer components {
|
|
141
|
+
[data-slot="toast-item"] {
|
|
142
|
+
position: relative;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
</style>
|
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* @component Toaster
|
|
4
|
+
* @title Toaster
|
|
5
|
+
* @description The toast stack: a live region under the top bar's right end where toasts slide in from the right, stack (the newest in front, two peeking out below) and unfurl into a list with Clear all on hover or keyboard focus.
|
|
6
|
+
*
|
|
7
|
+
* @preview
|
|
8
|
+
*
|
|
9
|
+
* <Toaster />
|
|
10
|
+
* <Button variant="outline" data-demo-toast="success">Show a toast</Button>
|
|
11
|
+
*
|
|
12
|
+
* @usage
|
|
13
|
+
*
|
|
14
|
+
* ```astro nocollapse
|
|
15
|
+
* ---
|
|
16
|
+
* import { Toaster } from '@cl-light-ds/ui/components/toast';
|
|
17
|
+
* ---
|
|
18
|
+
*
|
|
19
|
+
* <!-- once per page, e.g. in the layout -->
|
|
20
|
+
* <Toaster top="41px" />
|
|
21
|
+
*
|
|
22
|
+
* <script>
|
|
23
|
+
* import { toast } from '@cl-light-ds/ui/lib/toast';
|
|
24
|
+
*
|
|
25
|
+
* toast({ kind: 'success', title: 'Export ready', description: 'Acme Finance export.zip',
|
|
26
|
+
* action: { label: 'Download', href: '/exports/acme.zip', download: true } });
|
|
27
|
+
* </script>
|
|
28
|
+
* ```
|
|
29
|
+
*
|
|
30
|
+
* @api
|
|
31
|
+
*
|
|
32
|
+
* ### JS (`@cl-light-ds/ui/lib/toast`)
|
|
33
|
+
*
|
|
34
|
+
* | Function | Description |
|
|
35
|
+
* |----------|-------------|
|
|
36
|
+
* | `toast({ kind, title, description, action, duration, id, closeLabel, toaster })` | Show a toast, returns its id |
|
|
37
|
+
* | `toast.update(id, patch)` | Change a showing toast |
|
|
38
|
+
* | `toast.dismiss(id)` | Close one |
|
|
39
|
+
* | `toast.clear()` | Close all |
|
|
40
|
+
*
|
|
41
|
+
* ### Events (on the `[data-slot="toast"]` root, from `@data-slot/toast`)
|
|
42
|
+
*
|
|
43
|
+
* Inbound: `toast:show` (`{ title, description, type, action, duration, id }`), `toast:update`, `toast:dismiss`, `toast:clear`.
|
|
44
|
+
* Outbound: `toast:change` (`{ id, action: "show" | "dismiss" }`), `toast:action` (`{ id, value }`), and `toaster:ready` once bound.
|
|
45
|
+
*
|
|
46
|
+
* ### Placement
|
|
47
|
+
*
|
|
48
|
+
* Fixed; `top` is where the top bar ends (default 41px, the top bar's height), `offset` the gap under it, `right` the inset from the
|
|
49
|
+
* window's right edge (plus `--data-slot-scrollbar-width` while a dialog hides the scrollbar). Unfurled with two or more toasts the
|
|
50
|
+
* gap closes so Clear all sits flush under the bar (D172).
|
|
51
|
+
*/
|
|
52
|
+
import { cn } from "@cl-light-ds/ui/lib/utils";
|
|
53
|
+
import type { HTMLAttributes } from "astro/types";
|
|
54
|
+
|
|
55
|
+
import Button from "../button/Button.astro";
|
|
56
|
+
import Toast from "./Toast.astro";
|
|
57
|
+
|
|
58
|
+
interface Props extends HTMLAttributes<"ol"> {
|
|
59
|
+
/** Where the bar above ends (CSS length). Default 41px. */
|
|
60
|
+
top?: string;
|
|
61
|
+
/** Gap between the bar and the stack (CSS length). Default 0.5rem. */
|
|
62
|
+
offset?: string;
|
|
63
|
+
/** Inset from the window's right edge (CSS length). Default 1rem, 1.5rem from md. */
|
|
64
|
+
right?: string;
|
|
65
|
+
/** ms before a toast closes by itself; 0 = stays until closed (D166). */
|
|
66
|
+
duration?: number;
|
|
67
|
+
/** Toasts kept visible at once; older ones wait, hidden, until others close. */
|
|
68
|
+
limit?: number;
|
|
69
|
+
/** Accessible name of the region. */
|
|
70
|
+
label?: string;
|
|
71
|
+
clearAllLabel?: string;
|
|
72
|
+
closeLabel?: string;
|
|
73
|
+
/** Root id, to target this Toaster from `toast({ toaster })`. */
|
|
74
|
+
id?: string;
|
|
75
|
+
class?: string;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const {
|
|
79
|
+
top,
|
|
80
|
+
offset,
|
|
81
|
+
right,
|
|
82
|
+
duration = 0,
|
|
83
|
+
limit = 5,
|
|
84
|
+
label = "Notifications",
|
|
85
|
+
clearAllLabel = "Clear all",
|
|
86
|
+
closeLabel = "Close",
|
|
87
|
+
id,
|
|
88
|
+
class: className = "",
|
|
89
|
+
style,
|
|
90
|
+
...rest
|
|
91
|
+
} = Astro.props as Props;
|
|
92
|
+
|
|
93
|
+
const vars = [
|
|
94
|
+
top && `--toaster-top: ${top}`,
|
|
95
|
+
offset && `--toaster-offset: ${offset}`,
|
|
96
|
+
right && `--toaster-right: ${right}`,
|
|
97
|
+
]
|
|
98
|
+
.filter(Boolean)
|
|
99
|
+
.join("; ");
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
<div
|
|
103
|
+
data-slot="toast"
|
|
104
|
+
data-position="top-right"
|
|
105
|
+
data-duration={duration}
|
|
106
|
+
data-limit={limit}
|
|
107
|
+
id={id}
|
|
108
|
+
class="contents"
|
|
109
|
+
>
|
|
110
|
+
<template data-slot="toast-template">
|
|
111
|
+
<Toast closeLabel={closeLabel} />
|
|
112
|
+
</template>
|
|
113
|
+
<ol
|
|
114
|
+
data-slot="toast-viewport"
|
|
115
|
+
role="region"
|
|
116
|
+
aria-label={label}
|
|
117
|
+
class={cn("group/toaster", className)}
|
|
118
|
+
style={[vars, style].filter(Boolean).join("; ") || undefined}
|
|
119
|
+
{...rest}
|
|
120
|
+
>
|
|
121
|
+
{/* D171, D172, C23 — above the unfurled list: a link button, no hover background, only the underline. */}
|
|
122
|
+
<li data-slot="toaster-clear-all">
|
|
123
|
+
<Button
|
|
124
|
+
type="button"
|
|
125
|
+
variant="link"
|
|
126
|
+
size="sm"
|
|
127
|
+
class="bg-background hover:bg-background rounded-none px-2"
|
|
128
|
+
data-toaster-clear
|
|
129
|
+
>
|
|
130
|
+
{clearAllLabel}
|
|
131
|
+
</Button>
|
|
132
|
+
</li>
|
|
133
|
+
</ol>
|
|
134
|
+
</div>
|
|
135
|
+
|
|
136
|
+
<style is:global>
|
|
137
|
+
/*
|
|
138
|
+
* Stack geometry on top of @data-slot/toast's tokens (--toast-index,
|
|
139
|
+
* --toast-offset, --toast-initial-height, --toast-front-height,
|
|
140
|
+
* --toast-count, --toast-expanded-stack-size). D161, D164, D167: the newest
|
|
141
|
+
* in front, two more peeking out 10px below it, 5% smaller each and blank;
|
|
142
|
+
* unfurled, one under the other 12px apart at their own heights; 300ms.
|
|
143
|
+
*/
|
|
144
|
+
@layer components {
|
|
145
|
+
[data-slot="toast-viewport"] {
|
|
146
|
+
--toast-gap: 12px;
|
|
147
|
+
--toast-collapsed-peek: 10px;
|
|
148
|
+
--toaster-slide: 2rem;
|
|
149
|
+
--toaster-band: 0px;
|
|
150
|
+
--toaster-lift: 0px;
|
|
151
|
+
position: fixed;
|
|
152
|
+
z-index: 50;
|
|
153
|
+
top: calc(
|
|
154
|
+
var(--toaster-top, 41px) + var(--toaster-offset, 0.5rem) -
|
|
155
|
+
var(--toaster-lift)
|
|
156
|
+
);
|
|
157
|
+
right: calc(
|
|
158
|
+
var(--toaster-right, 1rem) + var(--data-slot-scrollbar-width, 0px)
|
|
159
|
+
);
|
|
160
|
+
width: min(24rem, calc(100vw - 2 * var(--toaster-right, 1rem)));
|
|
161
|
+
height: calc(
|
|
162
|
+
var(--toast-front-height, 0px) +
|
|
163
|
+
min(var(--toast-count, 0) - 1, 2) * var(--toast-collapsed-peek)
|
|
164
|
+
);
|
|
165
|
+
margin: 0;
|
|
166
|
+
padding: 0;
|
|
167
|
+
list-style: none;
|
|
168
|
+
transition:
|
|
169
|
+
top 300ms ease-out,
|
|
170
|
+
height 300ms ease-out;
|
|
171
|
+
}
|
|
172
|
+
@media (min-width: 48rem) {
|
|
173
|
+
[data-slot="toast-viewport"] {
|
|
174
|
+
--toaster-right: 1.5rem;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
[data-slot="toast-viewport"][data-expanded] {
|
|
178
|
+
height: calc(
|
|
179
|
+
var(--toast-expanded-stack-size, 0px) + var(--toaster-band)
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
/* D170–D172 — two or more, unfurled: room for Clear all, flush under the bar. */
|
|
183
|
+
[data-slot="toast-viewport"][data-expanded]:has(
|
|
184
|
+
> [data-slot="toast-item"][data-front="false"][data-removed="false"]
|
|
185
|
+
) {
|
|
186
|
+
--toaster-band: 2rem;
|
|
187
|
+
--toaster-lift: var(--toaster-offset, 0.5rem);
|
|
188
|
+
}
|
|
189
|
+
[data-slot="toaster-clear-all"] {
|
|
190
|
+
display: none;
|
|
191
|
+
position: absolute;
|
|
192
|
+
top: 0;
|
|
193
|
+
right: 0;
|
|
194
|
+
z-index: 1000;
|
|
195
|
+
}
|
|
196
|
+
[data-slot="toast-viewport"][data-expanded]:has(
|
|
197
|
+
> [data-slot="toast-item"][data-front="false"][data-removed="false"]
|
|
198
|
+
)
|
|
199
|
+
> [data-slot="toaster-clear-all"] {
|
|
200
|
+
display: block;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
[data-slot="toast-viewport"] > [data-slot="toast-item"] {
|
|
204
|
+
--toast-y: min(
|
|
205
|
+
calc(var(--toast-index, 0) * var(--toast-collapsed-peek)),
|
|
206
|
+
calc(2 * var(--toast-collapsed-peek))
|
|
207
|
+
);
|
|
208
|
+
--toast-s: calc(1 - min(var(--toast-index, 0), 3) * 0.05);
|
|
209
|
+
position: absolute;
|
|
210
|
+
inset-inline: 0;
|
|
211
|
+
top: 0;
|
|
212
|
+
height: var(--toast-initial-height, auto);
|
|
213
|
+
transform-origin: bottom;
|
|
214
|
+
translate: var(--toaster-slide) var(--toast-y);
|
|
215
|
+
scale: var(--toast-s);
|
|
216
|
+
opacity: 0;
|
|
217
|
+
transition:
|
|
218
|
+
translate 300ms ease-out,
|
|
219
|
+
scale 300ms ease-out,
|
|
220
|
+
opacity 300ms ease-out,
|
|
221
|
+
height 300ms ease-out;
|
|
222
|
+
}
|
|
223
|
+
[data-slot="toast-viewport"] > [data-slot="toast-item"][data-expanded="true"] {
|
|
224
|
+
--toast-y: calc(var(--toast-offset, 0px) + var(--toaster-band));
|
|
225
|
+
--toast-s: 1;
|
|
226
|
+
}
|
|
227
|
+
[data-slot="toast-viewport"]
|
|
228
|
+
> [data-slot="toast-item"][data-expanded="false"][data-front="false"] {
|
|
229
|
+
height: var(--toast-front-height);
|
|
230
|
+
}
|
|
231
|
+
/* In: slid home; a fourth one and beyond stay out of sight while stacked. */
|
|
232
|
+
[data-slot="toast-viewport"] > [data-slot="toast-item"][data-mounted="true"] {
|
|
233
|
+
translate: 0 var(--toast-y);
|
|
234
|
+
opacity: clamp(0, 3 - var(--toast-index, 0), 1);
|
|
235
|
+
}
|
|
236
|
+
[data-slot="toast-viewport"]
|
|
237
|
+
> [data-slot="toast-item"][data-mounted="true"][data-expanded="true"] {
|
|
238
|
+
opacity: 1;
|
|
239
|
+
}
|
|
240
|
+
[data-slot="toast-viewport"] > [data-slot="toast-item"][data-swiping="true"] {
|
|
241
|
+
translate: var(--toast-swipe-amount-x, 0px) var(--toast-y);
|
|
242
|
+
transition: none;
|
|
243
|
+
}
|
|
244
|
+
/* Out: back to the right, fading (or off where it was swiped). */
|
|
245
|
+
[data-slot="toast-viewport"]
|
|
246
|
+
> [data-slot="toast-item"][data-mounted][data-removed="true"],
|
|
247
|
+
[data-slot="toast-viewport"]
|
|
248
|
+
> [data-slot="toast-item"][data-mounted][data-visible="false"] {
|
|
249
|
+
translate: var(--toaster-slide) var(--toast-y);
|
|
250
|
+
opacity: 0;
|
|
251
|
+
}
|
|
252
|
+
[data-slot="toast-viewport"]
|
|
253
|
+
> [data-slot="toast-item"][data-mounted][data-swipe-out="true"] {
|
|
254
|
+
translate: var(--toast-swipe-end-x, 0px) var(--toast-y);
|
|
255
|
+
}
|
|
256
|
+
/* Stacked: the ones behind are blank. */
|
|
257
|
+
[data-slot="toast-viewport"]
|
|
258
|
+
> [data-slot="toast-item"]
|
|
259
|
+
:is([data-slot="toast-body"], [data-slot="toast-close"]) {
|
|
260
|
+
transition: opacity 300ms ease-out;
|
|
261
|
+
}
|
|
262
|
+
[data-slot="toast-viewport"]
|
|
263
|
+
> [data-slot="toast-item"][data-expanded="false"][data-front="false"]
|
|
264
|
+
:is([data-slot="toast-body"], [data-slot="toast-close"]) {
|
|
265
|
+
opacity: 0;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
@media (prefers-reduced-motion: reduce) {
|
|
269
|
+
[data-slot="toast-viewport"] {
|
|
270
|
+
--toaster-slide: 0px;
|
|
271
|
+
transition: none;
|
|
272
|
+
}
|
|
273
|
+
[data-slot="toast-viewport"] > [data-slot="toast-item"] {
|
|
274
|
+
transition: opacity 150ms linear;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
</style>
|
|
279
|
+
|
|
280
|
+
<script>
|
|
281
|
+
import { createToast } from "@data-slot/toast";
|
|
282
|
+
|
|
283
|
+
/** D164 — the attention effect starts once the slide in (300ms) is done. */
|
|
284
|
+
const IN_MS = 300;
|
|
285
|
+
/** D175 — three 700ms ripples (or seven 75ms swings each way). */
|
|
286
|
+
const ATTENTION_MS = 2400;
|
|
287
|
+
const reducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)");
|
|
288
|
+
|
|
289
|
+
document.querySelectorAll<HTMLElement>('[data-slot="toast"]').forEach((root) => {
|
|
290
|
+
if (root.dataset.toasterReady === "true") return;
|
|
291
|
+
const viewport = root.querySelector<HTMLElement>('[data-slot="toast-viewport"]');
|
|
292
|
+
if (!viewport) return;
|
|
293
|
+
|
|
294
|
+
const item = (id: string) =>
|
|
295
|
+
viewport.querySelector<HTMLElement>(
|
|
296
|
+
`[data-slot="toast-item"][data-id="${CSS.escape(id)}"]`,
|
|
297
|
+
);
|
|
298
|
+
|
|
299
|
+
const controller = createToast(root, {
|
|
300
|
+
onShow: (id) => {
|
|
301
|
+
// Only the newest draws the eye.
|
|
302
|
+
viewport
|
|
303
|
+
.querySelectorAll<HTMLElement>('[data-slot="toast-item"][data-attention]')
|
|
304
|
+
.forEach((el) => el.removeAttribute("data-attention"));
|
|
305
|
+
if (reducedMotion.matches) return;
|
|
306
|
+
window.setTimeout(() => {
|
|
307
|
+
const el = item(id);
|
|
308
|
+
if (!el || el.dataset.state !== "open" || el.dataset.front !== "true") return;
|
|
309
|
+
el.dataset.attention = "on";
|
|
310
|
+
window.setTimeout(() => el.removeAttribute("data-attention"), ATTENTION_MS);
|
|
311
|
+
}, IN_MS);
|
|
312
|
+
},
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
viewport
|
|
316
|
+
.querySelector<HTMLElement>("[data-toaster-clear]")
|
|
317
|
+
?.addEventListener("click", () => controller.dismissAll());
|
|
318
|
+
|
|
319
|
+
root.dataset.toasterReady = "true";
|
|
320
|
+
root.dispatchEvent(new CustomEvent("toaster:ready"));
|
|
321
|
+
});
|
|
322
|
+
</script>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { default as Toast } from "./Toast.astro";
|
|
2
|
+
export { default as Toaster } from "./Toaster.astro";
|
|
3
|
+
export { toast } from "../../lib/toast";
|
|
4
|
+
export type {
|
|
5
|
+
ToastKind,
|
|
6
|
+
ToastShowOptions,
|
|
7
|
+
ToastUpdateOptions,
|
|
8
|
+
ToastActionOptions,
|
|
9
|
+
} from "../../lib/toast";
|
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to the `Toggle` component will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## Date 15th Oct 2026
|
|
6
|
+
## Workspace selection handover (C12)
|
|
7
|
+
|
|
8
|
+
- Added `variant="ghost"`: no pressed background (the content shows the state, e.g. a filled `favorite` star); the normal hover background on hover, pressed or not. `default` and `outline` are unchanged, since `ToggleGroup` shows its selection with the pressed background.
|
|
9
|
+
- Storybook: "Ghost" preview with the favourite star.
|
|
10
|
+
|
|
11
|
+
|
|
5
12
|
## 2026-09-18 — Figma alignment (ClickLearn Lightning Design Library, node `5455-19034`)
|
|
6
13
|
|
|
7
14
|
Figma documents Toggle across four pages: Contents (Default, Clean, Tail Icon, Complex), States (Default, Hover, Pressed, Focus Default, Focus Pressed, Disabled), and Sizes (Small 28px, Medium 32px, Large 36px). Sizing/variant fixes live in the shared `toggleVariants` (`packages/ui/src/lib/toggle-shared.ts`), used by both `Toggle` and `ToggleGroup`.
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helpers for the `CommandSearch` component
|
|
3
|
+
* (`@cl-light-ds/ui/components/command-search`).
|
|
4
|
+
*
|
|
5
|
+
* ```ts
|
|
6
|
+
* import { setCommandSearchResults } from "@cl-light-ds/ui/lib/command-search";
|
|
7
|
+
*
|
|
8
|
+
* // mode="remote": search on your side, then hand the results back.
|
|
9
|
+
* palette.addEventListener("command-search:search", async (event) => {
|
|
10
|
+
* const { query } = event.detail;
|
|
11
|
+
* setCommandSearchResults(palette, await api.search(query), { query });
|
|
12
|
+
* });
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
export interface CommandSearchResultItem {
|
|
17
|
+
/** Unique value, sent with `command-search:select`. */
|
|
18
|
+
value: string;
|
|
19
|
+
/** The row's text. */
|
|
20
|
+
label: string;
|
|
21
|
+
/** Muted text on the right, e.g. the workspace it is in. */
|
|
22
|
+
description?: string;
|
|
23
|
+
/** Navigate here when chosen. */
|
|
24
|
+
href?: string;
|
|
25
|
+
disabled?: boolean;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface CommandSearchResultGroup {
|
|
29
|
+
heading: string;
|
|
30
|
+
items: CommandSearchResultItem[];
|
|
31
|
+
/** Total matches, when more exist than `items` holds. Defaults to `items.length`. */
|
|
32
|
+
count?: number;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
type Target = string | HTMLElement | undefined;
|
|
36
|
+
|
|
37
|
+
const findRoot = (target: Target): HTMLElement | null => {
|
|
38
|
+
if (typeof document === "undefined") return null;
|
|
39
|
+
if (target instanceof HTMLElement) return target;
|
|
40
|
+
if (typeof target === "string") return document.getElementById(target);
|
|
41
|
+
return document.querySelector<HTMLElement>("[data-command-search]");
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
/** Open the palette (empty, focused). Defaults to the first on the page. */
|
|
45
|
+
export function openCommandSearch(target?: Target) {
|
|
46
|
+
findRoot(target)?.dispatchEvent(
|
|
47
|
+
new CustomEvent("dialog:set", { detail: { open: true } }),
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Close the palette. */
|
|
52
|
+
export function closeCommandSearch(target?: Target) {
|
|
53
|
+
findRoot(target)?.dispatchEvent(
|
|
54
|
+
new CustomEvent("dialog:set", { detail: { open: false } }),
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* End a search in `mode="remote"`: render these groups as the results (or,
|
|
60
|
+
* with no groups, keep the rows you have put in the list yourself). `query`
|
|
61
|
+
* guards against stale answers: results for an older query are ignored.
|
|
62
|
+
*/
|
|
63
|
+
export function setCommandSearchResults(
|
|
64
|
+
target: Target,
|
|
65
|
+
groups?: CommandSearchResultGroup[],
|
|
66
|
+
options: { query?: string } = {},
|
|
67
|
+
) {
|
|
68
|
+
findRoot(target)?.dispatchEvent(
|
|
69
|
+
new CustomEvent("command-search:results", {
|
|
70
|
+
detail: { groups, query: options.query },
|
|
71
|
+
}),
|
|
72
|
+
);
|
|
73
|
+
}
|