@lumiose/ui 0.0.0-stage → 0.1.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 (149) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +53 -2
  3. package/dist/accordion/accordion.css +20 -0
  4. package/dist/accordion/accordion.script.ts +66 -0
  5. package/dist/accordion/accordion.tsx +288 -0
  6. package/dist/animation/animation.ts +34 -0
  7. package/dist/autocomplete/autocomplete.tsx +239 -0
  8. package/dist/avatar/avatar.css +54 -0
  9. package/dist/avatar/avatar.script.ts +96 -0
  10. package/dist/avatar/avatar.tsx +110 -0
  11. package/dist/button/button.script.ts +35 -0
  12. package/dist/button/button.tsx +40 -0
  13. package/dist/checkbox/checkbox.script.ts +225 -0
  14. package/dist/checkbox/checkbox.tsx +156 -0
  15. package/dist/checkbox/context.ts +17 -0
  16. package/dist/checkbox-group/checkbox-group.tsx +111 -0
  17. package/dist/collapsible/collapsible.css +12 -0
  18. package/dist/collapsible/collapsible.script.ts +7 -0
  19. package/dist/collapsible/collapsible.tsx +153 -0
  20. package/dist/collapsible/panel.ts +115 -0
  21. package/dist/combobox/combobox.css +37 -0
  22. package/dist/combobox/combobox.script.ts +974 -0
  23. package/dist/combobox/combobox.tsx +556 -0
  24. package/dist/combobox/context.ts +46 -0
  25. package/dist/context-menu/context-menu.css +16 -0
  26. package/dist/context-menu/context-menu.script.ts +172 -0
  27. package/dist/context-menu/context-menu.tsx +178 -0
  28. package/dist/dialog/dialog.css +35 -0
  29. package/dist/dialog/dialog.script.ts +269 -0
  30. package/dist/dialog/dialog.tsx +214 -0
  31. package/dist/drawer/drawer.css +38 -0
  32. package/dist/drawer/drawer.script.ts +207 -0
  33. package/dist/drawer/drawer.tsx +113 -0
  34. package/dist/field/context.ts +156 -0
  35. package/dist/field/field.css +16 -0
  36. package/dist/field/field.script.ts +422 -0
  37. package/dist/field/field.tsx +465 -0
  38. package/dist/fieldset/fieldset.tsx +41 -0
  39. package/dist/form/form.tsx +50 -0
  40. package/dist/form/native.ts +21 -0
  41. package/dist/form/reset.ts +53 -0
  42. package/dist/index.ts +261 -0
  43. package/dist/input/input.tsx +67 -0
  44. package/dist/layer/layer.ts +191 -0
  45. package/dist/locale/digits.ts +21 -0
  46. package/dist/menu/menu.css +22 -0
  47. package/dist/menu/menu.script.ts +591 -0
  48. package/dist/menu/menu.tsx +520 -0
  49. package/dist/menubar/menubar.css +16 -0
  50. package/dist/menubar/menubar.script.ts +195 -0
  51. package/dist/menubar/menubar.tsx +67 -0
  52. package/dist/meter/meter.tsx +134 -0
  53. package/dist/navigation-menu/navigation-menu.css +14 -0
  54. package/dist/navigation-menu/navigation-menu.script.ts +466 -0
  55. package/dist/navigation-menu/navigation-menu.tsx +266 -0
  56. package/dist/number-field/format.ts +70 -0
  57. package/dist/number-field/number-field.css +38 -0
  58. package/dist/number-field/number-field.script.ts +490 -0
  59. package/dist/number-field/number-field.tsx +403 -0
  60. package/dist/otp-field/otp-field.css +45 -0
  61. package/dist/otp-field/otp-field.script.ts +330 -0
  62. package/dist/otp-field/otp-field.tsx +320 -0
  63. package/dist/placement/place.ts +148 -0
  64. package/dist/placement/placement.css +155 -0
  65. package/dist/placement/placement.ts +60 -0
  66. package/dist/popover/hover.ts +178 -0
  67. package/dist/popover/popover.css +20 -0
  68. package/dist/popover/popover.script.ts +256 -0
  69. package/dist/popover/popover.tsx +317 -0
  70. package/dist/preview-card/preview-card.css +7 -0
  71. package/dist/preview-card/preview-card.script.ts +136 -0
  72. package/dist/preview-card/preview-card.tsx +128 -0
  73. package/dist/progress/progress.tsx +155 -0
  74. package/dist/progress/value.ts +23 -0
  75. package/dist/radio-group/radio-group.tsx +156 -0
  76. package/dist/scroll-area/scroll-area.css +72 -0
  77. package/dist/scroll-area/scroll-area.script.ts +281 -0
  78. package/dist/scroll-area/scroll-area.tsx +118 -0
  79. package/dist/select/select.css +25 -0
  80. package/dist/select/select.tsx +171 -0
  81. package/dist/separator/separator.css +12 -0
  82. package/dist/separator/separator.tsx +38 -0
  83. package/dist/slider/format.ts +33 -0
  84. package/dist/slider/slider.css +93 -0
  85. package/dist/slider/slider.script.ts +355 -0
  86. package/dist/slider/slider.tsx +374 -0
  87. package/dist/styles.css +30 -0
  88. package/dist/switch/switch.tsx +92 -0
  89. package/dist/tabs/tabs.css +54 -0
  90. package/dist/tabs/tabs.script.ts +225 -0
  91. package/dist/tabs/tabs.tsx +272 -0
  92. package/dist/themes/default.css +2111 -0
  93. package/dist/toast/command.ts +3 -0
  94. package/dist/toast/toast.css +68 -0
  95. package/dist/toast/toast.script.ts +516 -0
  96. package/dist/toast/toast.tsx +432 -0
  97. package/dist/toggle/context.ts +16 -0
  98. package/dist/toggle/toggle.tsx +99 -0
  99. package/dist/toggle-group/toggle-group.script.ts +166 -0
  100. package/dist/toggle-group/toggle-group.tsx +117 -0
  101. package/dist/toolbar/context.ts +18 -0
  102. package/dist/toolbar/toolbar.css +12 -0
  103. package/dist/toolbar/toolbar.script.ts +123 -0
  104. package/dist/toolbar/toolbar.tsx +180 -0
  105. package/dist/tooltip/tooltip.css +8 -0
  106. package/dist/tooltip/tooltip.script.ts +222 -0
  107. package/dist/tooltip/tooltip.tsx +149 -0
  108. package/docs/README.md +174 -0
  109. package/docs/accordion.md +208 -0
  110. package/docs/autocomplete.md +161 -0
  111. package/docs/avatar.md +198 -0
  112. package/docs/button.md +98 -0
  113. package/docs/checkbox-group.md +112 -0
  114. package/docs/checkbox.md +133 -0
  115. package/docs/collapsible.md +165 -0
  116. package/docs/combobox.md +369 -0
  117. package/docs/context-menu.md +197 -0
  118. package/docs/dialog.md +256 -0
  119. package/docs/drawer.md +204 -0
  120. package/docs/field.md +295 -0
  121. package/docs/fieldset.md +92 -0
  122. package/docs/form.md +131 -0
  123. package/docs/input.md +87 -0
  124. package/docs/installation.md +65 -0
  125. package/docs/llms-full.txt +6923 -0
  126. package/docs/llms.txt +53 -0
  127. package/docs/menu.md +365 -0
  128. package/docs/menubar.md +186 -0
  129. package/docs/meter.md +103 -0
  130. package/docs/navigation-menu.md +257 -0
  131. package/docs/number-field.md +181 -0
  132. package/docs/otp-field.md +160 -0
  133. package/docs/popover.md +271 -0
  134. package/docs/preview-card.md +130 -0
  135. package/docs/progress.md +115 -0
  136. package/docs/radio-group.md +127 -0
  137. package/docs/scroll-area.md +147 -0
  138. package/docs/select.md +236 -0
  139. package/docs/separator.md +116 -0
  140. package/docs/slider.md +184 -0
  141. package/docs/switch.md +98 -0
  142. package/docs/tabs.md +254 -0
  143. package/docs/theming.md +132 -0
  144. package/docs/toast.md +311 -0
  145. package/docs/toggle-group.md +119 -0
  146. package/docs/toggle.md +94 -0
  147. package/docs/toolbar.md +138 -0
  148. package/docs/tooltip.md +128 -0
  149. package/package.json +102 -3
