@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 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.6.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
- "@olenbetong/appframe-core": "2.11.10",
27
- "@olenbetong/appframe-data": "1.5.0",
28
- "@olenbetong/appframe-react": "2.0.0",
29
- "react-window": "^1.8.11",
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
- "clsx": "^2.1.1"
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.17",
37
- "@types/react-dom": "19.2.3",
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",
@@ -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/Combobox.css", "es/AfLookup/Combobox.css"],
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);
@@ -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 { Combobox, type ComboboxProps } from "./Combobox.js";
12
+ import { LookupCombobox, type LookupComboboxProps } from "./LookupCombobox.js";
13
13
 
14
- export type AfLookupProps<T extends Record<string, unknown>> = DistributiveOmit<ComboboxProps<T>, "value"> & {
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<ComboboxProps<T>, "value"> & {
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
- <Combobox
73
+ <LookupCombobox
74
74
  {...props}
75
75
  initialSearchValue={initialSearchValueRef.current}
76
76
  onClose={() => dialogRef.current?.close()}
@@ -64,7 +64,7 @@
64
64
  left: -100%;
65
65
  height: 100%;
66
66
  width: 50%;
67
- background-color: var(--ds-color-accent-base-default, #0062ba);
67
+ background-color: var(--ds-color-base-default);
68
68
  animation: ObCombobox-progress-anim 1.5s ease-in-out infinite;
69
69
  }
70
70
 
@@ -1,4 +1,4 @@
1
- import "./Combobox.css";
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 ComboboxBaseProps<T extends Record<string, unknown>> {
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 ComboboxListProps<T extends Record<string, unknown>> extends ComboboxBaseProps<T> {
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 ComboboxGridProps<T extends Record<string, unknown>> extends ComboboxBaseProps<T> {
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 ComboboxProps<T extends Record<string, unknown>> = ComboboxListProps<T> | ComboboxGridProps<T>;
42
+ export type LookupComboboxProps<T extends Record<string, unknown>> = LookupComboboxListProps<T> | LookupComboboxGridProps<T>;
43
43
 
44
- export function Combobox<T extends Record<string, unknown>>(props: ComboboxProps<T>) {
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 ComboboxGridProps<T>).columns : undefined;
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 ComboboxListProps<T>).renderItem;
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 { ComboboxProps } from "./Combobox.js";
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
- ComboboxProps<T>,
15
+ LookupComboboxProps<T>,
16
16
  "value" | "onChange"
17
17
  > & {
18
18
  /**
@@ -1,5 +1,5 @@
1
1
  export * from "./BoundLookup.js";
2
- export * from "./Combobox.js";
2
+ export * from "./LookupCombobox.js";
3
3
  export * from "./Lookup.js";
4
4
  export * from "./LookupEdit.js";
5
5
  export * from "./LookupGrid.js";
@@ -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
+ }