@olenbetong/appframe-ds 1.6.0 → 1.7.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.
Files changed (154) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/package.json +3 -3
  3. package/scripts/copyAssets.mjs +27 -27
  4. package/src/autocomplete/Autocomplete.css +253 -253
  5. package/src/autocomplete/Autocomplete.tsx +320 -320
  6. package/src/autocomplete/AutocompleteFrame.tsx +168 -168
  7. package/src/autocomplete/Combobox.tsx +286 -286
  8. package/src/autocomplete/index.ts +4 -4
  9. package/src/autocomplete/types.ts +204 -204
  10. package/src/autocomplete/utils.ts +183 -183
  11. package/src/binding/BoundNumberInput.tsx +42 -42
  12. package/src/binding/BoundNumericTextField.tsx +102 -102
  13. package/src/binding/BoundTextField.tsx +49 -49
  14. package/src/binding/CancelButton.tsx +30 -30
  15. package/src/binding/CancelIconButton.tsx +30 -30
  16. package/src/binding/DataEditToolbar.css +23 -23
  17. package/src/binding/DataEditToolbar.tsx +84 -84
  18. package/src/binding/DeleteButton.tsx +35 -35
  19. package/src/binding/DeleteIconButton.tsx +35 -35
  20. package/src/binding/RefreshButton.tsx +31 -31
  21. package/src/binding/RefreshIconButton.tsx +33 -33
  22. package/src/binding/RefreshRowIconButton.tsx +33 -33
  23. package/src/binding/SaveButton.tsx +37 -37
  24. package/src/binding/SaveIconButton.tsx +38 -38
  25. package/src/binding/index.ts +20 -20
  26. package/src/container/Container.tsx +50 -50
  27. package/src/container/index.ts +1 -1
  28. package/src/cutoff/CutOffPicker.css +12 -12
  29. package/src/cutoff/CutOffPicker.tsx +165 -165
  30. package/src/cutoff/context.tsx +194 -194
  31. package/src/cutoff/dsCutOffDates.ts +21 -21
  32. package/src/cutoff/index.ts +4 -4
  33. package/src/cutoff/utils.test.ts +59 -59
  34. package/src/cutoff/utils.ts +276 -276
  35. package/src/filter/ChipListInput.tsx +79 -79
  36. package/src/filter/FieldFilterPanel.css +150 -150
  37. package/src/filter/FieldFilterPanel.tsx +309 -309
  38. package/src/filter/FilterBuilder.css +152 -152
  39. package/src/filter/FilterBuilder.test.tsx +153 -153
  40. package/src/filter/FilterBuilder.tsx +435 -435
  41. package/src/filter/FilterEditor.css +231 -231
  42. package/src/filter/FilterEditor.test.tsx +285 -285
  43. package/src/filter/FilterEditor.tsx +75 -75
  44. package/src/filter/FilterGroupEditor.tsx +160 -160
  45. package/src/filter/FilterNameDialog.css +22 -22
  46. package/src/filter/FilterNameDialog.tsx +81 -81
  47. package/src/filter/FilterRow.tsx +117 -117
  48. package/src/filter/FilterShareDialog.css +28 -28
  49. package/src/filter/FilterShareDialog.tsx +201 -201
  50. package/src/filter/FilterStringField.tsx +78 -78
  51. package/src/filter/FilterValueEditor.tsx +239 -239
  52. package/src/filter/SavedFilterTree.css +107 -107
  53. package/src/filter/SavedFilterTree.test.tsx +94 -94
  54. package/src/filter/SavedFilterTree.tsx +218 -218
  55. package/src/filter/fieldFilter.test.ts +158 -158
  56. package/src/filter/fieldFilter.ts +139 -139
  57. package/src/filter/index.ts +27 -27
  58. package/src/filter/types.ts +38 -38
  59. package/src/filter/useDistinctValues.test.ts +102 -102
  60. package/src/filter/useDistinctValues.ts +230 -230
  61. package/src/global.d.ts +11 -11
  62. package/src/grid/formatters.ts +23 -0
  63. package/src/grid/index.css +8 -2
  64. package/src/grid/index.tsx +73 -3
  65. package/src/grid/useAfColumns.tsx +28 -4
  66. package/src/grid/useAfGridApi.ts +3 -0
  67. package/src/grid/utcDate.test.ts +25 -0
  68. package/src/grid/utcDate.ts +28 -0
  69. package/src/grid/utcDateEditing.tsx +11 -0
  70. package/src/i18n.ts +13 -13
  71. package/src/index.ts +14 -14
  72. package/src/input/InputAdornments.css +80 -80
  73. package/src/input/InputAdornments.tsx +102 -102
  74. package/src/input/NumberInput.css +9 -9
  75. package/src/input/NumberInput.test.tsx +212 -212
  76. package/src/input/NumberInput.tsx +268 -268
  77. package/src/input/index.ts +3 -3
  78. package/src/input/numberFormat.test.ts +79 -79
  79. package/src/input/numberFormat.ts +94 -94
  80. package/src/layout/AppMenu.tsx +74 -74
  81. package/src/layout/AppMenuDrawer.css +71 -71
  82. package/src/layout/AppMenuDrawer.tsx +122 -122
  83. package/src/layout/AppMenuItem.css +6 -2
  84. package/src/layout/DialogMotion.css +79 -79
  85. package/src/layout/ErrorBoundary.tsx +162 -162
  86. package/src/layout/PageContainer.css +48 -48
  87. package/src/layout/PageContainer.tsx +41 -41
  88. package/src/layout/ResponsiveDialog.css +86 -86
  89. package/src/layout/ResponsiveDialog.tsx +55 -55
  90. package/src/layout/index.tsx +70 -70
  91. package/src/link/Link.tsx +23 -23
  92. package/src/link/index.ts +1 -1
  93. package/src/mdi/Mdi.css +8 -8
  94. package/src/mdi/Mdi.test.tsx +91 -91
  95. package/src/mdi/Mdi.tsx +141 -141
  96. package/src/mdi/MdiConfirmCloseDialog.tsx +47 -47
  97. package/src/mdi/MdiContext.tsx +59 -59
  98. package/src/mdi/MdiDocumentContext.tsx +60 -60
  99. package/src/mdi/MdiPanel.tsx +61 -61
  100. package/src/mdi/MdiRestore.test.tsx +86 -86
  101. package/src/mdi/MdiTab.tsx +30 -30
  102. package/src/mdi/controller.test.ts +382 -382
  103. package/src/mdi/controller.ts +656 -656
  104. package/src/mdi/dockviewHost.ts +132 -132
  105. package/src/mdi/fakeHost.ts +200 -200
  106. package/src/mdi/host.ts +44 -44
  107. package/src/mdi/ids.test.ts +20 -20
  108. package/src/mdi/ids.ts +18 -18
  109. package/src/mdi/index.ts +31 -31
  110. package/src/mdi/layoutRestore.test.ts +156 -156
  111. package/src/mdi/layoutRestore.ts +311 -311
  112. package/src/mdi/layoutSpec.test.ts +143 -143
  113. package/src/mdi/layoutSpec.ts +214 -214
  114. package/src/mdi/layoutStore.test.ts +93 -93
  115. package/src/mdi/layoutStore.ts +99 -99
  116. package/src/mdi/memorySettingsStore.ts +21 -21
  117. package/src/mdi/registry.ts +34 -34
  118. package/src/mdi/testTypes.ts +37 -37
  119. package/src/mdi/theme.ts +22 -22
  120. package/src/mdi/types.ts +214 -214
  121. package/src/page/Page.css +57 -57
  122. package/src/page/PageBlock.css +64 -64
  123. package/src/page/PageBlock.tsx +93 -93
  124. package/src/page/PageHeader.css +88 -88
  125. package/src/page/PageHeader.tsx +115 -115
  126. package/src/page/PageTitle.css +5 -5
  127. package/src/page/PageTitle.tsx +21 -21
  128. package/src/page/index.ts +4 -4
  129. package/src/paper/Paper.tsx +52 -52
  130. package/src/progress/LinearProgress.css +121 -121
  131. package/src/progress/LinearProgress.tsx +106 -106
  132. package/src/progress/index.ts +1 -1
  133. package/src/report-downloader/ReportDownloader.css +67 -67
  134. package/src/report-downloader/components/StatusItem.tsx +87 -87
  135. package/src/report-downloader/components/StatusList.tsx +28 -28
  136. package/src/report-downloader/index.ts +41 -41
  137. package/src/report-downloader/status.tsx +92 -92
  138. package/src/sortable/DropIndicator.css +51 -51
  139. package/src/sortable/DropIndicator.tsx +27 -27
  140. package/src/sortable/SortableItemList.css +21 -21
  141. package/src/sortable/SortableItemList.tsx +264 -264
  142. package/src/sortable/index.ts +8 -8
  143. package/src/sortable/reorderSortOrder.ts +126 -126
  144. package/src/sortable/sortOrder.ts +101 -101
  145. package/src/sortable/types.ts +9 -9
  146. package/src/sortable/useSortableContainer.ts +53 -53
  147. package/src/sortable/useSortableItem.ts +95 -95
  148. package/src/sortable/useSortableList.ts +117 -117
  149. package/src/test/happy-dom-document-class.ts +24 -24
  150. package/src/test/setup.ts +27 -27
  151. package/src/theme/useDsColorScheme.ts +53 -53
  152. package/tsconfig.build.json +9 -9
  153. package/tsconfig.json +8 -8
  154. package/vitest.config.ts +10 -10