@@ -0,0 +1,266 @@
1
+ // Navigation Menu (decision-62, TASK-380): site navigation whose items open panels of links, after
2
+ // Base UI's NavigationMenu and the WAI-ARIA disclosure navigation pattern. Root is a `<nav>`
3
+ // landmark (a `<div>` when nested in another menu's panel) around a list; each item's trigger
4
+ // opens its content through `popovertarget`: a `popover="auto"` placed under the trigger by CSS
5
+ // anchor positioning. So without JavaScript the links work, a panel opens on a click, one at a
6
+ // time, and closes on Escape or a click outside. `<lu-navigation-menu>`
7
+ // (./navigation-menu.script.ts) adds hover intent, the arrow keys, closing when focus leaves,
8
+ // `value` and the activation direction. Base UI's Viewport, Positioner, Portal, Backdrop and Arrow
9
+ // are left out: the top layer and anchor positioning place each panel, and without a portal the
10
+ // panels cannot share one viewport.
11
+ import { createContext, Dynamic, effect, onMount, ref, untrack, useContext, useId } from "lumiose";
12
+ import { anchorName, placement, withStyle, type PlacementProps } from "../placement/placement.ts";
13
+ import navigationMenuScript from "./navigation-menu.script.ts?client";
14
+ import type {
15
+ NavigationMenuElement,
16
+ NavigationMenuValueChangeDetail,
17
+ } from "./navigation-menu.script.ts";
18
+
19
+ declare module "lumiose/jsx-runtime" {
20
+ namespace JSX {
21
+ interface IntrinsicElements {
22
+ /**
23
+ * NavigationMenu.Root's element: it opens and closes its items' contents. Property `value`;
24
+ * event `valuechange` (`detail.value`).
25
+ */
26
+ "lu-navigation-menu": JSX.HTMLProps<NavigationMenuElement> & {
27
+ "data-delay"?: number;
28
+ "data-close-delay"?: number;
29
+ };
30
+ }
31
+ }
32
+ }
33
+
34
+ /** Which way a navigation menu's items run. */
35
+ export type NavigationMenuOrientation = "horizontal" | "vertical";
36
+
37
+ /**
38
+ * `NavigationMenu.Root` renders a `<nav>` (a `<div>` inside another navigation menu's content)
39
+ * inside `<lu-navigation-menu>`. `bind:value={signal}` stands in for `value` and `onValueChange`.
40
+ */
41
+ export type NavigationMenuRootProps = Omit<JSX.IntrinsicElements["nav"], symbol> & {
42
+ /**
43
+ * The `value` of the item whose content is open, or `null` for none; opens or closes them when it
44
+ * changes, in the browser. Pair it with `onValueChange`; leave both out and the menu keeps its own
45
+ * state. Server HTML always starts closed.
46
+ */
47
+ value?: string | null;
48
+ /** The item open at first, in the browser, when `value` is left out. */
49
+ defaultValue?: string | null;
50
+ /** Called after an item's content opens (its `value`) or the open one closes (`null`). */
51
+ onValueChange?: (value: string | null) => void;
52
+ /** How long the pointer rests on a trigger before its content opens, in milliseconds. Default `50`. */
53
+ delay?: number;
54
+ /** How long after the pointer leaves an item its content closes, in milliseconds. Default `50`. */
55
+ closeDelay?: number;
56
+ /**
57
+ * Which way the items run: `"vertical"` moves between them with Arrow Up and Down, and places
58
+ * contents at the inline end. Default `"horizontal"`.
59
+ */
60
+ orientation?: NavigationMenuOrientation;
61
+ };
62
+
63
+ /** A `<ul>` of the menu's items. */
64
+ export type NavigationMenuListProps = JSX.IntrinsicElements["ul"];
65
+
66
+ /** An `<li>`: a trigger with its content, or a link. */
67
+ export type NavigationMenuItemProps = JSX.IntrinsicElements["li"] & {
68
+ /** The value `Root`'s `value` names this item by. Default: a generated id. */
69
+ value?: string;
70
+ };
71
+
72
+ /** The `<button popovertarget="…">` that opens and closes its item's content. */
73
+ export type NavigationMenuTriggerProps = JSX.IntrinsicElements["button"];
74
+
75
+ /** A `<span aria-hidden="true">` inside the trigger, such as a chevron. */
76
+ export type NavigationMenuIconProps = JSX.IntrinsicElements["span"];
77
+
78
+ /** The `<div popover="auto">` of an item's links, placed next to its trigger. */
79
+ export type NavigationMenuContentProps = JSX.IntrinsicElements["div"] & PlacementProps;
80
+
81
+ /** An `<a>`: a link in the list or in a content. */
82
+ export type NavigationMenuLinkProps = JSX.IntrinsicElements["a"] & {
83
+ /** The link to the current page: `aria-current="page"` and `data-active`. */
84
+ active?: boolean;
85
+ /** Whether following the link closes the open content. Default `false`. */
86
+ closeOnClick?: boolean;
87
+ };
88
+
89
+ interface RootContextValue {
90
+ orientation: NavigationMenuOrientation;
91
+ }
92
+
93
+ interface ItemContextValue {
94
+ id: string;
95
+ }
96
+
97
+ const RootContext = createContext<RootContextValue>();
98
+ const ItemContext = createContext<ItemContextValue>();
99
+
100
+ function useItem(part: string): ItemContextValue {
101
+ const item = useContext(ItemContext);
102
+ if (!item) throw new Error(`NavigationMenu.${part} must be inside <NavigationMenu.Item>.`);
103
+ return item;
104
+ }
105
+
106
+ function Root({
107
+ value,
108
+ defaultValue,
109
+ onValueChange,
110
+ delay,
111
+ closeDelay,
112
+ orientation,
113
+ children,
114
+ ...rest
115
+ }: NavigationMenuRootProps) {
116
+ // Hover intent, keys and `value` where JavaScript runs; the page loads it once (decision-63).
117
+ navigationMenuScript();
118
+ const nested = useContext(RootContext) !== undefined;
119
+ const host = ref<NavigationMenuElement>();
120
+ const vertical = () => orientation === "vertical";
121
+
122
+ // `value` drives the element; its `valuechange` event reports back, so a signal written by
123
+ // `onValueChange` sees the same value again and nothing loops. A content opened from the server
124
+ // HTML before the island hydrated stays open, and the signal hears of it.
125
+ let mounted = false;
126
+ effect(() => {
127
+ const element = host();
128
+ const wanted = value;
129
+ if (!element?.isConnected || wanted === undefined) return;
130
+ if (!mounted && wanted === null && element.value !== null) {
131
+ untrack(() => onValueChange?.(element.value));
132
+ } else {
133
+ element.value = wanted;
134
+ }
135
+ mounted = true;
136
+ });
137
+ onMount(() => {
138
+ const element = host();
139
+ if (element && value === undefined && defaultValue != null) element.value = defaultValue;
140
+ });
141
+
142
+ const attributes = () => ({
143
+ ...rest,
144
+ "data-orientation": vertical() ? "vertical" : "horizontal",
145
+ });
146
+ return (
147
+ <RootContext value={{ orientation: vertical() ? "vertical" : "horizontal" }}>
148
+ <lu-navigation-menu
149
+ ref={host}
150
+ data-delay={delay}
151
+ data-close-delay={closeDelay}
152
+ data-orientation={vertical() ? "vertical" : "horizontal"}
153
+ on:valuechange={(event: CustomEvent<NavigationMenuValueChangeDetail>) =>
154
+ onValueChange?.(event.detail.value)
155
+ }
156
+ >
157
+ {/* A nested menu is a `<div>`, inside its parent's landmark. */}
158
+ <Dynamic component={nested ? "div" : "nav"} {...attributes()}>
159
+ {children}
160
+ </Dynamic>
161
+ </lu-navigation-menu>
162
+ </RootContext>
163
+ );
164
+ }
165
+
166
+ // Children are rendered explicitly: a spread does not pass them on (TASK-347).
167
+ function List({ children, ...rest }: NavigationMenuListProps) {
168
+ return <ul {...rest}>{children}</ul>;
169
+ }
170
+
171
+ function Item({ value, children, ...rest }: NavigationMenuItemProps) {
172
+ const id = useId();
173
+ return (
174
+ <ItemContext value={{ id }}>
175
+ <li {...rest} data-value={value ?? id}>
176
+ {children}
177
+ </li>
178
+ </ItemContext>
179
+ );
180
+ }
181
+
182
+ function Trigger({ children, style, ...rest }: NavigationMenuTriggerProps) {
183
+ const item = useItem("Trigger");
184
+ return (
185
+ <button
186
+ type="button"
187
+ {...rest}
188
+ // Nothing points at the trigger: a caller's `id` names it (TASK-441 M39).
189
+ id={rest.id ?? `${item.id}-trigger`}
190
+ popovertarget={`${item.id}-content`}
191
+ style={withStyle(style, { "anchor-name": anchorName(item.id) })}
192
+ >
193
+ {children}
194
+ </button>
195
+ );
196
+ }
197
+
198
+ function Icon({ children, ...rest }: NavigationMenuIconProps) {
199
+ return (
200
+ <span aria-hidden="true" {...rest}>
201
+ {children}
202
+ </span>
203
+ );
204
+ }
205
+
206
+ function Content({
207
+ children,
208
+ side,
209
+ align,
210
+ sideOffset,
211
+ alignOffset,
212
+ style,
213
+ ...rest
214
+ }: NavigationMenuContentProps) {
215
+ const item = useItem("Content");
216
+ const root = useContext(RootContext);
217
+ return (
218
+ <div
219
+ {...rest}
220
+ id={`${item.id}-content`}
221
+ popover="auto"
222
+ {...placement(
223
+ { side, align, sideOffset, alignOffset, style },
224
+ item.id,
225
+ root?.orientation === "vertical" ? "inline-end" : "bottom",
226
+ root?.orientation === "vertical" ? "start" : "center",
227
+ )}
228
+ >
229
+ {children}
230
+ </div>
231
+ );
232
+ }
233
+
234
+ function Link({ children, active, closeOnClick, ...rest }: NavigationMenuLinkProps) {
235
+ return (
236
+ <a
237
+ {...rest}
238
+ aria-current={active ? "page" : rest["aria-current"]}
239
+ data-active={active || undefined}
240
+ data-close-on-click={closeOnClick ? "true" : undefined}
241
+ >
242
+ {children}
243
+ </a>
244
+ );
245
+ }
246
+
247
+ /**
248
+ * Site navigation whose items open panels of links, after Base UI's NavigationMenu: Root, List,
249
+ * Item, Trigger, Icon, Content and Link. A `<nav>` landmark whose links work without JavaScript.
250
+ *
251
+ * @example
252
+ * <NavigationMenu.Root aria-label="Main">
253
+ * <NavigationMenu.List>
254
+ * <NavigationMenu.Item>
255
+ * <NavigationMenu.Trigger>Products</NavigationMenu.Trigger>
256
+ * <NavigationMenu.Content>
257
+ * <NavigationMenu.Link href="/analytics">Analytics</NavigationMenu.Link>
258
+ * </NavigationMenu.Content>
259
+ * </NavigationMenu.Item>
260
+ * <NavigationMenu.Item>
261
+ * <NavigationMenu.Link href="/pricing">Pricing</NavigationMenu.Link>
262
+ * </NavigationMenu.Item>
263
+ * </NavigationMenu.List>
264
+ * </NavigationMenu.Root>
265
+ */
266
+ export const NavigationMenu = { Root, List, Item, Trigger, Icon, Content, Link };
@@ -0,0 +1,70 @@
1
+ // A number field's values as text and back, shared by the component (server HTML, islands) and its
2
+ // script: `Intl.NumberFormat` writes them, and `parseNumber` reads what a person typed in the same
3
+ // locale and format (grouping, the decimal sign, the locale's digits, a currency, a percent sign).
4
+ import { digitValue } from "../locale/digits.ts";
5
+
6
+ /**
7
+ * The formatter for `format` (`Intl.NumberFormat` options) and `locale`. Options the browser
8
+ * refuses (`{"style":"currency"}` without a currency) are left out, and then an invalid locale,
9
+ * so the field still works (TASK-473, L12); the server falls back the same way.
10
+ */
11
+ export function formatter(
12
+ format: Intl.NumberFormatOptions | undefined,
13
+ locale: Intl.LocalesArgument | null | undefined,
14
+ ): Intl.NumberFormat {
15
+ try {
16
+ return new Intl.NumberFormat(locale ?? undefined, format);
17
+ } catch {
18
+ try {
19
+ return new Intl.NumberFormat(locale ?? undefined);
20
+ } catch {
21
+ return new Intl.NumberFormat();
22
+ }
23
+ }
24
+ }
25
+
26
+ /** `value` as the field shows it: `""` for no value. */
27
+ export function formatNumber(value: number | null, numbers: Intl.NumberFormat): string {
28
+ return value === null || !Number.isFinite(value) ? "" : numbers.format(value);
29
+ }
30
+
31
+ /** The signs `numbers` writes: its decimal sign and its digits, 0 to 9. */
32
+ function signs(numbers: Intl.NumberFormat) {
33
+ const { locale, numberingSystem } = numbers.resolvedOptions();
34
+ const plain = new Intl.NumberFormat(locale, { numberingSystem, useGrouping: false });
35
+ const decimal =
36
+ numbers.formatToParts(1.5).find((part) => part.type === "decimal")?.value ??
37
+ plain.formatToParts(1.5).find((part) => part.type === "decimal")?.value ??
38
+ ".";
39
+ const digits = new Map<string, string>();
40
+ for (let digit = 0; digit <= 9; digit++) digits.set(plain.format(digit), String(digit));
41
+ return { decimal, digits };
42
+ }
43
+
44
+ /**
45
+ * The number in `text`, as typed in `numbers`' locale and format: `null` when it is empty, `NaN`
46
+ * when it holds no number. Grouping, currency and unit signs and spaces are skipped; the locale's
47
+ * decimal sign (or a `.`) marks the fraction; a minus sign or parentheses make it negative; with
48
+ * the percent style, `50 %` is `0.5`.
49
+ */
50
+ export function parseNumber(text: string, numbers: Intl.NumberFormat): number | null {
51
+ const trimmed = text.trim();
52
+ if (trimmed === "") return null;
53
+ const { decimal, digits } = signs(numbers);
54
+ let normalized = "";
55
+ let negative = false;
56
+ let fraction = false;
57
+ for (const char of trimmed) {
58
+ if (/\d/.test(char)) normalized += char;
59
+ else if (digits.has(char)) normalized += digits.get(char);
60
+ // Digits of another script (full-width `1`, `٤`): what a keyboard or an IME may type (M34).
61
+ else if (digitValue(char) !== undefined) normalized += String(digitValue(char));
62
+ else if ((char === decimal || (char === "." && decimal !== ",")) && !fraction) {
63
+ normalized += ".";
64
+ fraction = true;
65
+ } else if (char === "-" || char === "−" || char === "(") negative = true;
66
+ }
67
+ if (!/\d/.test(normalized)) return Number.NaN;
68
+ const value = Number(normalized) * (negative ? -1 : 1);
69
+ return numbers.resolvedOptions().style === "percent" ? value / 100 : value;
70
+ }
@@ -0,0 +1,38 @@
1
+ /*
2
+ * The CSS Number Field needs to work, and nothing else (decision-62). Its own buttons replace the
3
+ * native number input's spinners once its script runs (`:defined`); until then (or without
4
+ * JavaScript) the buttons, which need the script, stay hidden and the native spinners work. A
5
+ * scrub area takes the pointer for its drag. It sits in the `lu.core` layer, so any app CSS wins.
6
+ */
7
+ @layer lu.core, lu.theme;
8
+
9
+ @layer lu.core {
10
+ lu-number-field {
11
+ display: block;
12
+ }
13
+ lu-number-field:not(:defined) button[data-action] {
14
+ visibility: hidden;
15
+ }
16
+ lu-number-field:defined input[type="number"] {
17
+ appearance: textfield;
18
+ }
19
+ lu-number-field:defined input[type="number"]::-webkit-inner-spin-button,
20
+ lu-number-field:defined input[type="number"]::-webkit-outer-spin-button {
21
+ appearance: none;
22
+ margin: 0;
23
+ }
24
+ /* Held, a button must not select text or open a touch callout. */
25
+ lu-number-field button[data-action] {
26
+ user-select: none;
27
+ -webkit-touch-callout: none;
28
+ touch-action: manipulation;
29
+ }
30
+ lu-number-field [data-scrub] {
31
+ cursor: ew-resize;
32
+ touch-action: none;
33
+ user-select: none;
34
+ }
35
+ lu-number-field [data-scrub][data-direction="vertical"] {
36
+ cursor: ns-resize;
37
+ }
38
+ }