rift-ds 1.16.0 → 1.18.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/bin/templates/blocks/app/dashboard/page.module.css +4 -3
- package/bin/templates/blocks/app/dashboard/page.tsx +1 -1
- package/bin/templates/blocks/components/blocks/AppShell.module.css +50 -0
- package/bin/templates/blocks/components/blocks/AppShell.tsx +26 -7
- package/chat/ChatLauncher.d.ts +11 -1
- package/chat/ChatLauncher.js +7 -1
- package/chat/ChatThreads.d.ts +3 -1
- package/chat/ChatThreads.js +3 -1
- package/chat/ChatWidget.css +21 -0
- package/components/AppLayout/AppLayout.css +90 -2
- package/components/AppLayout/AppLayout.d.ts +10 -2
- package/components/AppLayout/AppLayout.js +20 -2
- package/components/AppSidebar/AppSidebar.css +104 -38
- package/components/AppSidebar/AppSidebar.d.ts +15 -4
- package/components/AppSidebar/AppSidebar.js +8 -2
- package/components/DataTable/DataTable.js +29 -9
- package/components/DataTable/FilterEditor.js +1 -2
- package/components/DataTable/filterModel.js +4 -3
- package/components/DataTable/views.d.ts +9 -0
- package/components/DataTable/views.js +10 -1
- package/components/SegmentedControl/SegmentedControl.css +69 -0
- package/components/SegmentedControl/SegmentedControl.d.ts +4 -3
- package/components/SegmentedControl/SegmentedControl.js +90 -48
- package/components/Stat/Stat.css +57 -0
- package/components/Stat/Stat.d.ts +8 -2
- package/components/Stat/Stat.js +4 -2
- package/components/TableToolbar/TableToolbar.css +6 -0
- package/components/TableToolbar/TableToolbar.js +52 -16
- package/components/ThemeToggle/ThemeToggle.css +2 -72
- package/components/ThemeToggle/ThemeToggle.d.ts +1 -1
- package/components/ThemeToggle/ThemeToggle.js +4 -40
- package/components/registry.json +1 -1
- package/components/registry.json.d.ts +1 -1
- package/components/registry.json.js +1 -1
- package/package.json +1 -1
- package/tokens/registry.json +2 -0
- package/tokens/registry.json.d.ts +2 -0
- package/tokens/registry.json.js +1 -1
- package/tokens/tokens-dark.css +7 -0
- package/tokens/tokens-light.css +7 -0
|
@@ -51,6 +51,22 @@
|
|
|
51
51
|
backdrop-filter: blur(24px);
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
+
/* The inset layout's gap: between the content surface and the viewport
|
|
55
|
+
edges, and around a docked chat panel's inset surface (ChatLauncher's
|
|
56
|
+
surface="inset" falls back to the same value when this sheet is absent). */
|
|
57
|
+
:root {
|
|
58
|
+
--ds-shell-inset: 10px;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/* ============================================
|
|
62
|
+
INSET VARIANT — the rail flush on the app
|
|
63
|
+
shell colour, beside a content surface inset
|
|
64
|
+
from the edges (AppLayout's inset layout)
|
|
65
|
+
============================================ */
|
|
66
|
+
.ds-app-sidebar--inset {
|
|
67
|
+
background-color: var(--color-bg-app-shell);
|
|
68
|
+
}
|
|
69
|
+
|
|
54
70
|
/* ============================================
|
|
55
71
|
TOP SECTION — logo + nav
|
|
56
72
|
============================================ */
|
|
@@ -605,6 +621,15 @@
|
|
|
605
621
|
inset-inline-start: calc(var(--ds-sidebar-float-inset, 20px) + 280px + var(--gap-300));
|
|
606
622
|
}
|
|
607
623
|
|
|
624
|
+
/* Inset: the side panel joins the rail on the shell plane, one hairline
|
|
625
|
+
divider between the two levels, and the inset content surface starts
|
|
626
|
+
beyond both (AppLayout reserves the panel's width while it shows). The
|
|
627
|
+
trailing edge needs no border: the surface's own edge ends the panel. */
|
|
628
|
+
.ds-app-sidebar__panel--inset {
|
|
629
|
+
background-color: var(--color-bg-app-shell);
|
|
630
|
+
border-inline-end-color: transparent;
|
|
631
|
+
}
|
|
632
|
+
|
|
608
633
|
/* ============================================
|
|
609
634
|
PANEL MODE RAIL — labels under icons
|
|
610
635
|
With the second level in the panel, the
|
|
@@ -612,85 +637,126 @@
|
|
|
612
637
|
widens to 80px and names every destination:
|
|
613
638
|
a pill indicator behind the icon, the label
|
|
614
639
|
beneath (the navigation-rail pattern).
|
|
640
|
+
|
|
641
|
+
Expand/collapse holds one seat, as ThreadPanel
|
|
642
|
+
does: the logo, every icon and the avatar sit
|
|
643
|
+
at the same x and y in both states, so only the
|
|
644
|
+
rail's width, the rows' explicit heights and
|
|
645
|
+
the labels' opacity move. The caption under the
|
|
646
|
+
icon fades out as the inline label fades in.
|
|
615
647
|
============================================ */
|
|
616
648
|
.ds-app-sidebar--panel-mode:not(.ds-app-sidebar--expanded) {
|
|
617
649
|
width: 80px;
|
|
618
650
|
}
|
|
619
651
|
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
652
|
+
/* The seat: 12px row margin + 16px row padding puts every 24px icon's
|
|
653
|
+
centre on the 80px rail's centre line (28 + 12 = 40), and the logo,
|
|
654
|
+
the category labels and the profile row share that leading edge. */
|
|
655
|
+
.ds-app-sidebar--panel-mode .ds-app-sidebar__logo {
|
|
656
|
+
padding-inline-start: calc(var(--padding-500) + var(--padding-200));
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
.ds-app-sidebar--panel-mode .ds-app-sidebar__category {
|
|
660
|
+
padding-inline-start: calc(var(--padding-500) + var(--padding-200));
|
|
623
661
|
}
|
|
624
662
|
|
|
625
|
-
.ds-app-sidebar--panel-mode
|
|
663
|
+
.ds-app-sidebar--panel-mode .ds-app-sidebar__nav {
|
|
626
664
|
gap: 0;
|
|
665
|
+
transition: gap var(--motion-duration-slow) var(--motion-ease-emphasized);
|
|
627
666
|
}
|
|
628
667
|
|
|
629
|
-
.ds-app-sidebar--panel-mode
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
668
|
+
.ds-app-sidebar--panel-mode.ds-app-sidebar--expanded .ds-app-sidebar__nav {
|
|
669
|
+
gap: var(--gap-100);
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
/* The icon is pinned to the row's top in both states, so it holds its
|
|
673
|
+
y while the row's height glides between its two explicit values. */
|
|
674
|
+
.ds-app-sidebar--panel-mode .ds-app-sidebar__btn {
|
|
675
|
+
position: relative;
|
|
676
|
+
isolation: isolate;
|
|
677
|
+
align-items: flex-start;
|
|
678
|
+
/* Collapsed: the 32px indicator, the caption beneath it, a 4px beat
|
|
679
|
+
above, between and below. */
|
|
680
|
+
height: calc(32px + var(--font-caption-line-height) + var(--padding-100) * 3);
|
|
681
|
+
margin: 0 var(--padding-300);
|
|
682
|
+
padding: var(--padding-200) var(--padding-200) var(--padding-200) var(--padding-400);
|
|
637
683
|
overflow: visible;
|
|
684
|
+
transition:
|
|
685
|
+
background-color var(--motion-duration-fast) var(--motion-ease-standard),
|
|
686
|
+
height var(--motion-duration-slow) var(--motion-ease-emphasized);
|
|
638
687
|
}
|
|
639
688
|
|
|
640
|
-
.ds-app-sidebar--panel-mode
|
|
641
|
-
|
|
642
|
-
background-color: transparent;
|
|
689
|
+
.ds-app-sidebar--panel-mode.ds-app-sidebar--expanded .ds-app-sidebar__btn {
|
|
690
|
+
height: 40px;
|
|
643
691
|
}
|
|
644
692
|
|
|
645
|
-
.ds-app-sidebar--panel-mode
|
|
646
|
-
|
|
647
|
-
gap: var(--gap-100);
|
|
648
|
-
width: 100%;
|
|
693
|
+
.ds-app-sidebar--panel-mode .ds-app-sidebar__btn-left {
|
|
694
|
+
height: 24px;
|
|
649
695
|
}
|
|
650
696
|
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
the icon and label off centre. Here they leave the layout outright. */
|
|
654
|
-
.ds-app-sidebar--panel-mode:not(.ds-app-sidebar--expanded) .ds-app-sidebar__btn-chevron,
|
|
655
|
-
.ds-app-sidebar--panel-mode:not(.ds-app-sidebar--expanded) .ds-app-sidebar__btn-badge {
|
|
656
|
-
display: none;
|
|
697
|
+
.ds-app-sidebar--panel-mode .ds-app-sidebar__btn-chevron {
|
|
698
|
+
flex-shrink: 0;
|
|
657
699
|
}
|
|
658
700
|
|
|
659
701
|
/* The indicator: a 56 x 32 pill behind the icon carries hover and the
|
|
660
|
-
selection, so the
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
702
|
+
selection while collapsed, so the caption stays legible on the page
|
|
703
|
+
ground; expanded, the row's own pill takes over and the two crossfade. */
|
|
704
|
+
.ds-app-sidebar--panel-mode .ds-app-sidebar__btn::before {
|
|
705
|
+
content: '';
|
|
706
|
+
position: absolute;
|
|
707
|
+
top: var(--padding-100);
|
|
708
|
+
inset-inline-start: 0;
|
|
709
|
+
z-index: -1;
|
|
665
710
|
width: 56px;
|
|
666
711
|
height: 32px;
|
|
667
712
|
border-radius: var(--radius-pill);
|
|
713
|
+
background-color: transparent;
|
|
668
714
|
transition: background-color var(--motion-duration-fast) var(--motion-ease-standard);
|
|
669
715
|
}
|
|
670
716
|
|
|
671
|
-
.ds-app-sidebar--panel-mode:not(.ds-app-sidebar--expanded) .ds-app-sidebar__btn:hover
|
|
672
|
-
.ds-app-sidebar--panel-mode:not(.ds-app-sidebar--expanded) .ds-app-sidebar__btn--active
|
|
717
|
+
.ds-app-sidebar--panel-mode:not(.ds-app-sidebar--expanded) .ds-app-sidebar__btn:hover,
|
|
718
|
+
.ds-app-sidebar--panel-mode:not(.ds-app-sidebar--expanded) .ds-app-sidebar__btn--active {
|
|
719
|
+
background-color: transparent;
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
.ds-app-sidebar--panel-mode:not(.ds-app-sidebar--expanded) .ds-app-sidebar__btn:hover::before,
|
|
723
|
+
.ds-app-sidebar--panel-mode:not(.ds-app-sidebar--expanded) .ds-app-sidebar__btn--active::before {
|
|
673
724
|
background-color: var(--color-action-passive-bg-hover);
|
|
674
725
|
}
|
|
675
726
|
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
727
|
+
/* The caption: centred under the indicator, the rail's 72px wide
|
|
728
|
+
(80 less a 4px inset either side), fading out as the rail opens. */
|
|
729
|
+
.ds-app-sidebar__btn-caption {
|
|
730
|
+
position: absolute;
|
|
731
|
+
top: calc(32px + var(--padding-100) * 2);
|
|
732
|
+
inset-inline-start: calc(var(--padding-200) * -1);
|
|
733
|
+
width: 72px;
|
|
679
734
|
font-family: var(--font-caption-family);
|
|
680
735
|
font-size: var(--font-caption-size);
|
|
681
736
|
font-weight: var(--font-caption-weight);
|
|
682
737
|
line-height: var(--font-caption-line-height);
|
|
683
738
|
letter-spacing: var(--font-caption-letter-spacing);
|
|
739
|
+
color: var(--color-text-primary);
|
|
684
740
|
text-align: center;
|
|
741
|
+
white-space: nowrap;
|
|
742
|
+
overflow: hidden;
|
|
685
743
|
text-overflow: ellipsis;
|
|
744
|
+
pointer-events: none;
|
|
745
|
+
opacity: 1;
|
|
746
|
+
transition: opacity var(--motion-duration-base) var(--motion-ease-standard);
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
.ds-app-sidebar--expanded .ds-app-sidebar__btn-caption {
|
|
750
|
+
opacity: 0;
|
|
751
|
+
transition-duration: var(--motion-duration-fast);
|
|
686
752
|
}
|
|
687
753
|
|
|
688
|
-
.ds-app-sidebar--panel-mode:not(.ds-app-sidebar--expanded) .ds-app-sidebar__btn--active .ds-app-sidebar__btn-
|
|
754
|
+
.ds-app-sidebar--panel-mode:not(.ds-app-sidebar--expanded) .ds-app-sidebar__btn--active .ds-app-sidebar__btn-caption {
|
|
689
755
|
font-weight: var(--font-paragraph-sm-emphasis-weight);
|
|
690
756
|
}
|
|
691
757
|
|
|
692
|
-
.ds-app-sidebar--panel-mode
|
|
693
|
-
/* Centres the 40px avatar in the 80px rail. */
|
|
758
|
+
.ds-app-sidebar--panel-mode .ds-app-sidebar__profile {
|
|
759
|
+
/* Centres the 40px avatar in the 80px rail, and holds it there open. */
|
|
694
760
|
padding: 0 var(--padding-500);
|
|
695
761
|
}
|
|
696
762
|
|
|
@@ -61,6 +61,8 @@ export interface AppSidebarProfile {
|
|
|
61
61
|
/** Avatar image URL */
|
|
62
62
|
avatarUrl?: string;
|
|
63
63
|
}
|
|
64
|
+
/** How the rail meets the page: edge to edge, floating as a card, or flush beside an inset content surface. */
|
|
65
|
+
export type AppSidebarLayout = 'flush' | 'floating' | 'inset';
|
|
64
66
|
/** Props owned by AppSidebar itself — everything else falls through to the `<nav>`. */
|
|
65
67
|
type AppSidebarOwnProps = {
|
|
66
68
|
/** Navigation sections */
|
|
@@ -97,10 +99,19 @@ type AppSidebarOwnProps = {
|
|
|
97
99
|
/** Callback when profile more button clicked */
|
|
98
100
|
onProfileMore?: () => void;
|
|
99
101
|
/**
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
* the
|
|
102
|
+
* How the rail meets the page. `flush` runs it edge to edge on the page
|
|
103
|
+
* colour. `floating` floats it off the viewport edges as a glass card:
|
|
104
|
+
* inset with rounded corners, the translucent glass fill over a backdrop
|
|
105
|
+
* blur, and the floating shadow (the inset defaults to 20px and is
|
|
106
|
+
* overridable via the --ds-sidebar-float-inset custom property). `inset`
|
|
107
|
+
* runs it flush on the app shell colour, beside a content surface inset
|
|
108
|
+
* from the edges, as AppLayout's `inset` layout draws it. Defaults to
|
|
109
|
+
* `flush` (or `floating` while the deprecated `floating` is set).
|
|
110
|
+
*/
|
|
111
|
+
layout?: AppSidebarLayout;
|
|
112
|
+
/**
|
|
113
|
+
* Floats the rail as a glass card, the same as `layout="floating"`.
|
|
114
|
+
* @deprecated Use `layout="floating"`.
|
|
104
115
|
*/
|
|
105
116
|
floating?: boolean;
|
|
106
117
|
/** Rendered under the logo row, above the nav; fades out while collapsed */
|
|
@@ -46,7 +46,8 @@ const AppSidebar = React.forwardRef(function AppSidebar2({
|
|
|
46
46
|
expanded: controlledExpanded,
|
|
47
47
|
onExpandedChange,
|
|
48
48
|
onProfileMore,
|
|
49
|
-
|
|
49
|
+
layout: layoutProp,
|
|
50
|
+
floating: floatingProp = false,
|
|
50
51
|
topSlot,
|
|
51
52
|
footerSlot,
|
|
52
53
|
className = "",
|
|
@@ -63,6 +64,8 @@ const AppSidebar = React.forwardRef(function AppSidebar2({
|
|
|
63
64
|
...rest
|
|
64
65
|
}, ref) {
|
|
65
66
|
const baseClass = "ds-app-sidebar";
|
|
67
|
+
const layout = layoutProp ?? (floatingProp ? "floating" : "flush");
|
|
68
|
+
const floating = layout === "floating";
|
|
66
69
|
const Link = useLinkComponent(linkComponent);
|
|
67
70
|
const avatarSeed = (item) => typeof item.pixelAvatar === "string" ? item.pixelAvatar : item.label;
|
|
68
71
|
const avatarItems = sections.flatMap(
|
|
@@ -228,6 +231,7 @@ const AppSidebar = React.forwardRef(function AppSidebar2({
|
|
|
228
231
|
// a modal overlay would be all cost and no labels.
|
|
229
232
|
isExpanded || mobileOpen ? `${baseClass}--expanded` : "",
|
|
230
233
|
floating ? `${baseClass}--floating` : "",
|
|
234
|
+
layout === "inset" ? `${baseClass}--inset` : "",
|
|
231
235
|
mobileOpen ? `${baseClass}--mobile-open` : "",
|
|
232
236
|
isPanelMode ? `${baseClass}--panel-mode` : "",
|
|
233
237
|
className
|
|
@@ -320,6 +324,7 @@ const AppSidebar = React.forwardRef(function AppSidebar2({
|
|
|
320
324
|
),
|
|
321
325
|
/* @__PURE__ */ jsx("span", { className: `${baseClass}__btn-label`, children: item.label })
|
|
322
326
|
] }),
|
|
327
|
+
isPanelMode && /* @__PURE__ */ jsx("span", { className: `${baseClass}__btn-caption`, "aria-hidden": "true", children: item.label }),
|
|
323
328
|
item.badge !== void 0 && /* @__PURE__ */ jsx("span", { className: `${baseClass}__btn-badge`, children: item.badge }),
|
|
324
329
|
hasChildren && /* @__PURE__ */ jsx(
|
|
325
330
|
"span",
|
|
@@ -480,7 +485,8 @@ const AppSidebar = React.forwardRef(function AppSidebar2({
|
|
|
480
485
|
panelVisible ? `${baseClass}__panel--visible` : "",
|
|
481
486
|
isExpanded ? `${baseClass}__panel--beside-expanded` : "",
|
|
482
487
|
isPanelMode ? `${baseClass}__panel--panel-mode` : "",
|
|
483
|
-
floating ? `${baseClass}__panel--floating` : ""
|
|
488
|
+
floating ? `${baseClass}__panel--floating` : "",
|
|
489
|
+
layout === "inset" ? `${baseClass}__panel--inset` : ""
|
|
484
490
|
].filter(Boolean).join(" "),
|
|
485
491
|
items: panelSource.items,
|
|
486
492
|
title: panelSource.label,
|
|
@@ -22,7 +22,7 @@ import { TableContext } from "./tableContext.js";
|
|
|
22
22
|
import { Popover } from "../Popover/Popover.js";
|
|
23
23
|
import { typedComparator, TYPE_DEFAULTS, typedText, formatNumber } from "./columnTypes.js";
|
|
24
24
|
import { TypedCell } from "./TypedCell.js";
|
|
25
|
-
import { reconcileView } from "./views.js";
|
|
25
|
+
import { slotStartingView, reconcileView } from "./views.js";
|
|
26
26
|
import { calculate, TOTALS, NUMERIC_TOTALS } from "./aggregates.js";
|
|
27
27
|
import { useLinkComponent } from "../../behaviors/LinkProvider.js";
|
|
28
28
|
import { TYPED_KINDS, LIVE_KINDS, EDITOR_MIN_WIDTH } from "./editorKinds.js";
|
|
@@ -272,11 +272,21 @@ function DataTable({
|
|
|
272
272
|
const locale = useLocale(localeProp);
|
|
273
273
|
const collator = useMemo(() => textCollator(locale), [locale]);
|
|
274
274
|
const compareValues = useMemo(() => valueComparator(collator), [collator]);
|
|
275
|
-
const [
|
|
276
|
-
const
|
|
275
|
+
const [startingView] = useState(() => slotStartingView(toolbar));
|
|
276
|
+
const startsWith = (key, controlled) => controlled === void 0 && startingView?.[key] !== void 0;
|
|
277
|
+
const [size, setSize] = useControllable(
|
|
278
|
+
sizeProp,
|
|
279
|
+
(startsWith("size", sizeProp) ? startingView.size : void 0) ?? defaultSize ?? "default",
|
|
280
|
+
onSizeChange
|
|
281
|
+
);
|
|
282
|
+
const [search, setSearchState] = useControllable(
|
|
283
|
+
searchProp,
|
|
284
|
+
(startsWith("search", searchProp) ? startingView.search : void 0) ?? defaultSearch ?? "",
|
|
285
|
+
onSearchChange
|
|
286
|
+
);
|
|
277
287
|
const [filters, setFiltersState] = useControllable(
|
|
278
288
|
filtersProp,
|
|
279
|
-
defaultFilters ?? [],
|
|
289
|
+
(startsWith("filters", filtersProp) ? startingView.filters : void 0) ?? defaultFilters ?? [],
|
|
280
290
|
onFiltersChange
|
|
281
291
|
);
|
|
282
292
|
const filterLabels = {
|
|
@@ -286,7 +296,7 @@ function DataTable({
|
|
|
286
296
|
};
|
|
287
297
|
const [pageSize, setPageSize] = useControllable(
|
|
288
298
|
pageSizeProp,
|
|
289
|
-
defaultPageSize,
|
|
299
|
+
(startsWith("pageSize", pageSizeProp) ? startingView.pageSize : void 0) ?? defaultPageSize,
|
|
290
300
|
onPageSizeChange
|
|
291
301
|
);
|
|
292
302
|
const [page, setPageState] = useControllable(pageProp, defaultPage ?? 1, onPageChange);
|
|
@@ -321,14 +331,15 @@ function DataTable({
|
|
|
321
331
|
return () => clearTimeout(timer);
|
|
322
332
|
}, [announceRequest]);
|
|
323
333
|
const isSortControlled = sortsProp !== void 0 || sort !== void 0;
|
|
334
|
+
const openingSorts = defaultSorts ?? (defaultSort ? [defaultSort] : []);
|
|
324
335
|
const [uncontrolledSorts, setUncontrolledSorts] = useState(
|
|
325
|
-
|
|
336
|
+
(startsWith("sorts", sortsProp ?? sort) ? startingView.sorts : void 0) ?? openingSorts
|
|
326
337
|
);
|
|
327
338
|
const currentSorts = sortsProp ?? (sort !== void 0 ? sort ? [sort] : [] : uncontrolledSorts);
|
|
328
339
|
const sortOf = (key) => currentSorts.find((entry) => entry.key === key);
|
|
329
340
|
const [groupingState, setGroupingState] = useControllable(
|
|
330
341
|
groupingProp,
|
|
331
|
-
defaultGrouping ?? [],
|
|
342
|
+
(startsWith("grouping", groupingProp) ? startingView.grouping : void 0) ?? defaultGrouping ?? [],
|
|
332
343
|
onGroupingChange
|
|
333
344
|
);
|
|
334
345
|
const [expandedState, setExpanded] = useControllable(
|
|
@@ -391,7 +402,7 @@ function DataTable({
|
|
|
391
402
|
const canHide = (col) => !col.locked && (col.hideable ?? true);
|
|
392
403
|
const [widthsState, setWidths] = useControllable(
|
|
393
404
|
columnWidths,
|
|
394
|
-
defaultColumnWidths ?? {},
|
|
405
|
+
(startsWith("columnWidths", columnWidths) ? startingView.columnWidths : void 0) ?? defaultColumnWidths ?? {},
|
|
395
406
|
onColumnWidthsChange
|
|
396
407
|
);
|
|
397
408
|
const [orderState, setOrder] = useControllable(columnOrder, defaultColumnOrder ?? [], onColumnOrderChange);
|
|
@@ -1022,7 +1033,16 @@ function DataTable({
|
|
|
1022
1033
|
size,
|
|
1023
1034
|
...pageSize !== void 0 ? { pageSize } : {}
|
|
1024
1035
|
};
|
|
1025
|
-
const [baseView] = useState(
|
|
1036
|
+
const [baseView] = useState(() => ({
|
|
1037
|
+
...currentView,
|
|
1038
|
+
...startsWith("sorts", sortsProp ?? sort) ? { sorts: openingSorts } : {},
|
|
1039
|
+
...startsWith("filters", filtersProp) ? { filters: defaultFilters ?? [] } : {},
|
|
1040
|
+
...startsWith("search", searchProp) ? { search: defaultSearch ?? "" } : {},
|
|
1041
|
+
...startsWith("columnWidths", columnWidths) ? { columnWidths: defaultColumnWidths ?? {} } : {},
|
|
1042
|
+
...startsWith("grouping", groupingProp) ? { grouping: defaultGrouping ?? [] } : {},
|
|
1043
|
+
...startsWith("size", sizeProp) ? { size: defaultSize ?? "default" } : {},
|
|
1044
|
+
...startsWith("pageSize", pageSizeProp) ? { pageSize: defaultPageSize } : {}
|
|
1045
|
+
}));
|
|
1026
1046
|
const viewColumns = columns.map((col) => ({
|
|
1027
1047
|
key: col.key,
|
|
1028
1048
|
filterKind: col.type ? TYPE_DEFAULTS[col.type].filter : void 0,
|
|
@@ -120,9 +120,8 @@ function FilterEditor({
|
|
|
120
120
|
}
|
|
121
121
|
);
|
|
122
122
|
})();
|
|
123
|
-
const isList = column.kind === "enum" || column.kind === "tags";
|
|
124
123
|
return /* @__PURE__ */ jsxs("div", { className: "ds-data-table__filter-editor", role: "group", "aria-label": labels.editor(column.label), children: [
|
|
125
|
-
|
|
124
|
+
operators.length > 1 && /* @__PURE__ */ jsx(
|
|
126
125
|
Dropdown,
|
|
127
126
|
{
|
|
128
127
|
size,
|
|
@@ -2,9 +2,10 @@ const OPERATORS = {
|
|
|
2
2
|
text: ["contains", "notContains", "is", "isNot", "startsWith", "endsWith", "isEmpty", "isNotEmpty"],
|
|
3
3
|
number: ["eq", "neq", "lt", "lte", "gt", "gte", "between", "isEmpty", "isNotEmpty"],
|
|
4
4
|
date: ["is", "before", "after", "between", "isEmpty", "isNotEmpty"],
|
|
5
|
-
// A list filter is its checkboxes
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
// A list filter is its checkboxes; the condition says what a tick means,
|
|
6
|
+
// and the first, "any of", is the one a new filter starts with.
|
|
7
|
+
enum: ["isAnyOf", "isNoneOf"],
|
|
8
|
+
tags: ["hasAnyOf", "hasAllOf", "hasNoneOf"],
|
|
8
9
|
boolean: ["isChecked", "isNotChecked"]
|
|
9
10
|
};
|
|
10
11
|
const VALUELESS = /* @__PURE__ */ new Set(["isEmpty", "isNotEmpty", "isChecked", "isNotChecked"]);
|
|
@@ -30,6 +30,15 @@ export interface TableView {
|
|
|
30
30
|
/** Rows per page. */
|
|
31
31
|
pageSize?: number;
|
|
32
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* The view a table starts on when a TableToolbar with saved views sits
|
|
35
|
+
* directly in its `toolbar` slot: the active one (`activeView`, else
|
|
36
|
+
* `defaultActiveView`, else the first). Read from the element's props, so
|
|
37
|
+
* the table's first render, the server's HTML included, is already that
|
|
38
|
+
* view; the toolbar applies it on mount too, for a bar the table cannot
|
|
39
|
+
* see into (one wrapped in another component).
|
|
40
|
+
*/
|
|
41
|
+
export declare function slotStartingView(toolbar: unknown): TableView | undefined;
|
|
33
42
|
/** What reconciling needs to know about a column. */
|
|
34
43
|
export interface ViewColumn {
|
|
35
44
|
key: string;
|
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
import { OPERATORS, VALUELESS } from "./filterModel.js";
|
|
2
|
+
function slotStartingView(toolbar) {
|
|
3
|
+
if (!toolbar || typeof toolbar !== "object" || !("props" in toolbar)) return void 0;
|
|
4
|
+
const props = toolbar.props;
|
|
5
|
+
const views = props?.views ?? props?.defaultViews;
|
|
6
|
+
if (!Array.isArray(views) || views.length === 0) return void 0;
|
|
7
|
+
const id = props?.activeView ?? props?.defaultActiveView;
|
|
8
|
+
return (views.find((v) => v.id === id) ?? views[0]).view;
|
|
9
|
+
}
|
|
2
10
|
function reconcileView(view, columns) {
|
|
3
11
|
const byKey = new Map(columns.map((col) => [col.key, col]));
|
|
4
12
|
const known = (key) => byKey.has(key);
|
|
@@ -35,5 +43,6 @@ function reconcileView(view, columns) {
|
|
|
35
43
|
return next;
|
|
36
44
|
}
|
|
37
45
|
export {
|
|
38
|
-
reconcileView
|
|
46
|
+
reconcileView,
|
|
47
|
+
slotStartingView
|
|
39
48
|
};
|
|
@@ -228,3 +228,72 @@
|
|
|
228
228
|
.ds-segmented-control--compact .ds-segmented-control__icon {
|
|
229
229
|
--icon-size: var(--icon-size-500);
|
|
230
230
|
}
|
|
231
|
+
|
|
232
|
+
/* ============================================
|
|
233
|
+
ICON STRIP (showLabels={false})
|
|
234
|
+
The glyphs alone, with each label turned
|
|
235
|
+
into the segment's tooltip: it appears below
|
|
236
|
+
the segment on hover and keyboard focus, on
|
|
237
|
+
Tooltip's surface, so an unfamiliar glyph is
|
|
238
|
+
never left to be guessed. The component
|
|
239
|
+
places the label with the shared
|
|
240
|
+
anchored-position behaviour, so a seat that
|
|
241
|
+
clips cannot cut it off, and it flips above
|
|
242
|
+
the strip when there is no room below. The
|
|
243
|
+
label stays in the accessibility tree as the
|
|
244
|
+
segment's name. A strip the collapse ladder
|
|
245
|
+
squeezed to icons keeps its labels clipped
|
|
246
|
+
instead.
|
|
247
|
+
============================================ */
|
|
248
|
+
|
|
249
|
+
.ds-segmented-control__label--tip {
|
|
250
|
+
/* Out of flow at rest; the behaviour makes it fixed once it shows. */
|
|
251
|
+
position: absolute;
|
|
252
|
+
z-index: var(--layer-floating);
|
|
253
|
+
padding: var(--padding-150) var(--padding-200);
|
|
254
|
+
border-radius: var(--radius-100);
|
|
255
|
+
border: var(--border-025) solid var(--color-bg-container-border);
|
|
256
|
+
background-color: var(--color-bg-page-primary);
|
|
257
|
+
color: var(--color-text-primary);
|
|
258
|
+
box-shadow: var(--shadow-floating);
|
|
259
|
+
font-family: var(--font-paragraph-sm-family);
|
|
260
|
+
font-size: var(--font-paragraph-sm-size);
|
|
261
|
+
font-weight: var(--font-paragraph-sm-weight);
|
|
262
|
+
line-height: var(--font-paragraph-sm-line-height);
|
|
263
|
+
letter-spacing: var(--font-paragraph-sm-letter-spacing);
|
|
264
|
+
white-space: nowrap;
|
|
265
|
+
pointer-events: none;
|
|
266
|
+
opacity: 0;
|
|
267
|
+
transform: translateY(calc(-1 * var(--gap-100)));
|
|
268
|
+
transition:
|
|
269
|
+
opacity var(--motion-duration-fast) var(--motion-ease-standard),
|
|
270
|
+
transform var(--motion-duration-fast) var(--motion-ease-standard);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/* --motion-duration-slow matches MOTION_HOVER_SHOW_DELAY_MS, the hover-intent
|
|
274
|
+
delay Tooltip uses in JS, so the label does not flash while the pointer
|
|
275
|
+
passes through. Hiding takes no delay, like a tooltip leaving. */
|
|
276
|
+
.ds-segmented-control__segment:hover .ds-segmented-control__label--tip,
|
|
277
|
+
.ds-segmented-control__segment:focus-visible .ds-segmented-control__label--tip {
|
|
278
|
+
opacity: 1;
|
|
279
|
+
transform: none;
|
|
280
|
+
transition-delay: var(--motion-duration-slow);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/* Flipped above the strip, it slides down into place instead. */
|
|
284
|
+
.ds-segmented-control__label--tip[data-side='top'] {
|
|
285
|
+
transform: translateY(var(--gap-100));
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/* Without an inline label a segment's asymmetric padding reads lopsided:
|
|
289
|
+
square each one into an even pill around its glyph. */
|
|
290
|
+
.ds-segmented-control--icons .ds-segmented-control__segment {
|
|
291
|
+
padding-inline: var(--padding-200);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/* ds-allow(motion): drops the label tooltip's slide outright rather than playing it at 0.01ms. */
|
|
295
|
+
@media (prefers-reduced-motion: reduce) {
|
|
296
|
+
.ds-segmented-control__label--tip {
|
|
297
|
+
transition: none;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
@@ -65,9 +65,10 @@ type SegmentedControlOwnProps = {
|
|
|
65
65
|
collapse?: boolean;
|
|
66
66
|
/**
|
|
67
67
|
* Show each segment's label beside its icon. Set it to `false` for an
|
|
68
|
-
* icon-only strip:
|
|
69
|
-
*
|
|
70
|
-
* screen. Defaults to
|
|
68
|
+
* icon-only strip: each label becomes its segment's tooltip on hover and
|
|
69
|
+
* keyboard focus and stays the segment's name in the accessibility tree,
|
|
70
|
+
* and a segment without an icon keeps its label on screen. Defaults to
|
|
71
|
+
* `true`.
|
|
71
72
|
*/
|
|
72
73
|
showLabels?: boolean;
|
|
73
74
|
/**
|