@olenbetong/appframe-ds 0.6.0 → 0.8.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/CHANGELOG.md +115 -0
- package/package.json +11 -9
- package/scripts/copyAssets.mjs +6 -1
- package/src/AfLookup/Lookup.tsx +4 -4
- package/src/AfLookup/{Combobox.css → LookupCombobox.css} +1 -1
- package/src/AfLookup/{Combobox.tsx → LookupCombobox.tsx} +8 -8
- package/src/AfLookup/LookupEdit.tsx +2 -2
- package/src/AfLookup/index.ts +1 -1
- package/src/autocomplete/Autocomplete.css +243 -0
- package/src/autocomplete/Autocomplete.tsx +318 -0
- package/src/autocomplete/AutocompleteFrame.tsx +168 -0
- package/src/autocomplete/Combobox.tsx +273 -0
- package/src/autocomplete/index.ts +4 -0
- package/src/autocomplete/types.ts +204 -0
- package/src/autocomplete/utils.ts +183 -0
- package/src/container/Container.tsx +49 -0
- package/src/container/index.ts +1 -0
- package/src/index.ts +3 -0
- package/src/paper/Paper.tsx +2 -0
- package/src/sortable/DropIndicator.css +51 -0
- package/src/sortable/DropIndicator.tsx +27 -0
- package/src/sortable/SortableItemList.css +21 -0
- package/src/sortable/SortableItemList.tsx +264 -0
- package/src/sortable/index.ts +8 -0
- package/src/sortable/reorderSortOrder.ts +126 -0
- package/src/sortable/sortOrder.ts +101 -0
- package/src/sortable/types.ts +9 -0
- package/src/sortable/useSortableContainer.ts +53 -0
- package/src/sortable/useSortableItem.ts +95 -0
- package/src/sortable/useSortableList.ts +117 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,120 @@
|
|
|
1
1
|
# @olenbetong/appframe-ds
|
|
2
2
|
|
|
3
|
+
## 0.8.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- b50190d: Add a `Container` component: a centered, max-width content wrapper equivalent to MUI `Container`. It is a thin alias over `Page.Block` with gutters enabled by default and a MUI-familiar `maxWidth` prop (`xs` | `sm` | `md` | `lg` | `xl` | `false`).
|
|
8
|
+
- 27ef468: Add `FilterEditor`, the criteria tree of the web filter builder.
|
|
9
|
+
|
|
10
|
+
`FilterEditor` renders a nested criteria tree, matching the advanced filter
|
|
11
|
+
editor in the Windows client: groups can be nested to any depth and each group
|
|
12
|
+
chooses whether its items are combined with `and` or `or`. An empty filter is a
|
|
13
|
+
single `and` group. Every group ends with
|
|
14
|
+
a blank row, so a condition is added just by picking a field. Value editors are
|
|
15
|
+
chosen from the field type and the operator's arity, and cover text, number and
|
|
16
|
+
date inputs, ranges, value lists and relative dates such as "Today" or "Current
|
|
17
|
+
user". Set `showFilterString` to show the equivalent filter string, which can
|
|
18
|
+
also be edited and parsed back into the tree. A group's conditions are indented
|
|
19
|
+
and boxed below its header so it is clear which group they belong to. On narrow
|
|
20
|
+
viewports, where the parts of a condition wrap onto several lines, conditions are
|
|
21
|
+
separated by extra spacing and a rule, and the group buttons are reduced to
|
|
22
|
+
icons so they no longer overflow.
|
|
23
|
+
|
|
24
|
+
`@olenbetong/appframe-core` gained path-based filter tree helpers —
|
|
25
|
+
`getNodeAtPath`, `updateNodeAtPath`, `insertNodeAtPath`, `removeNodeAtPath`,
|
|
26
|
+
`setGroupMode` and `addGroupAtPath` — replacing the group/item index helpers
|
|
27
|
+
`addExpression`, `updateExpression`, `removeExpression`, `addGroup` and
|
|
28
|
+
`removeGroup`, which only worked on the flat standard layout. `pruneFilter` now
|
|
29
|
+
prunes nested groups, `toFilterTree` normalizes a filter without flattening it,
|
|
30
|
+
and `isStandardLayout` reports whether a filter fits the simplified layout the
|
|
31
|
+
Windows flow editor can render.
|
|
32
|
+
|
|
33
|
+
`Combobox` in `@olenbetong/appframe-ds` gained two related fixes: it now accepts
|
|
34
|
+
`aria-label` for use without a visible label, and the input shows the selected
|
|
35
|
+
option's label on mount and whenever the selection is changed from the outside.
|
|
36
|
+
`Combobox` also no longer renders text one step smaller than a `Textfield` or
|
|
37
|
+
`Select` of the same `data-size`. Designsystemet inputs inherit their font size
|
|
38
|
+
when `data-size` is set, and `data-size` rescales `--ds-font-size-*`, so
|
|
39
|
+
referencing the type scale at all made the text smaller — `Combobox` now
|
|
40
|
+
inherits its font size the same way `Textfield` does, and has a minimum height
|
|
41
|
+
matching `--dsc-input-size` so it lines up exactly with an input beside it.
|
|
42
|
+
|
|
43
|
+
`Combobox` options were also unreadable in dark mode: the hovered and selected
|
|
44
|
+
states referenced `--ds-color-accent-*`, a colour set that does not exist in
|
|
45
|
+
these themes, so every option fell back to a hard-coded light background while
|
|
46
|
+
the text stayed light. The option states now use the contextual `--ds-color-*`
|
|
47
|
+
aliases, and all hard-coded colour fallbacks — which only ever encoded the light
|
|
48
|
+
theme — have been removed from `Combobox` and `AfLookup`.
|
|
49
|
+
|
|
50
|
+
### Patch Changes
|
|
51
|
+
|
|
52
|
+
- b50190d: `Paper` now accepts a `ref`, which is forwarded to the rendered element.
|
|
53
|
+
- 3ff7c84: Depend on the workspace versions of `@olenbetong/appframe-core`, `@olenbetong/appframe-data` and `@olenbetong/appframe-react` instead of the published versions from the catalog, so the package is built and tested against the current source like the other workspace packages.
|
|
54
|
+
- a0df74a: Add the report metadata model, `useReport` and `ReportParameters`.
|
|
55
|
+
|
|
56
|
+
`@olenbetong/appframe-core` gained a pure report model: `Report`,
|
|
57
|
+
`ReportParameter`, `ReportRecordSource` and their raw record counterparts, plus
|
|
58
|
+
`toReport`, `getReportFormats`, `parseRecordSource` and the parameter helpers
|
|
59
|
+
`toReportParameters`, `coerceParameterValue`, `getDefaultParameterValues`,
|
|
60
|
+
`serializeParameters`, `parseParameters` and `restoreParameterValues`. The
|
|
61
|
+
parameters XML is the same `<Parameters Version="1">` document the Windows
|
|
62
|
+
launcher writes, so a filter saved in either client restores in the other. A
|
|
63
|
+
parameter's editor is derived the way the Windows launcher derives it: a lookup
|
|
64
|
+
wins over everything, a fixed item list comes next, and otherwise the data type
|
|
65
|
+
decides. `FormatCSV` enables XLSX and XLS as well, matching the single
|
|
66
|
+
DevExpress export the flag stands for.
|
|
67
|
+
|
|
68
|
+
`@olenbetong/appframe-react` gained `useReport`, which loads a report from
|
|
69
|
+
`sviw_WinClient_MyReports` — so a report that does not come back is one the user
|
|
70
|
+
may not run — merges the `HasTitle`/`HasHeader` bands from
|
|
71
|
+
`sviw_WinClient_ReportsWeb` and the parameters from
|
|
72
|
+
`sviw_WinClient_ReportsParameters`. It returns the filter the report is designed
|
|
73
|
+
with, overridable through an `initialFilter` option the way opening a report
|
|
74
|
+
programmatically overrides it in the Windows client, and the literal parameter
|
|
75
|
+
defaults. Defaults naming a stored procedure are left unset, to be resolved
|
|
76
|
+
server-side. `useParameterOptions` loads the options for a lookup parameter from
|
|
77
|
+
its `afRecordSource`.
|
|
78
|
+
|
|
79
|
+
`@olenbetong/synergi-react` gained `ReportParameters`, the parameter form of the
|
|
80
|
+
report launcher. It renders nothing when a report has no parameters, so the
|
|
81
|
+
launcher can include it unconditionally, and gives each parameter the editor its
|
|
82
|
+
metadata asks for: a lookup combobox, a select, a checkbox, or a text, number or
|
|
83
|
+
date input. Values are coerced to the parameter's type as they are entered.
|
|
84
|
+
|
|
85
|
+
`Combobox` in `@olenbetong/appframe-ds` had no spacing between its label and its
|
|
86
|
+
control: the root is not a `.ds-field`, which is what normally spaces a label,
|
|
87
|
+
description and input apart, so it now does that spacing itself and lines up
|
|
88
|
+
with a `Textfield` or `Select` beside it.
|
|
89
|
+
|
|
90
|
+
- 1bb10de: `Autocomplete` and `Combobox` now open their option list inside the dialog they
|
|
91
|
+
are used in. The list is portalled, and a modal dialog is painted in the top
|
|
92
|
+
layer, so a list portalled to the body ended up behind the dialog backdrop and
|
|
93
|
+
appeared not to open at all.
|
|
94
|
+
- Updated dependencies [448c378]
|
|
95
|
+
- Updated dependencies [54e5344]
|
|
96
|
+
- Updated dependencies [448c378]
|
|
97
|
+
- Updated dependencies [a0df74a]
|
|
98
|
+
- Updated dependencies [7a54755]
|
|
99
|
+
- Updated dependencies [3954bc9]
|
|
100
|
+
- Updated dependencies [0a23d5b]
|
|
101
|
+
- Updated dependencies [3ff7c84]
|
|
102
|
+
- Updated dependencies [ace635e]
|
|
103
|
+
- Updated dependencies [27ef468]
|
|
104
|
+
- @olenbetong/appframe-react@2.1.0
|
|
105
|
+
- @olenbetong/appframe-core@2.12.0
|
|
106
|
+
- @olenbetong/appframe-data@1.6.0
|
|
107
|
+
|
|
108
|
+
## 0.7.0
|
|
109
|
+
|
|
110
|
+
### Minor Changes
|
|
111
|
+
|
|
112
|
+
- 52e2e10: Add `Autocomplete` and `Combobox`, built on the Base UI components of the same name and styled with Designsystemet tokens. Both expose an API close to MUI's `Autocomplete` (`options`, `value`/`onChange`, `inputValue`/`onInputChange`, `getOptionLabel`, `isOptionEqualToValue`, `filterOptions`, `renderOption`, `groupBy`, `multiple`), plus Designsystemet field props (`label`, `description`, `error`, `data-size`). Use `Combobox` when the user must pick one of the options and `Autocomplete` when free text is allowed (`freeSolo`). Multiple selection renders the selected options as chips above the input, like Designsystemet's `Suggestion`. `createFilterOptions` is exported for custom filtering.
|
|
113
|
+
|
|
114
|
+
**Breaking:** the data object bound `Combobox` used internally by `AfLookup` is renamed to `LookupCombobox` (`ComboboxProps` → `LookupComboboxProps`) to free up the `Combobox` name.
|
|
115
|
+
|
|
116
|
+
- 77e96aa: Add `SortableItemList`, an `ItemList` variant bound to a data object where items can be reordered by dragging or through a menu on the drag handle. The building blocks are exported as well (`useSortableList`, `useSortableItem`, `useSortableContainer`, `DropIndicator`, `reorderSortOrder`, `getNextSortOrder`, `ensureNonZeroSortOrder`) for lists that need custom item markup.
|
|
117
|
+
|
|
3
118
|
## 0.6.0
|
|
4
119
|
|
|
5
120
|
### Minor Changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@olenbetong/appframe-ds",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "Components that bind Designsystemet components to Appframe data objects",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"types": "./es/index.d.ts",
|
|
@@ -23,18 +23,21 @@
|
|
|
23
23
|
"react-router": ">=8.3.0"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@
|
|
27
|
-
"@
|
|
28
|
-
"@
|
|
29
|
-
"
|
|
26
|
+
"@atlaskit/pragmatic-drag-and-drop": "2.0.2",
|
|
27
|
+
"@atlaskit/pragmatic-drag-and-drop-hitbox": "2.0.0",
|
|
28
|
+
"@base-ui/react": "1.7.0",
|
|
29
|
+
"clsx": "^2.1.1",
|
|
30
30
|
"react-virtualized-auto-sizer": "^1.0.26",
|
|
31
|
-
"
|
|
31
|
+
"react-window": "^1.8.11",
|
|
32
|
+
"@olenbetong/appframe-data": "1.6.0",
|
|
33
|
+
"@olenbetong/appframe-core": "2.12.0",
|
|
34
|
+
"@olenbetong/appframe-react": "2.1.0"
|
|
32
35
|
},
|
|
33
36
|
"devDependencies": {
|
|
34
37
|
"@digdir/designsystemet-react": "1.18.0",
|
|
35
38
|
"@navikt/aksel-icons": "8.16.1",
|
|
36
|
-
"@types/react": "19.2.
|
|
37
|
-
"@types/react-dom": "19.2.
|
|
39
|
+
"@types/react": "19.2.18",
|
|
40
|
+
"@types/react-dom": "19.2.4",
|
|
38
41
|
"@types/react-window": "^1.8.8",
|
|
39
42
|
"react": "19.2.8",
|
|
40
43
|
"react-dom": "19.2.8",
|
|
@@ -42,7 +45,6 @@
|
|
|
42
45
|
"react-router": "8.3.0",
|
|
43
46
|
"typescript": "7.0.2"
|
|
44
47
|
},
|
|
45
|
-
"optionalDependencies": {},
|
|
46
48
|
"scripts": {
|
|
47
49
|
"build": "pnpm run build:tsc && pnpm run build:css",
|
|
48
50
|
"build:tsc": "node --no-warnings ../../scripts/clean.ts ./es tsconfig.build.tsbuildinfo && tsc -p tsconfig.build.json",
|
package/scripts/copyAssets.mjs
CHANGED
|
@@ -3,7 +3,7 @@ import { cpSync, mkdirSync } from "node:fs";
|
|
|
3
3
|
const files = [
|
|
4
4
|
["src/AfLookup/Lookup.css", "es/AfLookup/Lookup.css"],
|
|
5
5
|
["src/AfLookup/LookupGrid.css", "es/AfLookup/LookupGrid.css"],
|
|
6
|
-
["src/AfLookup/
|
|
6
|
+
["src/AfLookup/LookupCombobox.css", "es/AfLookup/LookupCombobox.css"],
|
|
7
7
|
["src/layout/AppMenu.css", "es/layout/AppMenu.css"],
|
|
8
8
|
["src/layout/AppMenuItem.css", "es/layout/AppMenuItem.css"],
|
|
9
9
|
["src/layout/AppMenuSubheader.css", "es/layout/AppMenuSubheader.css"],
|
|
@@ -19,6 +19,9 @@ const files = [
|
|
|
19
19
|
["src/input/InputAdornments.css", "es/input/InputAdornments.css"],
|
|
20
20
|
["src/paper/Paper.css", "es/paper/Paper.css"],
|
|
21
21
|
["src/progress/LinearProgress.css", "es/progress/LinearProgress.css"],
|
|
22
|
+
["src/sortable/DropIndicator.css", "es/sortable/DropIndicator.css"],
|
|
23
|
+
["src/sortable/SortableItemList.css", "es/sortable/SortableItemList.css"],
|
|
24
|
+
["src/autocomplete/Autocomplete.css", "es/autocomplete/Autocomplete.css"],
|
|
22
25
|
];
|
|
23
26
|
|
|
24
27
|
mkdirSync("es/layout", { recursive: true });
|
|
@@ -27,6 +30,8 @@ mkdirSync("es/list", { recursive: true });
|
|
|
27
30
|
mkdirSync("es/page", { recursive: true });
|
|
28
31
|
mkdirSync("es/paper", { recursive: true });
|
|
29
32
|
mkdirSync("es/progress", { recursive: true });
|
|
33
|
+
mkdirSync("es/sortable", { recursive: true });
|
|
34
|
+
mkdirSync("es/autocomplete", { recursive: true });
|
|
30
35
|
|
|
31
36
|
for (let [src, dest] of files) {
|
|
32
37
|
cpSync(src, dest);
|
package/src/AfLookup/Lookup.tsx
CHANGED
|
@@ -9,9 +9,9 @@ import clsx from "clsx";
|
|
|
9
9
|
import { useRef } from "react";
|
|
10
10
|
import { createPortal } from "react-dom";
|
|
11
11
|
|
|
12
|
-
import {
|
|
12
|
+
import { LookupCombobox, type LookupComboboxProps } from "./LookupCombobox.js";
|
|
13
13
|
|
|
14
|
-
export type AfLookupProps<T extends Record<string, unknown>> = DistributiveOmit<
|
|
14
|
+
export type AfLookupProps<T extends Record<string, unknown>> = DistributiveOmit<LookupComboboxProps<T>, "value"> & {
|
|
15
15
|
/**
|
|
16
16
|
* Allows the user to enter a value manually that might not be in the list.
|
|
17
17
|
*/
|
|
@@ -26,7 +26,7 @@ export type AfLookupProps<T extends Record<string, unknown>> = DistributiveOmit<
|
|
|
26
26
|
};
|
|
27
27
|
};
|
|
28
28
|
|
|
29
|
-
export type LookupProps<T extends Record<string, unknown>> = DistributiveOmit<
|
|
29
|
+
export type LookupProps<T extends Record<string, unknown>> = DistributiveOmit<LookupComboboxProps<T>, "value"> & {
|
|
30
30
|
slotProps?: {
|
|
31
31
|
dialog?: Partial<React.HTMLProps<HTMLDialogElement>>;
|
|
32
32
|
};
|
|
@@ -70,7 +70,7 @@ export function useLookup<T extends Record<string, unknown>>({
|
|
|
70
70
|
style={{ positionAnchor: anchorName, ...slotProps?.dialog?.style } as React.CSSProperties}
|
|
71
71
|
>
|
|
72
72
|
{open && (
|
|
73
|
-
<
|
|
73
|
+
<LookupCombobox
|
|
74
74
|
{...props}
|
|
75
75
|
initialSearchValue={initialSearchValueRef.current}
|
|
76
76
|
onClose={() => dialogRef.current?.close()}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import "./
|
|
1
|
+
import "./LookupCombobox.css";
|
|
2
2
|
|
|
3
3
|
import { Button, Textfield } from "@digdir/designsystemet-react";
|
|
4
4
|
import { getLocalizedString } from "@olenbetong/appframe-core";
|
|
@@ -11,7 +11,7 @@ import { VariableSizeList as VirtualList } from "react-window";
|
|
|
11
11
|
|
|
12
12
|
import { LookupGrid } from "./LookupGrid.js";
|
|
13
13
|
|
|
14
|
-
export interface
|
|
14
|
+
export interface LookupComboboxBaseProps<T extends Record<string, unknown>> {
|
|
15
15
|
nullable?: boolean;
|
|
16
16
|
dataObject: DataObject<T>;
|
|
17
17
|
uniqueIdField?: string;
|
|
@@ -22,7 +22,7 @@ export interface ComboboxBaseProps<T extends Record<string, unknown>> {
|
|
|
22
22
|
onClose?: () => void;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
export interface
|
|
25
|
+
export interface LookupComboboxListProps<T extends Record<string, unknown>> extends LookupComboboxBaseProps<T> {
|
|
26
26
|
/**
|
|
27
27
|
* Renders the popup content as a list (default).
|
|
28
28
|
*/
|
|
@@ -30,7 +30,7 @@ export interface ComboboxListProps<T extends Record<string, unknown>> extends Co
|
|
|
30
30
|
renderItem: (props: React.ComponentPropsWithRef<"li"> & { item: T }) => React.ReactNode;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
export interface
|
|
33
|
+
export interface LookupComboboxGridProps<T extends Record<string, unknown>> extends LookupComboboxBaseProps<T> {
|
|
34
34
|
/**
|
|
35
35
|
* Renders the popup content as a grid with a header row and one column per
|
|
36
36
|
* entry in `columns`.
|
|
@@ -39,9 +39,9 @@ export interface ComboboxGridProps<T extends Record<string, unknown>> extends Co
|
|
|
39
39
|
columns: LookupColumn<T>[];
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
export type
|
|
42
|
+
export type LookupComboboxProps<T extends Record<string, unknown>> = LookupComboboxListProps<T> | LookupComboboxGridProps<T>;
|
|
43
43
|
|
|
44
|
-
export function
|
|
44
|
+
export function LookupCombobox<T extends Record<string, unknown>>(props: LookupComboboxProps<T>) {
|
|
45
45
|
let {
|
|
46
46
|
nullable,
|
|
47
47
|
dataObject,
|
|
@@ -52,7 +52,7 @@ export function Combobox<T extends Record<string, unknown>>(props: ComboboxProps
|
|
|
52
52
|
onClose,
|
|
53
53
|
} = props;
|
|
54
54
|
let isGrid = props.variant === "grid";
|
|
55
|
-
let columns = isGrid ? (props as
|
|
55
|
+
let columns = isGrid ? (props as LookupComboboxGridProps<T>).columns : undefined;
|
|
56
56
|
let listRef = useRef<VirtualList>(null);
|
|
57
57
|
|
|
58
58
|
let [isDesktop, setIsDesktop] = useState(
|
|
@@ -121,7 +121,7 @@ export function Combobox<T extends Record<string, unknown>>(props: ComboboxProps
|
|
|
121
121
|
let item = data[index];
|
|
122
122
|
let itemId = getItemId(item);
|
|
123
123
|
let active = index === currentIndex;
|
|
124
|
-
let ItemComponent = (props as
|
|
124
|
+
let ItemComponent = (props as LookupComboboxListProps<T>).renderItem;
|
|
125
125
|
|
|
126
126
|
return (
|
|
127
127
|
<ItemComponent
|
|
@@ -8,11 +8,11 @@ import type { DistributiveOmit } from "@olenbetong/appframe-react";
|
|
|
8
8
|
import clsx from "clsx";
|
|
9
9
|
import type React from "react";
|
|
10
10
|
|
|
11
|
-
import type {
|
|
11
|
+
import type { LookupComboboxProps } from "./LookupCombobox.js";
|
|
12
12
|
import { useLookup } from "./Lookup.js";
|
|
13
13
|
|
|
14
14
|
export type AfLookupEditProps<T extends Record<string, unknown>> = DistributiveOmit<
|
|
15
|
-
|
|
15
|
+
LookupComboboxProps<T>,
|
|
16
16
|
"value" | "onChange"
|
|
17
17
|
> & {
|
|
18
18
|
/**
|
package/src/AfLookup/index.ts
CHANGED
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
.ObAutocomplete-root {
|
|
2
|
+
--obAutocomplete-padding: 0.25rem;
|
|
3
|
+
--obAutocomplete-gap: 0.25rem;
|
|
4
|
+
|
|
5
|
+
display: inline-grid;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/*
|
|
9
|
+
* The root is not a `.ds-field`, so it has to space its label, description,
|
|
10
|
+
* control and validation message itself. Mirrors `.ds-field > * + *`, including
|
|
11
|
+
* the description sitting tight under the label.
|
|
12
|
+
*/
|
|
13
|
+
.ObAutocomplete-root > * + * {
|
|
14
|
+
margin-block-start: var(--ds-size-2);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.ObAutocomplete-root > [data-field="description"] {
|
|
18
|
+
margin-block-start: 0;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.ObAutocomplete-root[data-full-width="true"] {
|
|
22
|
+
display: grid;
|
|
23
|
+
inline-size: 100%;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/*
|
|
27
|
+
* Font size is deliberately never set: `ds-input` inherits its font size when a
|
|
28
|
+
* `data-size` attribute is present, so referencing `--ds-font-size-*` here — which
|
|
29
|
+
* `data-size` rescales — would render the text smaller than a `Textfield` or
|
|
30
|
+
* `Select` of the same size. Only the padding varies per size.
|
|
31
|
+
*/
|
|
32
|
+
.ObAutocomplete-root[data-size="xs"] {
|
|
33
|
+
--obAutocomplete-padding: 0.125rem;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.ObAutocomplete-root[data-size="sm"] {
|
|
37
|
+
--obAutocomplete-padding: 0.25rem;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.ObAutocomplete-root[data-size="md"] {
|
|
41
|
+
--obAutocomplete-padding: 0.25rem;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.ObAutocomplete-root[data-size="lg"] {
|
|
45
|
+
--obAutocomplete-padding: 0.375rem;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/*
|
|
49
|
+
* The control is the visual input: it owns the border, background, hover and
|
|
50
|
+
* focus styling, so the chips, the bare input and the buttons read as a single
|
|
51
|
+
* control. Mirrors how Designsystemet's Suggestion renders selected chips
|
|
52
|
+
* above the input.
|
|
53
|
+
*/
|
|
54
|
+
.ObAutocomplete-control {
|
|
55
|
+
display: flex;
|
|
56
|
+
flex-direction: column;
|
|
57
|
+
justify-content: center;
|
|
58
|
+
box-sizing: border-box;
|
|
59
|
+
gap: var(--obAutocomplete-gap);
|
|
60
|
+
padding: var(--obAutocomplete-padding);
|
|
61
|
+
inline-size: 100%;
|
|
62
|
+
/* Matches `--dsc-input-size`, so the control lines up with a Textfield. */
|
|
63
|
+
min-block-size: var(--ds-size-12);
|
|
64
|
+
color: var(--ds-color-neutral-text-default);
|
|
65
|
+
background-color: var(--ds-color-neutral-surface-default);
|
|
66
|
+
border: var(--ds-border-width-default) solid var(--ds-color-neutral-border-default);
|
|
67
|
+
border-radius: var(--ds-border-radius-md);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.ObAutocomplete-control:hover:not(:focus-within) {
|
|
71
|
+
border-color: var(--ds-color-neutral-border-strong);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.ObAutocomplete-control:has(.ObAutocomplete-input:focus-visible) {
|
|
75
|
+
box-shadow: var(--dsc-focus-boxShadow);
|
|
76
|
+
outline: var(--dsc-focus-outline);
|
|
77
|
+
outline-offset: var(--ds-border-width-focus);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.ObAutocomplete-control[data-invalid="true"] {
|
|
81
|
+
border-color: var(--ds-color-danger-border-default);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.ObAutocomplete-control[data-disabled="true"] {
|
|
85
|
+
cursor: not-allowed;
|
|
86
|
+
opacity: var(--ds-opacity-disabled);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.ObAutocomplete-chips {
|
|
90
|
+
display: flex;
|
|
91
|
+
flex-wrap: wrap;
|
|
92
|
+
gap: var(--obAutocomplete-gap);
|
|
93
|
+
padding: var(--obAutocomplete-padding);
|
|
94
|
+
padding-block-end: 0;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.ObAutocomplete-inputGroup {
|
|
98
|
+
display: flex;
|
|
99
|
+
align-items: center;
|
|
100
|
+
gap: var(--obAutocomplete-gap);
|
|
101
|
+
min-inline-size: 0;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.ObAutocomplete-input {
|
|
105
|
+
flex: 1;
|
|
106
|
+
min-inline-size: 0;
|
|
107
|
+
padding: var(--ds-size-1);
|
|
108
|
+
font: inherit;
|
|
109
|
+
color: inherit;
|
|
110
|
+
background: transparent;
|
|
111
|
+
border: none;
|
|
112
|
+
outline: none;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.ObAutocomplete-input:disabled {
|
|
116
|
+
cursor: not-allowed;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.ObAutocomplete-button {
|
|
120
|
+
display: inline-flex;
|
|
121
|
+
align-items: center;
|
|
122
|
+
justify-content: center;
|
|
123
|
+
flex: none;
|
|
124
|
+
padding: var(--ds-size-1);
|
|
125
|
+
color: inherit;
|
|
126
|
+
background: transparent;
|
|
127
|
+
border: none;
|
|
128
|
+
border-radius: var(--ds-border-radius-sm);
|
|
129
|
+
cursor: pointer;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.ObAutocomplete-button:hover:not(:disabled) {
|
|
133
|
+
background-color: var(--ds-color-neutral-surface-hover);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
.ObAutocomplete-button:disabled {
|
|
137
|
+
cursor: not-allowed;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.ObAutocomplete-button[data-visible="false"] {
|
|
141
|
+
display: none;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.ObAutocomplete-trigger[data-popup-open] svg {
|
|
145
|
+
rotate: 180deg;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.ObAutocomplete-spinner {
|
|
149
|
+
flex: none;
|
|
150
|
+
display: inline-flex;
|
|
151
|
+
align-items: center;
|
|
152
|
+
padding-inline: var(--ds-size-1);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.ObAutocomplete-positioner {
|
|
156
|
+
z-index: 1500;
|
|
157
|
+
/* Base UI renders the positioner even while closed */
|
|
158
|
+
outline: none;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
.ObAutocomplete-popup {
|
|
162
|
+
box-sizing: border-box;
|
|
163
|
+
inline-size: var(--anchor-width);
|
|
164
|
+
max-block-size: min(24rem, var(--available-height));
|
|
165
|
+
overflow-y: auto;
|
|
166
|
+
overscroll-behavior: contain;
|
|
167
|
+
padding: var(--ds-size-1);
|
|
168
|
+
color: var(--ds-color-neutral-text-default);
|
|
169
|
+
background-color: var(--ds-color-neutral-surface-default);
|
|
170
|
+
border: var(--ds-border-width-default) solid var(--ds-color-neutral-border-subtle);
|
|
171
|
+
border-radius: var(--ds-border-radius-md);
|
|
172
|
+
box-shadow: var(--ds-shadow-md);
|
|
173
|
+
transform-origin: var(--transform-origin);
|
|
174
|
+
transition:
|
|
175
|
+
opacity 0.12s ease-out,
|
|
176
|
+
scale 0.12s ease-out;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
.ObAutocomplete-popup[data-starting-style],
|
|
180
|
+
.ObAutocomplete-popup[data-ending-style] {
|
|
181
|
+
opacity: 0;
|
|
182
|
+
scale: 0.98;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
.ObAutocomplete-list {
|
|
186
|
+
display: flex;
|
|
187
|
+
flex-direction: column;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
.ObAutocomplete-item {
|
|
191
|
+
display: flex;
|
|
192
|
+
align-items: center;
|
|
193
|
+
gap: var(--ds-size-2);
|
|
194
|
+
padding: var(--ds-size-2);
|
|
195
|
+
border-radius: var(--ds-border-radius-sm);
|
|
196
|
+
cursor: default;
|
|
197
|
+
user-select: none;
|
|
198
|
+
scroll-margin-block: var(--ds-size-1);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/*
|
|
202
|
+
* The `accent` colour set does not exist in every Designsystemet theme, so the
|
|
203
|
+
* option states use the contextual `--ds-color-*` aliases — the same tokens
|
|
204
|
+
* Designsystemet's own Suggestion list uses.
|
|
205
|
+
*/
|
|
206
|
+
.ObAutocomplete-item[data-highlighted] {
|
|
207
|
+
color: var(--ds-color-text-default);
|
|
208
|
+
background-color: var(--ds-color-surface-hover);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
.ObAutocomplete-item[data-selected] {
|
|
212
|
+
font-weight: var(--ds-font-weight-medium);
|
|
213
|
+
color: var(--ds-color-text-default);
|
|
214
|
+
background-color: var(--ds-color-surface-tinted);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
.ObAutocomplete-item[data-selected][data-highlighted] {
|
|
218
|
+
background-color: var(--ds-color-surface-active);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
.ObAutocomplete-item[data-disabled] {
|
|
222
|
+
cursor: not-allowed;
|
|
223
|
+
opacity: var(--ds-opacity-disabled);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
.ObAutocomplete-itemIndicator {
|
|
227
|
+
display: inline-flex;
|
|
228
|
+
flex: none;
|
|
229
|
+
inline-size: 1em;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
.ObAutocomplete-groupLabel {
|
|
233
|
+
padding: var(--ds-size-2);
|
|
234
|
+
font-size: var(--ds-font-size-1);
|
|
235
|
+
font-weight: var(--ds-font-weight-semibold);
|
|
236
|
+
color: var(--ds-color-neutral-text-subtle);
|
|
237
|
+
text-transform: uppercase;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
.ObAutocomplete-empty:not(:empty) {
|
|
241
|
+
padding: var(--ds-size-2);
|
|
242
|
+
color: var(--ds-color-neutral-text-subtle);
|
|
243
|
+
}
|