@@ -1,55 +1,55 @@
1
- import "./ResponsiveDialog.css";
2
-
3
- import { Dialog, type DialogProps, Heading } from "@digdir/designsystemet-react";
4
- import clsx from "clsx";
5
-
6
- export type ResponsiveDialogMaxWidth = "xs" | "sm" | "md" | "lg" | "xl" | false;
7
-
8
- export type ResponsiveDialogProps = Omit<DialogProps, "title" | "open" | "onClose"> & {
9
- open: boolean;
10
- onClose?: () => void;
11
- title?: React.ReactNode;
12
- /**
13
- * Width of the dialog on screens wide enough for it, in MUI's breakpoint names (`sm` is
14
- * 600px, `md` 900px, `lg` 1200px, `xl` 1536px). Below the next breakpoint up the dialog
15
- * fills the screen. `false` fills the screen always.
16
- */
17
- maxWidth?: ResponsiveDialogMaxWidth;
18
- };
19
-
20
- /**
21
- * A Designsystemet `Dialog` that is a centred, width-limited dialog on large screens and
22
- * full-screen on small ones - the MUI `Dialog fullScreen` behaviour, decided by CSS media
23
- * queries rather than a hook. The title goes in the first `Dialog.Block` with the dialog's own
24
- * close button; put the content and actions in further `Dialog.Block`s.
25
- *
26
- * Equivalent of `ResponsiveDialog` in `@olenbetong/appframe-mui`. `onClose` takes no
27
- * arguments: the dialog closes on the close button, the backdrop and Escape alike.
28
- */
29
- export function ResponsiveDialog({
30
- children,
31
- className,
32
- maxWidth = "sm",
33
- onClose,
34
- open,
35
- title,
36
- ...props
37
- }: ResponsiveDialogProps) {
38
- return (
39
- <Dialog
40
- closedby="any"
41
- {...props}
42
- open={open}
43
- onClose={onClose}
44
- className={clsx("ObResponsiveDialog-root", className)}
45
- data-max-width={maxWidth === false ? "none" : maxWidth}
46
- >
47
- {title && (
48
- <Dialog.Block>
49
- <Heading data-size="xs">{title}</Heading>
50
- </Dialog.Block>
51
- )}
52
- {children}
53
- </Dialog>
54
- );
55
- }
1
+ import "./ResponsiveDialog.css";
2
+
3
+ import { Dialog, type DialogProps, Heading } from "@digdir/designsystemet-react";
4
+ import clsx from "clsx";
5
+
6
+ export type ResponsiveDialogMaxWidth = "xs" | "sm" | "md" | "lg" | "xl" | false;
7
+
8
+ export type ResponsiveDialogProps = Omit<DialogProps, "title" | "open" | "onClose"> & {
9
+ open: boolean;
10
+ onClose?: () => void;
11
+ title?: React.ReactNode;
12
+ /**
13
+ * Width of the dialog on screens wide enough for it, in MUI's breakpoint names (`sm` is
14
+ * 600px, `md` 900px, `lg` 1200px, `xl` 1536px). Below the next breakpoint up the dialog
15
+ * fills the screen. `false` fills the screen always.
16
+ */
17
+ maxWidth?: ResponsiveDialogMaxWidth;
18
+ };
19
+
20
+ /**
21
+ * A Designsystemet `Dialog` that is a centred, width-limited dialog on large screens and
22
+ * full-screen on small ones - the MUI `Dialog fullScreen` behaviour, decided by CSS media
23
+ * queries rather than a hook. The title goes in the first `Dialog.Block` with the dialog's own
24
+ * close button; put the content and actions in further `Dialog.Block`s.
25
+ *
26
+ * Equivalent of `ResponsiveDialog` in `@olenbetong/appframe-mui`. `onClose` takes no
27
+ * arguments: the dialog closes on the close button, the backdrop and Escape alike.
28
+ */
29
+ export function ResponsiveDialog({
30
+ children,
31
+ className,
32
+ maxWidth = "sm",
33
+ onClose,
34
+ open,
35
+ title,
36
+ ...props
37
+ }: ResponsiveDialogProps) {
38
+ return (
39
+ <Dialog
40
+ closedby="any"
41
+ {...props}
42
+ open={open}
43
+ onClose={onClose}
44
+ className={clsx("ObResponsiveDialog-root", className)}
45
+ data-max-width={maxWidth === false ? "none" : maxWidth}
46
+ >
47
+ {title && (
48
+ <Dialog.Block>
49
+ <Heading data-size="xs">{title}</Heading>
50
+ </Dialog.Block>
51
+ )}
52
+ {children}
53
+ </Dialog>
54
+ );
55
+ }
@@ -1,70 +1,70 @@
1
- import "./index.css";
2
- import "./DialogMotion.css";
3
-
4
- import { useEffect, useState } from "react";
5
- import type React from "react";
6
-
7
- import { AppLayoutDesktop } from "./AppLayoutDesktop.js";
8
- import { AppLayoutMobile } from "./AppLayoutMobile.js";
9
- import type { AppMenuProps } from "./AppMenu.js";
10
-
11
- export type AppLayoutProps = {
12
- AppMenuProps?: AppMenuProps;
13
- children: React.ReactNode | React.ReactNode[];
14
- menu?: React.ReactNode | React.ReactNode[];
15
- /**
16
- * @deprecated The toolbar is now server-rendered. This prop has no effect.
17
- */
18
- toolbar?: React.ReactNode | React.ReactNode[];
19
- /**
20
- * @deprecated The title is now rendered in the server-rendered toolbar. This prop has no effect.
21
- */
22
- title?: React.ReactNode;
23
- /**
24
- * @deprecated The toolbar is now server-rendered. This prop has no effect.
25
- */
26
- enableMenuToggle?: boolean;
27
- /**
28
- * Enable partner mode (for partner.olenbetong.no applications).
29
- * Adds a toggle button to the existing partner header instead of the app bar.
30
- */
31
- partner?: boolean;
32
- };
33
-
34
- function useIsMobile() {
35
- let [isMobile, setIsMobile] = useState(() => {
36
- if (typeof window === "undefined") return false;
37
- return window.matchMedia("(max-width: 1199px)").matches;
38
- });
39
-
40
- useEffect(() => {
41
- let mq = window.matchMedia("(max-width: 1199px)");
42
- let handler = (e: MediaQueryListEvent) => setIsMobile(e.matches);
43
- mq.addEventListener("change", handler);
44
- return () => mq.removeEventListener("change", handler);
45
- }, []);
46
-
47
- return isMobile;
48
- }
49
-
50
- export * from "./AppMenu.js";
51
- export * from "./AppMenuDrawer.js";
52
- export * from "./AppMenuItem.js";
53
- export * from "./AppMenuSubheader.js";
54
- export * from "./constants.js";
55
- export * from "./ErrorBoundary.js";
56
- export * from "./PageContainer.js";
57
- export * from "./ResponsiveDialog.js";
58
- export * from "./PartnerToggle.js";
59
-
60
- /**
61
- * @deprecated Use the `Page` primitives instead (`Page`, `Page.Header`,
62
- * `Page.Block`). `<AppLayout menu={...} title={...}>` becomes
63
- * `<Page layout="app"><Page.Header title={...} menu={...} />...</Page>`.
64
- */
65
- export function AppLayout(props: AppLayoutProps) {
66
- let isSmall = useIsMobile();
67
- let Component = isSmall ? AppLayoutMobile : AppLayoutDesktop;
68
-
69
- return <Component {...props} />;
70
- }
1
+ import "./index.css";
2
+ import "./DialogMotion.css";
3
+
4
+ import { useEffect, useState } from "react";
5
+ import type React from "react";
6
+
7
+ import { AppLayoutDesktop } from "./AppLayoutDesktop.js";
8
+ import { AppLayoutMobile } from "./AppLayoutMobile.js";
9
+ import type { AppMenuProps } from "./AppMenu.js";
10
+
11
+ export type AppLayoutProps = {
12
+ AppMenuProps?: AppMenuProps;
13
+ children: React.ReactNode | React.ReactNode[];
14
+ menu?: React.ReactNode | React.ReactNode[];
15
+ /**
16
+ * @deprecated The toolbar is now server-rendered. This prop has no effect.
17
+ */
18
+ toolbar?: React.ReactNode | React.ReactNode[];
19
+ /**
20
+ * @deprecated The title is now rendered in the server-rendered toolbar. This prop has no effect.
21
+ */
22
+ title?: React.ReactNode;
23
+ /**
24
+ * @deprecated The toolbar is now server-rendered. This prop has no effect.
25
+ */
26
+ enableMenuToggle?: boolean;
27
+ /**
28
+ * Enable partner mode (for partner.olenbetong.no applications).
29
+ * Adds a toggle button to the existing partner header instead of the app bar.
30
+ */
31
+ partner?: boolean;
32
+ };
33
+
34
+ function useIsMobile() {
35
+ let [isMobile, setIsMobile] = useState(() => {
36
+ if (typeof window === "undefined") return false;
37
+ return window.matchMedia("(max-width: 1199px)").matches;
38
+ });
39
+
40
+ useEffect(() => {
41
+ let mq = window.matchMedia("(max-width: 1199px)");
42
+ let handler = (e: MediaQueryListEvent) => setIsMobile(e.matches);
43
+ mq.addEventListener("change", handler);
44
+ return () => mq.removeEventListener("change", handler);
45
+ }, []);
46
+
47
+ return isMobile;
48
+ }
49
+
50
+ export * from "./AppMenu.js";
51
+ export * from "./AppMenuDrawer.js";
52
+ export * from "./AppMenuItem.js";
53
+ export * from "./AppMenuSubheader.js";
54
+ export * from "./constants.js";
55
+ export * from "./ErrorBoundary.js";
56
+ export * from "./PageContainer.js";
57
+ export * from "./ResponsiveDialog.js";
58
+ export * from "./PartnerToggle.js";
59
+
60
+ /**
61
+ * @deprecated Use the `Page` primitives instead (`Page`, `Page.Header`,
62
+ * `Page.Block`). `<AppLayout menu={...} title={...}>` becomes
63
+ * `<Page layout="app"><Page.Header title={...} menu={...} />...</Page>`.
64
+ */
65
+ export function AppLayout(props: AppLayoutProps) {
66
+ let isSmall = useIsMobile();
67
+ let Component = isSmall ? AppLayoutMobile : AppLayoutDesktop;
68
+
69
+ return <Component {...props} />;
70
+ }
package/src/link/Link.tsx CHANGED
@@ -1,23 +1,23 @@
1
- import { Link as DsLink, type LinkProps as DsLinkProps } from "@digdir/designsystemet-react";
2
- import { forwardRef } from "react";
3
- import { Link as RouterLink, type LinkProps as RouterLinkProps } from "react-router";
4
-
5
- export type LinkProps = Omit<DsLinkProps, "href" | "asChild"> &
6
- Omit<RouterLinkProps, "to" | "children"> & {
7
- /** The in-app path, resolved by react-router. */
8
- href: string;
9
- };
10
-
11
- /**
12
- * A Designsystemet `Link` that navigates with react-router: `href` is the route, the
13
- * navigation is a view transition. Equivalent of `Link` in `@olenbetong/appframe-mui`.
14
- */
15
- export const Link = forwardRef<HTMLAnchorElement, LinkProps>(function Link({ href, children, ...props }, ref) {
16
- return (
17
- <DsLink asChild>
18
- <RouterLink viewTransition to={href} {...props} ref={ref}>
19
- {children}
20
- </RouterLink>
21
- </DsLink>
22
- );
23
- });
1
+ import { Link as DsLink, type LinkProps as DsLinkProps } from "@digdir/designsystemet-react";
2
+ import { forwardRef } from "react";
3
+ import { Link as RouterLink, type LinkProps as RouterLinkProps } from "react-router";
4
+
5
+ export type LinkProps = Omit<DsLinkProps, "href" | "asChild"> &
6
+ Omit<RouterLinkProps, "to" | "children"> & {
7
+ /** The in-app path, resolved by react-router. */
8
+ href: string;
9
+ };
10
+
11
+ /**
12
+ * A Designsystemet `Link` that navigates with react-router: `href` is the route, the
13
+ * navigation is a view transition. Equivalent of `Link` in `@olenbetong/appframe-mui`.
14
+ */
15
+ export const Link = forwardRef<HTMLAnchorElement, LinkProps>(function Link({ href, children, ...props }, ref) {
16
+ return (
17
+ <DsLink asChild>
18
+ <RouterLink viewTransition to={href} {...props} ref={ref}>
19
+ {children}
20
+ </RouterLink>
21
+ </DsLink>
22
+ );
23
+ });
package/src/link/index.ts CHANGED
@@ -1 +1 @@
1
- export * from "./Link.js";
1
+ export * from "./Link.js";
package/src/mdi/Mdi.css CHANGED
@@ -34,17 +34,17 @@
34
34
  --dv-separator-border: var(--ds-color-neutral-border-subtle, #d5d7d9);
35
35
  --dv-paneview-header-border-color: var(--ds-color-neutral-border-default, #a6a9ad);
36
36
  --dv-sash-color: transparent;
37
- --dv-active-sash-color: var(--ds-color-accent-base-default, #116069);
37
+ --dv-active-sash-color: var(--ds-color-brand-base-default, #116069);
38
38
  --dv-active-sash-transition-duration: 0.1s;
39
39
  --dv-active-sash-transition-delay: 0.3s;
40
- --dv-drag-over-background-color: color-mix(in srgb, var(--ds-color-accent-base-default, #116069) 20%, transparent);
41
- --dv-drag-over-border-color: var(--ds-color-accent-base-default, #116069);
40
+ --dv-drag-over-background-color: color-mix(in srgb, var(--ds-color-brand-base-default, #116069) 20%, transparent);
41
+ --dv-drag-over-border-color: var(--ds-color-brand-base-default, #116069);
42
42
  --dv-drag-over-border: none;
43
- --dv-edge-dock-indicator-color: var(--ds-color-accent-base-default, #116069);
43
+ --dv-edge-dock-indicator-color: var(--ds-color-brand-base-default, #116069);
44
44
  --dv-dnd-compass-color: var(--ds-color-neutral-border-default, #a6a9ad);
45
45
  --dv-dnd-compass-cell-color: var(--ds-color-neutral-surface-default, #f3f4f5);
46
46
  --dv-dnd-compass-edge-cell-color: var(--ds-color-neutral-surface-hover, #e6e8ea);
47
- --dv-dnd-compass-active-cell-color: var(--ds-color-accent-surface-hover, #d1e5e7);
47
+ --dv-dnd-compass-active-cell-color: var(--ds-color-brand-surface-hover, #d1e5e7);
48
48
  --dv-tabs-container-scrollbar-color: var(--ds-color-neutral-border-default, #a6a9ad);
49
49
  --dv-scrollbar-background-color: var(--ds-color-neutral-border-default, #a6a9ad);
50
50
  --dv-icon-hover-background-color: var(--ds-color-neutral-surface-hover, #e6e8ea);
@@ -65,8 +65,8 @@
65
65
  --dv-sash-border-radius: 0px;
66
66
  --dv-dropdown-border-radius: var(--ds-border-radius-md, 0.25rem);
67
67
  --dv-tab-close-icon-size: inherit;
68
- --dv-smart-guides-color: var(--ds-color-accent-base-default, #116069);
69
- --dv-smart-guides-preview-color: color-mix(in srgb, var(--ds-color-accent-base-default, #116069) 20%, transparent);
68
+ --dv-smart-guides-color: var(--ds-color-brand-base-default, #116069);
69
+ --dv-smart-guides-preview-color: color-mix(in srgb, var(--ds-color-brand-base-default, #116069) 20%, transparent);
70
70
  --dv-tab-group-color-grey: #5f6368;
71
71
  --dv-tab-group-color-blue: #1a73e8;
72
72
  --dv-tab-group-color-red: #d93025;
@@ -88,7 +88,7 @@
88
88
 
89
89
  /* The active tab gets an accent line so it reads as selected without relying on background alone. */
90
90
  .ObMdi-theme .dv-tab.dv-active-tab {
91
- box-shadow: inset 0 -2px 0 var(--ds-color-accent-base-default, #116069);
91
+ box-shadow: inset 0 -2px 0 var(--ds-color-brand-base-default, #116069);
92
92
  }
93
93
 
94
94
  .ObMdi-theme .dv-tab:focus-visible {
@@ -1,91 +1,91 @@
1
- import { act, render, screen } from "@testing-library/react";
2
- import { useEffect } from "react";
3
- import { describe, expect, it, vi } from "vitest";
4
-
5
- import { Mdi } from "./Mdi.js";
6
- import { MdiProvider, useMdi, useMdiActiveDocument } from "./MdiContext.js";
7
- import { useMdiDocument } from "./MdiDocumentContext.js";
8
- import type { MdiApi, MdiDocumentProps, MdiDocumentTypes, MdiLayoutSpec } from "./types.js";
9
-
10
- vi.mock("@olenbetong/appframe-core", () => ({
11
- getLocalizedString: (value: string) => value,
12
- createLocalizer: () => ({
13
- getLocalizedString: (value: string) => value,
14
- localize: (parts: TemplateStringsArray) => parts.join(""),
15
- }),
16
- }));
17
-
18
- function Note({ params }: MdiDocumentProps<{ label: string }>) {
19
- let document = useMdiDocument();
20
-
21
- return (
22
- <div data-testid={document.id}>
23
- <span>{params.label}</span>
24
- <button type="button" onClick={() => document.setTitle(`${params.label}!`)}>
25
- rename
26
- </button>
27
- </div>
28
- );
29
- }
30
-
31
- function ActiveLabel() {
32
- let active = useMdiActiveDocument();
33
- return <output data-testid="active">{active?.id ?? "none"}</output>;
34
- }
35
-
36
- let apiRef: { current: MdiApi | null } = { current: null };
37
-
38
- function Grab() {
39
- let api = useMdi();
40
- useEffect(() => {
41
- apiRef.current = api;
42
- });
43
- return null;
44
- }
45
-
46
- const types: MdiDocumentTypes = {
47
- note: { component: Note, title: (params) => `Note ${params.label as string}` },
48
- tree: { component: Note, singleton: true, closable: false, title: "Tree" },
49
- };
50
-
51
- const spec: MdiLayoutSpec = {
52
- root: {
53
- direction: "horizontal",
54
- children: [
55
- { tabs: [{ type: "tree", params: { label: "Tree" } }], hideHeader: true },
56
- { tabs: [{ type: "note", key: "1", params: { label: "First" } }] },
57
- ],
58
- },
59
- };
60
-
61
- describe("Mdi", () => {
62
- it("renders the default layout through dockview and exposes the api", async () => {
63
- render(
64
- <MdiProvider types={types}>
65
- <Grab />
66
- <ActiveLabel />
67
- <div style={{ width: 800, height: 600 }}>
68
- <Mdi defaultLayout={spec} />
69
- </div>
70
- </MdiProvider>,
71
- );
72
-
73
- expect(await screen.findByText("First")).toBeInTheDocument();
74
- expect(screen.getByTestId("tree")).toBeInTheDocument();
75
- expect(apiRef.current?.getDocuments().map((document) => document.id)).toEqual(["tree", "note:1"]);
76
-
77
- await act(async () => {
78
- apiRef.current!.openDocument({ type: "note", key: "2", params: { label: "Second" } });
79
- });
80
-
81
- expect(await screen.findByText("Second")).toBeInTheDocument();
82
- expect(screen.getByTestId("active")).toHaveTextContent("note:2");
83
-
84
- await act(async () => {
85
- await apiRef.current!.close("note:2");
86
- });
87
-
88
- expect(screen.queryByText("Second")).not.toBeInTheDocument();
89
- expect(apiRef.current?.getDocument("tree")?.closable).toBe(false);
90
- });
91
- });
1
+ import { act, render, screen } from "@testing-library/react";
2
+ import { useEffect } from "react";
3
+ import { describe, expect, it, vi } from "vitest";
4
+
5
+ import { Mdi } from "./Mdi.js";
6
+ import { MdiProvider, useMdi, useMdiActiveDocument } from "./MdiContext.js";
7
+ import { useMdiDocument } from "./MdiDocumentContext.js";
8
+ import type { MdiApi, MdiDocumentProps, MdiDocumentTypes, MdiLayoutSpec } from "./types.js";
9
+
10
+ vi.mock("@olenbetong/appframe-core", () => ({
11
+ getLocalizedString: (value: string) => value,
12
+ createLocalizer: () => ({
13
+ getLocalizedString: (value: string) => value,
14
+ localize: (parts: TemplateStringsArray) => parts.join(""),
15
+ }),
16
+ }));
17
+
18
+ function Note({ params }: MdiDocumentProps<{ label: string }>) {
19
+ let document = useMdiDocument();
20
+
21
+ return (
22
+ <div data-testid={document.id}>
23
+ <span>{params.label}</span>
24
+ <button type="button" onClick={() => document.setTitle(`${params.label}!`)}>
25
+ rename
26
+ </button>
27
+ </div>
28
+ );
29
+ }
30
+
31
+ function ActiveLabel() {
32
+ let active = useMdiActiveDocument();
33
+ return <output data-testid="active">{active?.id ?? "none"}</output>;
34
+ }
35
+
36
+ let apiRef: { current: MdiApi | null } = { current: null };
37
+
38
+ function Grab() {
39
+ let api = useMdi();
40
+ useEffect(() => {
41
+ apiRef.current = api;
42
+ });
43
+ return null;
44
+ }
45
+
46
+ const types: MdiDocumentTypes = {
47
+ note: { component: Note, title: (params) => `Note ${params.label as string}` },
48
+ tree: { component: Note, singleton: true, closable: false, title: "Tree" },
49
+ };
50
+
51
+ const spec: MdiLayoutSpec = {
52
+ root: {
53
+ direction: "horizontal",
54
+ children: [
55
+ { tabs: [{ type: "tree", params: { label: "Tree" } }], hideHeader: true },
56
+ { tabs: [{ type: "note", key: "1", params: { label: "First" } }] },
57
+ ],
58
+ },
59
+ };
60
+
61
+ describe("Mdi", () => {
62
+ it("renders the default layout through dockview and exposes the api", async () => {
63
+ render(
64
+ <MdiProvider types={types}>
65
+ <Grab />
66
+ <ActiveLabel />
67
+ <div style={{ width: 800, height: 600 }}>
68
+ <Mdi defaultLayout={spec} />
69
+ </div>
70
+ </MdiProvider>,
71
+ );
72
+
73
+ expect(await screen.findByText("First")).toBeInTheDocument();
74
+ expect(screen.getByTestId("tree")).toBeInTheDocument();
75
+ expect(apiRef.current?.getDocuments().map((document) => document.id)).toEqual(["tree", "note:1"]);
76
+
77
+ await act(async () => {
78
+ apiRef.current!.openDocument({ type: "note", key: "2", params: { label: "Second" } });
79
+ });
80
+
81
+ expect(await screen.findByText("Second")).toBeInTheDocument();
82
+ expect(screen.getByTestId("active")).toHaveTextContent("note:2");
83
+
84
+ await act(async () => {
85
+ await apiRef.current!.close("note:2");
86
+ });
87
+
88
+ expect(screen.queryByText("Second")).not.toBeInTheDocument();
89
+ expect(apiRef.current?.getDocument("tree")?.closable).toBe(false);
90
+ });
91
+ });