@cl-light-ds/ui 0.5.0 → 0.6.1
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 +1 -1
- package/src/components/accordion/AccordionTrigger.astro +5 -1
- package/src/components/accordion/CHANGELOG.md +9 -0
- package/src/components/alert/Alert.astro +35 -2
- package/src/components/alert/CHANGELOG.md +10 -0
- package/src/components/collapsible/CHANGELOG.md +9 -0
- package/src/components/collapsible/CollapsibleTrigger.astro +5 -1
- package/src/components/combobox/CHANGELOG.md +13 -0
- package/src/components/combobox/Combobox.astro +18 -1
- package/src/components/combobox/ComboboxTrigger.astro +1 -1
- package/src/components/command-search/CHANGELOG.md +36 -0
- package/src/components/command-search/CommandSearch.astro +84 -5
- package/src/components/command-search/CommandSearchGroup.astro +14 -3
- package/src/components/dialog/CHANGELOG.md +37 -0
- package/src/components/dialog/Dialog.astro +14 -3
- package/src/components/dialog/DialogClose.astro +4 -2
- package/src/components/dialog/DialogContent.astro +19 -1
- package/src/components/dialog/DialogTrigger.astro +5 -1
- package/src/components/dropdown-menu/CHANGELOG.md +25 -0
- package/src/components/dropdown-menu/DropdownMenu.astro +13 -3
- package/src/components/dropdown-menu/DropdownMenuRadioGroup.astro +17 -2
- package/src/components/dropdown-menu/DropdownMenuTrigger.astro +7 -1
- package/src/components/field/CHANGELOG.md +18 -0
- package/src/components/field/Field.astro +18 -0
- package/src/components/field/FieldError.astro +13 -1
- package/src/components/field/FieldGroup.astro +12 -3
- package/src/components/hover-card/CHANGELOG.md +9 -0
- package/src/components/hover-card/HoverCardTrigger.astro +5 -1
- package/src/components/notifications-menu/CHANGELOG.md +7 -0
- package/src/components/notifications-menu/NotificationsMenu.astro +1 -0
- package/src/components/notifications-menu/NotificationsMenuItem.astro +1 -0
- package/src/components/option-card/CHANGELOG.md +10 -0
- package/src/components/option-card/OptionCard.astro +9 -1
- package/src/components/organisms/top-bar/CHANGELOG.md +12 -0
- package/src/components/organisms/top-bar/TopBar.astro +17 -13
- package/src/components/organisms/workspace/CHANGELOG.md +7 -0
- package/src/components/popover/CHANGELOG.md +14 -0
- package/src/components/popover/PopoverClose.astro +2 -1
- package/src/components/popover/PopoverTrigger.astro +7 -1
- package/src/components/select/CHANGELOG.md +8 -0
- package/src/components/select/SelectTrigger.astro +1 -1
- package/src/components/table/CHANGELOG.md +6 -0
- package/src/components/table/TableEditableCell.astro +2 -2
- package/src/components/table/TablePagination.astro +4 -4
- package/src/components/tabs/CHANGELOG.md +16 -0
- package/src/components/tabs/Tabs.astro +26 -0
- package/src/components/tabs/TabsList.astro +30 -3
- package/src/components/toggle-group/CHANGELOG.md +11 -0
- package/src/components/toggle-group/ToggleGroup.astro +19 -2
- package/src/components/toggle-group/ToggleGroupItem.astro +10 -0
- package/src/components/tooltip/CHANGELOG.md +26 -0
- package/src/components/tooltip/Tooltip.astro +31 -3
- package/src/components/tooltip/TooltipTrigger.astro +5 -1
- package/src/lib/command-search.ts +5 -0
- package/src/lib/dialog-focus.ts +73 -0
- package/src/lib/dropdown-menu-radio-groups.ts +79 -0
- package/src/lib/scroll-lock.ts +97 -0
- package/src/lib/toggle-shared.ts +6 -1
- package/src/styles/CHANGELOG.md +13 -0
- package/src/styles/globals.css +22 -0
|
@@ -3,6 +3,31 @@
|
|
|
3
3
|
All notable changes to the `DropdownMenu` component will be documented in this file.
|
|
4
4
|
|
|
5
5
|
## Date 15th Oct 2026
|
|
6
|
+
## Workspace selection handover (C10, gap G16)
|
|
7
|
+
|
|
8
|
+
### Several radio groups in one menu
|
|
9
|
+
|
|
10
|
+
- `DropdownMenuRadioGroup` takes `name` (and `defaultValue`). A named group has its own value, so one menu can hold "Sort by" and "Order". The `@data-slot/dropdown-menu` runtime holds one radio value per menu, so a named group's items run on its checkbox values (`lib/dropdown-menu-radio-groups.ts`): exactly one stays checked, picking the checked one again doesn't untick it, and they're still announced as `menuitemradio`. The group mirrors its value in `data-value` and the root emits `dropdown-menu:group-value-change` (`{ name, value, previousValue }`). Item values must be unique within the menu. Unnamed groups behave as before. A workaround until the runtime supports radio groups (bejamas/data-slot).
|
|
11
|
+
- Storybook: "Two radio groups in one menu" preview.
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
## Date 15th Oct 2026
|
|
15
|
+
## Workspace selection handover (C8, gap G5)
|
|
16
|
+
|
|
17
|
+
### Scroll lock
|
|
18
|
+
|
|
19
|
+
- `lockScroll` uses `lib/scroll-lock.ts` instead of the `@data-slot/dropdown-menu` runtime's lock, which added a scrollbar gutter and made short pages jump sideways when a menu opened. Nothing about the layout changes now. A workaround for a runtime bug (bejamas/data-slot).
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
## Date 15th Oct 2026
|
|
23
|
+
## Handover gaps: triggers don't submit forms (C29, gap G18)
|
|
24
|
+
|
|
25
|
+
### Fixed
|
|
26
|
+
|
|
27
|
+
- **`DropdownMenuTrigger` renders `type="button"`** unless you pass a `type`. It renders a `Button`, which has no type, so inside a `<form>` opening the menu submitted the form. Plain `Button` is unchanged; this also covers `TableColumnMenu`'s trigger.
|
|
28
|
+
- **`asChild`:** the wrapper is a `<div>`, not a button, so it can't set a type. Inside a `<form>`, give your child `type="button"` (now in the prop's JSDoc).
|
|
29
|
+
|
|
30
|
+
|
|
6
31
|
## Workspace selection handover (C17, gap G7)
|
|
7
32
|
|
|
8
33
|
### States
|
|
@@ -220,7 +220,17 @@ const defaultValuesJson =
|
|
|
220
220
|
|
|
221
221
|
<script>
|
|
222
222
|
import { createDropdownMenu } from "@data-slot/dropdown-menu";
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
223
|
+
import { lockScrollWhileOpen } from "@cl-light-ds/ui/lib/scroll-lock";
|
|
224
|
+
import { bindRadioGroups, prepareRadioGroups } from "@cl-light-ds/ui/lib/dropdown-menu-radio-groups";
|
|
225
|
+
|
|
226
|
+
document.querySelectorAll<HTMLElement>('[data-slot="dropdown-menu"]').forEach((el) => {
|
|
227
|
+
prepareRadioGroups(el);
|
|
228
|
+
// The runtime's own lock adds a scrollbar gutter, so short pages jump
|
|
229
|
+
// sideways when a menu opens; lib/scroll-lock doesn't touch the layout.
|
|
230
|
+
const menu = createDropdownMenu(el, { lockScroll: false });
|
|
231
|
+
bindRadioGroups(el, menu);
|
|
232
|
+
if (el.dataset.lockScroll !== "false") {
|
|
233
|
+
lockScrollWhileOpen(el, "dropdown-menu:open-change", () => menu.isOpen);
|
|
234
|
+
}
|
|
235
|
+
});
|
|
226
236
|
</script>
|
|
@@ -3,11 +3,26 @@ import type { HTMLAttributes } from "astro/types";
|
|
|
3
3
|
|
|
4
4
|
interface Props extends HTMLAttributes<"div"> {
|
|
5
5
|
class?: string;
|
|
6
|
+
/**
|
|
7
|
+
* Gives the group its own value, so one menu can hold several radio groups
|
|
8
|
+
* (e.g. "Sort by" and "Order"). Without a name, the group's items share the
|
|
9
|
+
* menu's single radio value, as before. Item values must be unique in the menu.
|
|
10
|
+
*/
|
|
11
|
+
name?: string;
|
|
12
|
+
/** The checked item of a named group. */
|
|
13
|
+
defaultValue?: string;
|
|
6
14
|
}
|
|
7
15
|
|
|
8
|
-
const { class: className = "", ...rest } = Astro.props as Props;
|
|
16
|
+
const { class: className = "", name, defaultValue, ...rest } = Astro.props as Props;
|
|
9
17
|
---
|
|
10
18
|
|
|
11
|
-
<div
|
|
19
|
+
<div
|
|
20
|
+
{...rest}
|
|
21
|
+
role="group"
|
|
22
|
+
data-slot="dropdown-menu-radio-group"
|
|
23
|
+
data-name={name}
|
|
24
|
+
data-default-value={name ? defaultValue : undefined}
|
|
25
|
+
class={className}
|
|
26
|
+
>
|
|
12
27
|
<slot />
|
|
13
28
|
</div>
|
|
@@ -6,7 +6,11 @@ import type { Props as ButtonProps } from "../button/Button.astro";
|
|
|
6
6
|
|
|
7
7
|
type Props = ButtonProps & {
|
|
8
8
|
class?: string;
|
|
9
|
-
/**
|
|
9
|
+
/**
|
|
10
|
+
* Radix-like: render wrapper so the child can be the interactive element.
|
|
11
|
+
* The wrapper is not a button, so the child sets its own type: give a
|
|
12
|
+
* `<button>`/`<Button>` child `type="button"` inside a `<form>` or it submits it.
|
|
13
|
+
*/
|
|
10
14
|
asChild?: boolean;
|
|
11
15
|
};
|
|
12
16
|
|
|
@@ -16,6 +20,7 @@ const {
|
|
|
16
20
|
class: className = "",
|
|
17
21
|
asChild = false,
|
|
18
22
|
variant = "outline",
|
|
23
|
+
type = "button",
|
|
19
24
|
tailIcon = true,
|
|
20
25
|
...rest
|
|
21
26
|
} = Astro.props as Props;
|
|
@@ -33,6 +38,7 @@ const {
|
|
|
33
38
|
variant={variant}
|
|
34
39
|
tailIcon={tailIcon}
|
|
35
40
|
{...rest as any}
|
|
41
|
+
type={(rest as any).as === "a" ? undefined : type}
|
|
36
42
|
>
|
|
37
43
|
<slot />
|
|
38
44
|
{tailIcon &&
|
|
@@ -2,6 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to the `Field` component will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## Date 15th Oct 2026
|
|
6
|
+
## Workspace selection handover (C21)
|
|
7
|
+
|
|
8
|
+
### Added
|
|
9
|
+
|
|
10
|
+
- `leading` prop on `FieldError` (`"default" | "compact"`, default `"default"`), sets `data-leading`. `"compact"` sets a 16px line height (`leading-4`) instead of `text-sm`'s 20px, so a one-line message under a control leaves as much space below it as the gap between fields (D147: 26px against 27px, instead of 28px against 25px). Replaces the workspace-selection screen's `class="leading-4"`.
|
|
11
|
+
- Opt-in, not a default fix: the 20px line is `text-sm`'s own and matches Figma's `text/md` style (14/20) used across the form text, so it isn't a bug; the tighter line is for evening out the space in a compact form.
|
|
12
|
+
- Storybook: a "Compact error line height" preview.
|
|
13
|
+
|
|
14
|
+
## Date 15th Oct 2026
|
|
15
|
+
## Workspace selection handover (C20)
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
|
|
19
|
+
- `spacing` prop on `FieldGroup` (`"default" | "compact"`, default `"default"`), sets `data-spacing`. `"compact"` puts 20px between the fields (`gap-5`) instead of 28px (`gap-7`), for a form that sits tighter, e.g. the New workspace dialog (D140). Nested field groups (`gap-4`) and checkbox groups (`gap-3`) keep their own gaps. Replaces the workspace-selection screen's `class="gap-5"`.
|
|
20
|
+
- Opt-in: field groups without `spacing` keep 28px.
|
|
21
|
+
- Storybook: a "Compact field group" preview.
|
|
22
|
+
|
|
5
23
|
## 2026-09-09 — Fixed control-to-label vertical alignment for native checkbox/radio
|
|
6
24
|
|
|
7
25
|
### Fixed
|
|
@@ -185,13 +185,22 @@
|
|
|
185
185
|
*
|
|
186
186
|
* | Prop | Type | Default |
|
|
187
187
|
* |------|------|---------|
|
|
188
|
+
* | `spacing` | `"default" \| "compact"` | `"default"` |
|
|
188
189
|
* | `class` | `string` | - |
|
|
189
190
|
*
|
|
191
|
+
* `spacing="compact"` puts 20px between the fields instead of 28px, for a
|
|
192
|
+
* form that sits tighter (e.g. in a dialog).
|
|
193
|
+
*
|
|
190
194
|
* ```astro nocollapse
|
|
191
195
|
* <FieldGroup class="@container/field-group flex flex-col gap-6">
|
|
192
196
|
* <Field>...</Field>
|
|
193
197
|
* <Field>...</Field>
|
|
194
198
|
* </FieldGroup>
|
|
199
|
+
*
|
|
200
|
+
* <FieldGroup spacing="compact">
|
|
201
|
+
* <Field>...</Field>
|
|
202
|
+
* <Field>...</Field>
|
|
203
|
+
* </FieldGroup>
|
|
195
204
|
* ```
|
|
196
205
|
*
|
|
197
206
|
* ### Field
|
|
@@ -292,12 +301,21 @@
|
|
|
292
301
|
* |------|------|---------|
|
|
293
302
|
* | `errors` | `Array<{ message?: string } \| undefined>` | `[]` |
|
|
294
303
|
* | `forceMount` | `boolean` | `false` |
|
|
304
|
+
* | `leading` | `"default" \| "compact"` | `"default"` |
|
|
295
305
|
* | `class` | `string` | - |
|
|
296
306
|
*
|
|
297
307
|
* ```astro nocollapse
|
|
298
308
|
* <FieldError errors={[{ message: "Username is required" }]} />
|
|
299
309
|
* ```
|
|
300
310
|
*
|
|
311
|
+
* `leading="compact"` sets a 16px line height (20px by default), so a one-line
|
|
312
|
+
* message under a control leaves as much space below it as the gap between
|
|
313
|
+
* fields:
|
|
314
|
+
*
|
|
315
|
+
* ```astro nocollapse
|
|
316
|
+
* <FieldError leading="compact">That address is taken.</FieldError>
|
|
317
|
+
* ```
|
|
318
|
+
*
|
|
301
319
|
* When `errors` contains multiple unique messages, the component renders a list
|
|
302
320
|
* automatically.
|
|
303
321
|
*
|
|
@@ -7,12 +7,19 @@ type ErrorItem = { message?: string } | undefined;
|
|
|
7
7
|
interface Props extends HTMLAttributes<"div"> {
|
|
8
8
|
errors?: ErrorItem[];
|
|
9
9
|
forceMount?: boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Line height: `"default"` 20px (text-sm's own), `"compact"` 16px
|
|
12
|
+
* (`leading-4`), so a one-line message under a control leaves as much space
|
|
13
|
+
* below it as the gap between fields (C21, D147).
|
|
14
|
+
*/
|
|
15
|
+
leading?: "default" | "compact";
|
|
10
16
|
}
|
|
11
17
|
|
|
12
18
|
const {
|
|
13
19
|
class: className = "",
|
|
14
20
|
errors = [],
|
|
15
21
|
forceMount = false,
|
|
22
|
+
leading = "default",
|
|
16
23
|
...rest
|
|
17
24
|
} = Astro.props as Props;
|
|
18
25
|
|
|
@@ -34,7 +41,12 @@ const shouldRender = forceMount || hasContent || uniqueMessages.length > 0;
|
|
|
34
41
|
<div
|
|
35
42
|
role="alert"
|
|
36
43
|
data-slot="field-error"
|
|
37
|
-
|
|
44
|
+
data-leading={leading}
|
|
45
|
+
class={cn(
|
|
46
|
+
"text-destructive text-sm font-normal",
|
|
47
|
+
leading === "compact" && "leading-4",
|
|
48
|
+
className,
|
|
49
|
+
)}
|
|
38
50
|
{...rest}
|
|
39
51
|
>
|
|
40
52
|
{hasContent && <slot />}
|
|
@@ -2,15 +2,24 @@
|
|
|
2
2
|
import type { HTMLAttributes } from "astro/types";
|
|
3
3
|
import { cn } from "@cl-light-ds/ui/lib/utils";
|
|
4
4
|
|
|
5
|
-
interface Props extends HTMLAttributes<"div"> {
|
|
5
|
+
interface Props extends HTMLAttributes<"div"> {
|
|
6
|
+
/**
|
|
7
|
+
* Space between the fields: `"default"` 28px (`gap-7`), `"compact"` 20px
|
|
8
|
+
* (`gap-5`), for a form that sits tighter, e.g. in a dialog (C20, D140).
|
|
9
|
+
* Nested field groups and checkbox groups keep their own gaps.
|
|
10
|
+
*/
|
|
11
|
+
spacing?: "default" | "compact";
|
|
12
|
+
}
|
|
6
13
|
|
|
7
|
-
const { class: className = "", ...rest } = Astro.props as Props;
|
|
14
|
+
const { spacing = "default", class: className = "", ...rest } = Astro.props as Props;
|
|
8
15
|
---
|
|
9
16
|
|
|
10
17
|
<div
|
|
11
18
|
data-slot="field-group"
|
|
19
|
+
data-spacing={spacing}
|
|
12
20
|
class={cn(
|
|
13
|
-
"gap-
|
|
21
|
+
spacing === "compact" ? "gap-5" : "gap-7",
|
|
22
|
+
"data-[slot=checkbox-group]:gap-3 *:data-[slot=field-group]:gap-4",
|
|
14
23
|
"group/field-group @container/field-group flex w-full flex-col",
|
|
15
24
|
className,
|
|
16
25
|
)}
|
|
@@ -2,6 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to the `HoverCard` component family (`HoverCard`, `HoverCardTrigger`, `HoverCardContent`) are documented in this file.
|
|
4
4
|
|
|
5
|
+
## Date 15th Oct 2026
|
|
6
|
+
## Handover gaps: triggers don't submit forms (C29, gap G18)
|
|
7
|
+
|
|
8
|
+
### Docs
|
|
9
|
+
|
|
10
|
+
- `HoverCardTrigger` already renders `type="button"` unless you pass a `type`; no change.
|
|
11
|
+
- **`asChild`:** the wrapper is a `<div>`, not a button, so it can't set a type. Inside a `<form>`, give your child `type="button"` (now in the prop's JSDoc).
|
|
12
|
+
|
|
13
|
+
|
|
5
14
|
## 2026-09-09 — Figma alignment (ClickLearn Lightning Design Library, node `5455-14770`)
|
|
6
15
|
|
|
7
16
|
Figma shows three content **Variants** (Default, Simple, Avatar) and two **States** (Default, Focus) for the card surface.
|
|
@@ -4,7 +4,11 @@ import type { HTMLAttributes } from "astro/types";
|
|
|
4
4
|
|
|
5
5
|
interface Props extends HTMLAttributes<"button"> {
|
|
6
6
|
class?: string;
|
|
7
|
-
/**
|
|
7
|
+
/**
|
|
8
|
+
* Radix-like: render wrapper so the child can be the interactive element.
|
|
9
|
+
* The wrapper is not a button, so the child sets its own type: give a
|
|
10
|
+
* `<button>`/`<Button>` child `type="button"` inside a `<form>` or it submits it.
|
|
11
|
+
*/
|
|
8
12
|
asChild?: boolean;
|
|
9
13
|
}
|
|
10
14
|
|
|
@@ -3,6 +3,13 @@
|
|
|
3
3
|
All notable changes to the `NotificationsMenu` component family (`NotificationsMenu`, `NotificationsMenuItem`) are documented in this file.
|
|
4
4
|
|
|
5
5
|
## Date 15th Oct 2026
|
|
6
|
+
## Handover gaps: triggers don't submit forms (C29, gap G18)
|
|
7
|
+
|
|
8
|
+
### Fixed
|
|
9
|
+
|
|
10
|
+
- **The bell, Clear all and each ✕ have `type="button"`.** The bell is a `PopoverTrigger` (fixed there); Clear all and ✕ are `Button`s, which have no type, so inside a `<form>` they submitted it.
|
|
11
|
+
|
|
12
|
+
|
|
6
13
|
## Workspace selection handover (N10)
|
|
7
14
|
|
|
8
15
|
### Added
|
|
@@ -165,6 +165,7 @@ const unseen = notifications.filter((n) => n.new).length;
|
|
|
165
165
|
<PopoverHeader class="flex-row items-center justify-between gap-2 p-3">
|
|
166
166
|
<PopoverTitle>{title}</PopoverTitle>
|
|
167
167
|
<Button
|
|
168
|
+
type="button"
|
|
168
169
|
variant="link"
|
|
169
170
|
size="sm"
|
|
170
171
|
class="group-data-[empty=true]/notifications:hidden"
|
|
@@ -118,6 +118,7 @@ const iconChipVariants = cva(
|
|
|
118
118
|
></span>
|
|
119
119
|
<span class="sr-only group-data-[new=false]/note:hidden">{newLabel}</span>
|
|
120
120
|
<Button
|
|
121
|
+
type="button"
|
|
121
122
|
variant="ghost"
|
|
122
123
|
size="icon-xs"
|
|
123
124
|
class="absolute top-2 right-2 opacity-0 group-focus-within/note:opacity-100 group-hover/note:opacity-100 pointer-coarse:opacity-100"
|
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to the `OptionCard` component will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## Date 15th Oct 2026
|
|
6
|
+
## Workspace selection handover (C19)
|
|
7
|
+
|
|
8
|
+
### Fixed
|
|
9
|
+
|
|
10
|
+
- Accent badges inside an `OptionCard` (the `badge` slot and the nested content) now get a `chart-1` border from the card itself (`[&_[data-slot=badge][data-variant=accent]]:border-chart-1`). The accent badge is bordered in its own fill, the same pale blue as a chosen card's tint, so "Recommended" and "Linked" disappeared on a chosen card (D109). Consumers no longer add `class="border-chart-1"`; it still works if left in.
|
|
11
|
+
- Not opt-in: it only touches accent badges inside an option card, and only their border colour, to keep them visible. Badges elsewhere, and other badge variants, are unchanged.
|
|
12
|
+
- Always on, not only while the card is chosen: the reference gives these badges the darker edge on every card, and the badge doesn't change as the choice moves between cards.
|
|
13
|
+
- Storybook: the badges in the previews no longer carry the class.
|
|
14
|
+
|
|
5
15
|
## Date 15th Oct 2026
|
|
6
16
|
## Workspace selection handover (N18)
|
|
7
17
|
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*
|
|
9
9
|
* <RadioGroup name="mode" defaultValue="copy" aria-label="How content is brought across" class="gap-3">
|
|
10
10
|
* <OptionCard value="copy" title="Make independent copies" description="The new workspace gets its own copies.">
|
|
11
|
-
* <Badge slot="badge" variant="accent" size="sm"
|
|
11
|
+
* <Badge slot="badge" variant="accent" size="sm">Recommended</Badge>
|
|
12
12
|
* </OptionCard>
|
|
13
13
|
* <OptionCard value="link" title="Link to the original" description="Any change made in either workspace appears in both." />
|
|
14
14
|
* </RadioGroup>
|
|
@@ -38,6 +38,10 @@
|
|
|
38
38
|
* - `content="panel"`: a white bordered panel inset in the card (a checklist).
|
|
39
39
|
* - `content="plain"`: no styling, only the show/hide.
|
|
40
40
|
*
|
|
41
|
+
* Accent badges anywhere in the card (the `badge` slot, nested notes) get a
|
|
42
|
+
* `chart-1` edge, so they don't disappear on the chosen card's accent tint
|
|
43
|
+
* (C19, D109). No class needed on the badge.
|
|
44
|
+
*
|
|
41
45
|
* Listen for `radio-group:change` on the `RadioGroup` for the value.
|
|
42
46
|
*/
|
|
43
47
|
|
|
@@ -103,6 +107,10 @@ const optionCardContentVariants = cva(
|
|
|
103
107
|
"group/option-card flex flex-col rounded-lg border bg-card transition-colors",
|
|
104
108
|
"has-[>[data-slot=option-card-label]>[data-checked]]:border-primary has-[>[data-slot=option-card-label]>[data-checked]]:bg-badge-accent/50",
|
|
105
109
|
"has-[>[data-slot=option-card-label]>[data-disabled]]:opacity-50",
|
|
110
|
+
// C19, D109 — an accent badge is bordered in its own fill, the same pale
|
|
111
|
+
// blue as the chosen tint; a chart-1 edge keeps it visible. Always on (not
|
|
112
|
+
// only while chosen), so the badge doesn't change as the choice moves.
|
|
113
|
+
"[&_[data-slot=badge][data-variant=accent]]:border-chart-1",
|
|
106
114
|
className,
|
|
107
115
|
)}
|
|
108
116
|
>
|
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to the top bar organism (`organisms/top-bar`) will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## Date 15th Oct 2026
|
|
6
|
+
## No scrollbar flash when a toast slides in (C31, gap G20)
|
|
7
|
+
|
|
8
|
+
- The bar's surface (`will-change: transform`) makes its toast stack part of the page, and a new toast slides in from 2rem while the stack sits 1–1.5rem from the edge, so the page widened for 300ms and a horizontal scrollbar flashed. The surface now clips sideways overflow (`overflow-x: clip`); the slide-in, ripple and shake are unchanged, and toasts and menus still hang below the bar. Screens can drop their own `overflow-x-clip` page wrapper.
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
## Date 15th Oct 2026
|
|
12
|
+
## Normal breadcrumb (C1 reverted)
|
|
13
|
+
|
|
14
|
+
- The bar uses the generic `Breadcrumb` as it is (36px crumbs, the normal spacing) instead of the compact 32px crumbs. To stay 41px with its border, the bar's vertical padding is 2px (was 4px); the 32px controls stay centred.
|
|
15
|
+
|
|
16
|
+
|
|
5
17
|
## Date 15th Oct 2026
|
|
6
18
|
## Workspace selection handover (N12, C1)
|
|
7
19
|
|
|
@@ -122,27 +122,31 @@ const hasBreadcrumbSlot = Astro.slots.has("breadcrumb");
|
|
|
122
122
|
const hasActionsSlot = Astro.slots.has("actions");
|
|
123
123
|
|
|
124
124
|
/*
|
|
125
|
-
* D23 — 41px:
|
|
125
|
+
* D23 — 41px: the 36px breadcrumb crumbs with 2px above and below, plus the 1px border
|
|
126
|
+
* (the 32px controls are centred).
|
|
126
127
|
* The side padding matches the Toaster's default right inset (1rem, 1.5rem from md),
|
|
127
128
|
* so the stack lines up with the bar's right end.
|
|
128
129
|
*/
|
|
129
130
|
const topBarVariants = cva(
|
|
130
|
-
"bg-background relative flex items-center gap-4 border-b px-4 py-
|
|
131
|
+
"bg-background relative flex items-center gap-4 border-b px-4 py-0.5 md:px-6",
|
|
131
132
|
);
|
|
132
133
|
|
|
133
|
-
/*
|
|
134
|
-
* C1 — the generic Breadcrumb pads crumbs as 36px pills (px-3 py-2). Here they are
|
|
135
|
-
* 32px (py-1.5), like the buttons, so the bar stays 41px, with 6px (px-1.5) either side
|
|
136
|
-
* of the chevron (D23, D97). The first crumb is pulled back by its padding so its text
|
|
137
|
-
* lines up with the bar's edge.
|
|
138
|
-
*/
|
|
139
|
-
const crumbClass =
|
|
140
|
-
"min-w-0 gap-0 [&_a]:px-1.5 [&_a]:py-1.5 [&_[aria-current=page]]:px-1.5 [&_[aria-current=page]]:py-1.5 first:-ms-1.5";
|
|
141
134
|
const lastIndex = crumbs.length - 1;
|
|
142
135
|
const Wrapper = sticky ? StickySurface : "div";
|
|
143
136
|
---
|
|
144
137
|
|
|
145
|
-
|
|
138
|
+
{
|
|
139
|
+
/*
|
|
140
|
+
overflow-x-clip: the surface's transform makes the toast stack part of the page, so a
|
|
141
|
+
toast sliding in from the right (2rem, past the 1–1.5rem inset) widened the page and
|
|
142
|
+
flashed a horizontal scrollbar (C31). Clipping sideways only keeps toasts and menus
|
|
143
|
+
free to hang below the bar.
|
|
144
|
+
*/
|
|
145
|
+
}
|
|
146
|
+
<Wrapper
|
|
147
|
+
data-slot="top-bar-surface"
|
|
148
|
+
class={sticky ? "overflow-x-clip" : "relative z-50 w-full overflow-x-clip will-change-transform"}
|
|
149
|
+
>
|
|
146
150
|
<header {...rest} data-slot="top-bar" class={cn(topBarVariants(), className)}>
|
|
147
151
|
<div data-slot="top-bar-start" class="flex min-w-0 flex-1 items-center">
|
|
148
152
|
{
|
|
@@ -155,7 +159,7 @@ const Wrapper = sticky ? StickySurface : "div";
|
|
|
155
159
|
{crumbs.map((crumb, i) => (
|
|
156
160
|
<>
|
|
157
161
|
{i > 0 && <BreadcrumbSeparator />}
|
|
158
|
-
<BreadcrumbItem class={cn(
|
|
162
|
+
<BreadcrumbItem class={cn(i === lastIndex && "min-w-0")}>
|
|
159
163
|
{i === lastIndex ? (
|
|
160
164
|
<span aria-current="page" class="text-foreground max-w-full min-w-0">
|
|
161
165
|
<span class="min-w-0 truncate">{crumb.label}</span>
|
|
@@ -165,7 +169,7 @@ const Wrapper = sticky ? StickySurface : "div";
|
|
|
165
169
|
{crumb.label}
|
|
166
170
|
</a>
|
|
167
171
|
) : (
|
|
168
|
-
<span class="text-muted-foreground
|
|
172
|
+
<span class="text-muted-foreground whitespace-nowrap">{crumb.label}</span>
|
|
169
173
|
)}
|
|
170
174
|
</BreadcrumbItem>
|
|
171
175
|
</>
|
|
@@ -3,6 +3,13 @@
|
|
|
3
3
|
All notable changes to the workspace organisms (`organisms/workspace`) will be documented in this file.
|
|
4
4
|
|
|
5
5
|
## Date 15th Oct 2026
|
|
6
|
+
## Handover gaps: triggers don't submit forms (C29, gap G18)
|
|
7
|
+
|
|
8
|
+
### Fixed
|
|
9
|
+
|
|
10
|
+
- **`WorkspaceIconPicker`: opening it no longer submits the form it's in.** Its avatar button is a `PopoverTrigger`, which now renders `type="button"`. In the New workspace form, opening the picker created the workspace, and Enter in a field opened the picker; the screen's `type="button"` workaround in `interactions.ts` can go.
|
|
11
|
+
|
|
12
|
+
|
|
6
13
|
## Workspace selection handover (N16)
|
|
7
14
|
|
|
8
15
|
### Added
|
|
@@ -2,6 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to the `Popover` component family (`Popover`, `PopoverTrigger`, `PopoverContent`, `PopoverHeader`, `PopoverTitle`, `PopoverDescription`, `PopoverClose`) are documented in this file.
|
|
4
4
|
|
|
5
|
+
## Date 15th Oct 2026
|
|
6
|
+
## Handover gaps: triggers don't submit forms (C29, gap G18)
|
|
7
|
+
|
|
8
|
+
### Fixed
|
|
9
|
+
|
|
10
|
+
- **`PopoverTrigger` renders `type="button"`** unless you pass a `type`. It renders a `Button`, which has no type, so inside a `<form>` opening the popover submitted the form (the WorkspaceIconPicker in the New workspace form created the workspace). Plain `Button` is unchanged.
|
|
11
|
+
- **`PopoverClose` renders `type="button"`** unless you pass a `type`.
|
|
12
|
+
- **`asChild`:** the wrapper is a `<div>`, not a button, so it can't set a type. Inside a `<form>`, give your child `type="button"` (now in the prop's JSDoc).
|
|
13
|
+
|
|
14
|
+
### Storybook
|
|
15
|
+
|
|
16
|
+
- "Inside a form" preview: a Popover trigger next to a submit button; the line under it counts submits.
|
|
17
|
+
|
|
18
|
+
|
|
5
19
|
## 2026-09-18 — Figma alignment (ClickLearn Lightning Design Library, node `5548-11795`)
|
|
6
20
|
|
|
7
21
|
Figma documents Popover across two pages: Items (the trigger button's Default/Hover/Pressed/Focus/Disabled states) and the expanded popover (12 Side × Alignment combinations, all sharing the same panel styling).
|
|
@@ -6,11 +6,12 @@ interface Props extends HTMLAttributes<"button"> {
|
|
|
6
6
|
class?: string;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
-
const { class: className = "", ...rest } = Astro.props as Props;
|
|
9
|
+
const { class: className = "", type = "button", ...rest } = Astro.props as Props;
|
|
10
10
|
---
|
|
11
11
|
|
|
12
12
|
<button
|
|
13
13
|
{...rest}
|
|
14
|
+
type={type}
|
|
14
15
|
data-slot="popover-close"
|
|
15
16
|
class={cn("", className)}
|
|
16
17
|
>
|
|
@@ -5,7 +5,11 @@ import type { Props as ButtonProps } from "../button/Button.astro";
|
|
|
5
5
|
|
|
6
6
|
type Props = ButtonProps & {
|
|
7
7
|
class?: string;
|
|
8
|
-
/**
|
|
8
|
+
/**
|
|
9
|
+
* Radix-like: render wrapper so the child can be the interactive element.
|
|
10
|
+
* The wrapper is not a button, so the child sets its own type: give a
|
|
11
|
+
* `<button>`/`<Button>` child `type="button"` inside a `<form>` or it submits it.
|
|
12
|
+
*/
|
|
9
13
|
asChild?: boolean;
|
|
10
14
|
};
|
|
11
15
|
|
|
@@ -13,6 +17,7 @@ const {
|
|
|
13
17
|
class: className = "",
|
|
14
18
|
asChild = false,
|
|
15
19
|
variant = "outline",
|
|
20
|
+
type = "button",
|
|
16
21
|
...rest
|
|
17
22
|
} = Astro.props as Props;
|
|
18
23
|
---
|
|
@@ -28,6 +33,7 @@ const {
|
|
|
28
33
|
variant={variant}
|
|
29
34
|
class={cn("", "inline-flex items-center", className)}
|
|
30
35
|
{...rest as any}
|
|
36
|
+
type={(rest as any).as === "a" ? undefined : type}
|
|
31
37
|
>
|
|
32
38
|
<slot />
|
|
33
39
|
</Button>
|
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to the `Select` component will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## Date 15th Oct 2026
|
|
6
|
+
## Handover gaps: triggers don't submit forms (C29, gap G18)
|
|
7
|
+
|
|
8
|
+
### Fixed
|
|
9
|
+
|
|
10
|
+
- **`SelectTrigger`: a `type` you pass now wins.** It always rendered `type="button"`, but after your attributes, so it overrode yours. The default is unchanged.
|
|
11
|
+
|
|
12
|
+
|
|
5
13
|
## Date 14th Sep 2026
|
|
6
14
|
## Figma alignment (node 2329-7946) + props audit
|
|
7
15
|
|
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to the `Table` component family (`Table`, `TableHeader`, `TableBody`, `TableFooter`, `TableRow`, `TableHead`, `TableCell`, `TableCaption`) are documented in this file.
|
|
4
4
|
|
|
5
|
+
## Date 15th Oct 2026
|
|
6
|
+
## Workspace selection handover (C29 follow-up)
|
|
7
|
+
|
|
8
|
+
- `TablePagination`'s page buttons and `TableEditableCell`'s Save/Cancel render `type="button"`, so they don't submit a surrounding `<form>` (same bug as C29, in buttons that aren't triggers).
|
|
9
|
+
|
|
10
|
+
|
|
5
11
|
## Date 15th Oct 2026
|
|
6
12
|
## Workspace selection handover (C13)
|
|
7
13
|
|
|
@@ -177,11 +177,11 @@ const affordanceIcon =
|
|
|
177
177
|
maxlength={maxLength}
|
|
178
178
|
class={cn("min-w-24 flex-1", align === "right" && "text-right")}
|
|
179
179
|
/>
|
|
180
|
-
<Button data-table-action="confirm" variant="ghost" size="icon-sm" aria-label="Save" class="text-primary">
|
|
180
|
+
<Button type="button" data-table-action="confirm" variant="ghost" size="icon-sm" aria-label="Save" class="text-primary">
|
|
181
181
|
<Check class="group-data-[state=saving]/cell:hidden" aria-hidden="true" />
|
|
182
182
|
<Spinner class="hidden group-data-[state=saving]/cell:block" aria-label="Saving" />
|
|
183
183
|
</Button>
|
|
184
|
-
<Button data-table-action="cancel" variant="ghost" size="icon-sm" aria-label="Cancel">
|
|
184
|
+
<Button type="button" data-table-action="cancel" variant="ghost" size="icon-sm" aria-label="Cancel">
|
|
185
185
|
<X aria-hidden="true" />
|
|
186
186
|
</Button>
|
|
187
187
|
</div>
|
|
@@ -48,10 +48,10 @@ const to = Math.min(page * pageSize, totalCount);
|
|
|
48
48
|
</label>
|
|
49
49
|
<span data-slot="table-pagination-status" class="text-foreground">Page {page} of {pageCount}</span>
|
|
50
50
|
<ButtonGroup>
|
|
51
|
-
<Button variant="outline" size="icon-sm" data-table-page="first" aria-label="First page" disabled={page <= 1}><ChevronsLeft /></Button>
|
|
52
|
-
<Button variant="outline" size="icon-sm" data-table-page="prev" aria-label="Previous page" disabled={page <= 1}><ChevronLeft /></Button>
|
|
53
|
-
<Button variant="outline" size="icon-sm" data-table-page="next" aria-label="Next page" disabled={page >= pageCount}><ChevronRight /></Button>
|
|
54
|
-
<Button variant="outline" size="icon-sm" data-table-page="last" aria-label="Last page" disabled={page >= pageCount}><ChevronsRight /></Button>
|
|
51
|
+
<Button type="button" variant="outline" size="icon-sm" data-table-page="first" aria-label="First page" disabled={page <= 1}><ChevronsLeft /></Button>
|
|
52
|
+
<Button type="button" variant="outline" size="icon-sm" data-table-page="prev" aria-label="Previous page" disabled={page <= 1}><ChevronLeft /></Button>
|
|
53
|
+
<Button type="button" variant="outline" size="icon-sm" data-table-page="next" aria-label="Next page" disabled={page >= pageCount}><ChevronRight /></Button>
|
|
54
|
+
<Button type="button" variant="outline" size="icon-sm" data-table-page="last" aria-label="Last page" disabled={page >= pageCount}><ChevronsRight /></Button>
|
|
55
55
|
</ButtonGroup>
|
|
56
56
|
</div>
|
|
57
57
|
</nav>
|
|
@@ -2,4 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to the `Tabs` component will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## Date 15th Oct 2026
|
|
6
|
+
## Indicator on a bordered list
|
|
7
|
+
|
|
8
|
+
- The `@data-slot/tabs` runtime subtracts the list's border width twice, so on a `TabsList` with a real border (e.g. `class="border"`) the indicator sat 1px off. `Tabs` now records each list's border widths (`--tabs-list-border-l`, `--tabs-list-border-t`) and both indicators add them back along the list (left when horizontal, top when vertical; the runtime measures the other axis correctly); with no border they are 0, so nothing else changes. A workaround for a runtime bug (bejamas/data-slot).
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
## Date 15th Oct 2026
|
|
12
|
+
## Workspace selection handover (C11)
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- `bordered` prop on `TabsList` (default `false`): a 1px `border`-token outline round the list, sets `data-bordered`. For a tabs list on a light grey page beside bordered controls (search field, buttons), where the muted fill alone blends in (D104). The list keeps its height.
|
|
17
|
+
- Drawn as an inset ring (`ring-1 ring-inset ring-border`) with 1px more padding, not a CSS `border`: same geometry, but the tabs runtime positions the indicator from `offsetLeft` and then subtracts the list's `clientLeft` (its border width) again, so with a real border (as the screen's `class="border"`) the indicator sat 1px left of its tab. Worth fixing upstream in `@data-slot/tabs`.
|
|
18
|
+
- Opt-in: existing tabs lists look the same. Replaces the workspace-selection screen's `class="border"`.
|
|
19
|
+
- Storybook: a "Bordered" preview.
|
|
20
|
+
|
|
5
21
|
## Unreleased
|