@dxos/react-ui 0.13.0 → 0.14.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/dist/lib/Accordion.mjs +5 -6
- package/dist/lib/Accordion.mjs.map +1 -1
- package/dist/lib/Banner.mjs +2 -2
- package/dist/lib/Card.mjs +2 -2
- package/dist/lib/Combobox.mjs +1 -1
- package/dist/lib/Editable.mjs +24 -8
- package/dist/lib/Editable.mjs.map +1 -1
- package/dist/lib/Layout.mjs +1 -1
- package/dist/lib/Listbox.mjs +5 -4
- package/dist/lib/Listbox.mjs.map +1 -1
- package/dist/lib/Menu.mjs +1 -1
- package/dist/lib/Select.mjs +1 -1
- package/dist/lib/Tag.mjs +1 -1
- package/dist/lib/chunk-Button.mjs +1 -1
- package/dist/lib/chunk-Container.mjs +1 -1
- package/dist/lib/chunk-Empty.mjs +1 -1
- package/dist/lib/chunk-Input.mjs +1 -1
- package/dist/lib/chunk-Main.mjs +1 -1
- package/dist/lib/chunk-grid.mjs +2 -0
- package/dist/lib/chunk-grid.mjs.map +1 -1
- package/dist/lib/chunk-recipes.mjs +1 -0
- package/dist/lib/chunk-recipes.mjs.map +1 -1
- package/dist/lib/chunk-useThemeContext.mjs +1 -1
- package/dist/types/src/next/components/Accordion/Accordion.d.ts.map +1 -1
- package/dist/types/src/next/components/Editable/Editable.d.ts +1 -1
- package/dist/types/src/next/components/Editable/Editable.d.ts.map +1 -1
- package/dist/types/src/next/components/Listbox/Listbox.d.ts +11 -5
- package/dist/types/src/next/components/Listbox/Listbox.d.ts.map +1 -1
- package/dist/types/src/next/components/Listbox/grid.d.ts.map +1 -1
- package/dist/types/src/next/components/Toolbar/Toolbar.stories.d.ts.map +1 -1
- package/dist/types/src/next/recipes.d.ts +1 -0
- package/dist/types/src/next/recipes.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +16 -16
- package/src/next/components/Accordion/Accordion.tsx +8 -5
- package/src/next/components/Editable/Editable.tsx +50 -22
- package/src/next/components/Listbox/Listbox.tsx +8 -5
- package/src/next/components/Listbox/grid.ts +6 -0
- package/src/next/components/Toolbar/Toolbar.stories.tsx +2 -1
- package/src/next/recipes.ts +1 -0
- package/src/next/theme/accordion.css +5 -2
- package/src/next/theme/card.css +6 -0
- package/src/next/theme/control.css +1 -3
- package/src/next/theme/editable.css +18 -0
- package/src/next/theme/row.css +4 -0
- package/src/next/theme/toggle.css +4 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/react-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
4
4
|
"description": "Low-level React components for DXOS, applying a theme to a core group of primitives",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
@@ -297,17 +297,17 @@
|
|
|
297
297
|
"i18next": "^21.10.0",
|
|
298
298
|
"react-error-boundary": "^4.0.13",
|
|
299
299
|
"react-i18next": "^11.18.6",
|
|
300
|
-
"@dxos/async": "0.
|
|
301
|
-
"@dxos/
|
|
302
|
-
"@dxos/
|
|
303
|
-
"@dxos/
|
|
304
|
-
"@dxos/
|
|
305
|
-
"@dxos/log": "0.
|
|
306
|
-
"@dxos/react-error-boundary": "0.
|
|
307
|
-
"@dxos/react-focus": "0.
|
|
308
|
-
"@dxos/react-hooks": "0.
|
|
309
|
-
"@dxos/ui-types": "0.
|
|
310
|
-
"@dxos/util": "0.
|
|
300
|
+
"@dxos/async": "0.14.0",
|
|
301
|
+
"@dxos/i18n": "0.14.0",
|
|
302
|
+
"@dxos/invariant": "0.14.0",
|
|
303
|
+
"@dxos/lit-ui": "0.14.0",
|
|
304
|
+
"@dxos/effect": "0.14.0",
|
|
305
|
+
"@dxos/log": "0.14.0",
|
|
306
|
+
"@dxos/react-error-boundary": "0.14.0",
|
|
307
|
+
"@dxos/react-focus": "0.14.0",
|
|
308
|
+
"@dxos/react-hooks": "0.14.0",
|
|
309
|
+
"@dxos/ui-types": "0.14.0",
|
|
310
|
+
"@dxos/util": "0.14.0"
|
|
311
311
|
},
|
|
312
312
|
"devDependencies": {
|
|
313
313
|
"@effect/atom-react": "4.0.0",
|
|
@@ -323,16 +323,16 @@
|
|
|
323
323
|
"react-aria-components": "^1.17.0",
|
|
324
324
|
"react-dom": "~19.2.7",
|
|
325
325
|
"vite": "^8.2.2",
|
|
326
|
-
"@dxos/random": "0.
|
|
327
|
-
"@dxos/ui-theme": "0.
|
|
328
|
-
"@dxos/util": "0.
|
|
326
|
+
"@dxos/random": "0.14.0",
|
|
327
|
+
"@dxos/ui-theme": "0.14.0",
|
|
328
|
+
"@dxos/util": "0.14.0"
|
|
329
329
|
},
|
|
330
330
|
"peerDependencies": {
|
|
331
331
|
"@effect/atom-react": "4.0.0",
|
|
332
332
|
"effect": "4.0.0",
|
|
333
333
|
"react": "~19.2.7",
|
|
334
334
|
"react-dom": "~19.2.7",
|
|
335
|
-
"@dxos/ui-theme": "^0.
|
|
335
|
+
"@dxos/ui-theme": "^0.14.0"
|
|
336
336
|
},
|
|
337
337
|
"publishConfig": {
|
|
338
338
|
"access": "public"
|
|
@@ -11,6 +11,7 @@ import { mx } from '@dxos/ui-theme';
|
|
|
11
11
|
import { type ThemedClassName } from '@dxos/ui-types';
|
|
12
12
|
|
|
13
13
|
import { recipes } from '../../recipes.ts';
|
|
14
|
+
import { Block } from '../Block/Block.tsx';
|
|
14
15
|
import * as Icon from '../Icon/Icon.tsx';
|
|
15
16
|
|
|
16
17
|
//
|
|
@@ -80,17 +81,19 @@ const AccordionItemTrigger = forwardRef<HTMLButtonElement, AccordionItemTriggerP
|
|
|
80
81
|
ref={forwardedRef}
|
|
81
82
|
>
|
|
82
83
|
{icon && (
|
|
83
|
-
<
|
|
84
|
+
<Block classNames={recipes.accordionItemIcon()}>
|
|
84
85
|
<Icon.Icon icon={icon} />
|
|
85
|
-
</
|
|
86
|
+
</Block>
|
|
86
87
|
)}
|
|
87
88
|
<span data-scope='accordion' data-part='item-text' className={recipes.accordionItemText()}>
|
|
88
89
|
{children}
|
|
89
90
|
</span>
|
|
90
91
|
{!disabled && (
|
|
91
|
-
<
|
|
92
|
-
<
|
|
93
|
-
|
|
92
|
+
<Block>
|
|
93
|
+
<AccordionPrimitive.ItemIndicator className={recipes.accordionItemIndicator()}>
|
|
94
|
+
<Icon.Icon icon='ph--caret-right--regular' />
|
|
95
|
+
</AccordionPrimitive.ItemIndicator>
|
|
96
|
+
</Block>
|
|
94
97
|
)}
|
|
95
98
|
</AccordionPrimitive.ItemTrigger>
|
|
96
99
|
);
|
|
@@ -23,6 +23,9 @@ const EDITABLE_INPUT_NAME = 'Editable.Input';
|
|
|
23
23
|
// parts read does not expose.
|
|
24
24
|
const [EditableActivationProvider, useEditableActivation] = createContext<EditableActivationBinding>(EDITABLE_NAME);
|
|
25
25
|
|
|
26
|
+
// A controlled field's submit never reaches the machine's commit callback, so Save commits through the hook.
|
|
27
|
+
const [EditableCommitProvider, useEditableCommit] = createContext<{ commit: () => void }>(EDITABLE_NAME);
|
|
28
|
+
|
|
26
29
|
//
|
|
27
30
|
// Root
|
|
28
31
|
//
|
|
@@ -50,7 +53,7 @@ const EditableRoot = forwardRef<HTMLDivElement, EditableRootProps>(
|
|
|
50
53
|
},
|
|
51
54
|
forwardedRef,
|
|
52
55
|
) => {
|
|
53
|
-
const { api, activationProps } = useEditable({
|
|
56
|
+
const { api, activationProps, commit } = useEditable({
|
|
54
57
|
value,
|
|
55
58
|
defaultValue,
|
|
56
59
|
onValueChange,
|
|
@@ -69,7 +72,9 @@ const EditableRoot = forwardRef<HTMLDivElement, EditableRootProps>(
|
|
|
69
72
|
className={mx(recipes.editable(), classNames)}
|
|
70
73
|
ref={forwardedRef}
|
|
71
74
|
>
|
|
72
|
-
<EditableActivationProvider {...activationProps}>
|
|
75
|
+
<EditableActivationProvider {...activationProps}>
|
|
76
|
+
<EditableCommitProvider commit={commit}>{children}</EditableCommitProvider>
|
|
77
|
+
</EditableActivationProvider>
|
|
73
78
|
</EditablePrimitive.RootProvider>
|
|
74
79
|
);
|
|
75
80
|
},
|
|
@@ -85,7 +90,7 @@ type EditablePreviewProps = ThemedClassName<Omit<ComponentPropsWithRef<'span'>,
|
|
|
85
90
|
|
|
86
91
|
/** The static text and the activation affordance; hidden by the machine while editing. */
|
|
87
92
|
const EditablePreview = forwardRef<HTMLSpanElement, EditablePreviewProps>(({ classNames, ...props }, forwardedRef) => {
|
|
88
|
-
const { valueText } = useEditableContext();
|
|
93
|
+
const { valueText, edit } = useEditableContext();
|
|
89
94
|
const { role, onKeyDown } = useEditableActivation(EDITABLE_PREVIEW_NAME);
|
|
90
95
|
|
|
91
96
|
return (
|
|
@@ -101,7 +106,18 @@ const EditablePreview = forwardRef<HTMLSpanElement, EditablePreviewProps>(({ cla
|
|
|
101
106
|
ref={forwardedRef}
|
|
102
107
|
>
|
|
103
108
|
<span className={recipes.editablePreviewText()}>{valueText}</span>
|
|
104
|
-
|
|
109
|
+
{/* The pencil is a door of its own: one click opens the field whatever gesture the preview takes (a double-click
|
|
110
|
+
in a list, where a single click selects the row). */}
|
|
111
|
+
<Icon.Icon
|
|
112
|
+
icon='ph--pencil-simple--regular'
|
|
113
|
+
classNames={recipes.editablePreviewIcon()}
|
|
114
|
+
data-testid='editable.edit'
|
|
115
|
+
// A disabled field hides the pencil (`editable.css`), so it takes no click.
|
|
116
|
+
onClick={(event) => {
|
|
117
|
+
event.stopPropagation();
|
|
118
|
+
edit();
|
|
119
|
+
}}
|
|
120
|
+
/>
|
|
105
121
|
</EditablePrimitive.Preview>
|
|
106
122
|
);
|
|
107
123
|
});
|
|
@@ -114,9 +130,10 @@ EditablePreview.displayName = EDITABLE_PREVIEW_NAME;
|
|
|
114
130
|
|
|
115
131
|
type EditableInputProps = ThemedClassName<Omit<ComponentPropsWithRef<'input'>, 'value' | 'onChange' | 'placeholder'>>;
|
|
116
132
|
|
|
117
|
-
/** The field shown while editing
|
|
133
|
+
/** The field shown while editing, focused with the caret at the end, with a save button at its end. */
|
|
118
134
|
const EditableInput = forwardRef<HTMLInputElement, EditableInputProps>(({ classNames, ...props }, forwardedRef) => {
|
|
119
135
|
const { editing } = useEditableContext();
|
|
136
|
+
const { commit } = useEditableCommit(EDITABLE_INPUT_NAME);
|
|
120
137
|
const localRef = useRef<HTMLInputElement | null>(null);
|
|
121
138
|
|
|
122
139
|
// The machine focuses the input but leaves the caret where the browser puts it, which from the keyboard is the start.
|
|
@@ -130,24 +147,35 @@ const EditableInput = forwardRef<HTMLInputElement, EditableInputProps>(({ classN
|
|
|
130
147
|
}, [editing]);
|
|
131
148
|
|
|
132
149
|
return (
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
forwardedRef
|
|
142
|
-
|
|
143
|
-
return () => {
|
|
144
|
-
localRef.current = null;
|
|
145
|
-
if (typeof cleanup === 'function') {
|
|
146
|
-
cleanup();
|
|
150
|
+
<>
|
|
151
|
+
<EditablePrimitive.Input
|
|
152
|
+
{...props}
|
|
153
|
+
className={mx(recipes.editableInput(), classNames)}
|
|
154
|
+
ref={(element) => {
|
|
155
|
+
localRef.current = element;
|
|
156
|
+
// Typed `unknown`: `ForwardedRef`'s callback is declared void, but React 19 may return a cleanup.
|
|
157
|
+
const cleanup: unknown = typeof forwardedRef === 'function' ? forwardedRef(element) : undefined;
|
|
158
|
+
if (forwardedRef && typeof forwardedRef !== 'function') {
|
|
159
|
+
forwardedRef.current = element;
|
|
147
160
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
161
|
+
return () => {
|
|
162
|
+
localRef.current = null;
|
|
163
|
+
if (typeof cleanup === 'function') {
|
|
164
|
+
cleanup();
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
}}
|
|
168
|
+
/>
|
|
169
|
+
{/* Saves the edit, at the input's end in the same cell; the machine hides it while not editing. */}
|
|
170
|
+
<EditablePrimitive.SubmitTrigger
|
|
171
|
+
className={recipes.editableSubmit()}
|
|
172
|
+
aria-label='Save'
|
|
173
|
+
data-testid='editable.save'
|
|
174
|
+
onClick={() => commit()}
|
|
175
|
+
>
|
|
176
|
+
<Icon.Icon icon='ph--check--bold' />
|
|
177
|
+
</EditablePrimitive.SubmitTrigger>
|
|
178
|
+
</>
|
|
151
179
|
);
|
|
152
180
|
});
|
|
153
181
|
|
|
@@ -45,7 +45,7 @@ type ListboxOption = {
|
|
|
45
45
|
};
|
|
46
46
|
|
|
47
47
|
/** `none` keeps zag's focus, keyboard and typeahead with no selection (AUDIT §6 group B: every list runs the machine). */
|
|
48
|
-
type ListboxSelectionMode = 'single' | 'multiple' | 'none';
|
|
48
|
+
type ListboxSelectionMode = 'single' | 'multiple' | 'extended' | 'none';
|
|
49
49
|
|
|
50
50
|
type RootContextValue = {
|
|
51
51
|
items: readonly ListboxOption[];
|
|
@@ -72,7 +72,10 @@ const useRootContext = (part: string) => {
|
|
|
72
72
|
|
|
73
73
|
type ListboxRootProps = ThemedClassName<Omit<ComponentPropsWithoutRef<'div'>, 'defaultValue' | 'onSelect'>> & {
|
|
74
74
|
items: readonly ListboxOption[];
|
|
75
|
-
/**
|
|
75
|
+
/**
|
|
76
|
+
* `single` by default; `multiple` toggles each item; `extended` selects one on a click and adds with Cmd/Ctrl or a
|
|
77
|
+
* Shift range, as a desktop list does; `none` selects nothing but keeps navigation and typeahead.
|
|
78
|
+
*/
|
|
76
79
|
selectionMode?: ListboxSelectionMode;
|
|
77
80
|
value?: string[];
|
|
78
81
|
defaultValue?: string[];
|
|
@@ -177,7 +180,7 @@ ListboxLabel.displayName = 'Listbox.Label';
|
|
|
177
180
|
|
|
178
181
|
type ListboxContentProps = ThemedClassName<ComponentPropsWithoutRef<'div'>> &
|
|
179
182
|
Pick<ScrollArea.RootProps, 'mode' | 'width' | 'native'> &
|
|
180
|
-
Pick<ContainerProps, 'gutter' | 'gap'> & {
|
|
183
|
+
Pick<ContainerProps, 'gutter' | 'gap' | 'padBlock'> & {
|
|
181
184
|
/**
|
|
182
185
|
* `false` renders the rows without a ScrollArea of their own, for a host that already scrolls (a ScrollArea
|
|
183
186
|
* composed in `Panel.Body`): the rows then inherit the host's rails (`gutter='inherit'` by default).
|
|
@@ -192,7 +195,7 @@ type ListboxContentProps = ThemedClassName<ComponentPropsWithoutRef<'div'>> &
|
|
|
192
195
|
const ListboxViewport = composable<
|
|
193
196
|
HTMLDivElement,
|
|
194
197
|
ContainerProps & Omit<ComponentPropsWithoutRef<'div'>, 'className'> & { hostScrolls?: boolean }
|
|
195
|
-
>(({ gutter, gap, hostScrolls, onKeyDown, onFocus, onBlur, children, ...props }, forwardedRef) => {
|
|
198
|
+
>(({ gutter, gap, padBlock, hostScrolls, onKeyDown, onFocus, onBlur, children, ...props }, forwardedRef) => {
|
|
196
199
|
const api = useListboxContext();
|
|
197
200
|
const { columns, virtual, scrollToIndexRef } = useRootContext('Content');
|
|
198
201
|
const [element, setElement] = useState<HTMLDivElement | null>(null);
|
|
@@ -202,7 +205,7 @@ const ListboxViewport = composable<
|
|
|
202
205
|
const ref = useComposedRefs<HTMLDivElement>(forwardedRef, setElement, windowing.listRef);
|
|
203
206
|
useRowTabStops(element);
|
|
204
207
|
|
|
205
|
-
const { style, ...attributes } = containerAttributes({ gutter, gap, columns });
|
|
208
|
+
const { style, ...attributes } = containerAttributes({ gutter, gap, columns, padBlock });
|
|
206
209
|
const {
|
|
207
210
|
className,
|
|
208
211
|
style: ownStyle,
|
|
@@ -21,6 +21,7 @@ const CONTROL_SELECTOR = [
|
|
|
21
21
|
].join(', ');
|
|
22
22
|
|
|
23
23
|
const ROW_SELECTOR = '[role="option"]';
|
|
24
|
+
const EDITABLE_PREVIEW_SELECTOR = '[data-scope="editable"][data-part="preview"]';
|
|
24
25
|
|
|
25
26
|
/** The row (`option`) holding `element` in this list, not in a list nested in a row's detail. */
|
|
26
27
|
const rowOf = (list: HTMLElement, element: Element): HTMLElement | null => {
|
|
@@ -49,6 +50,11 @@ export const isFromControl = (row: HTMLElement, target: EventTarget | null) => {
|
|
|
49
50
|
return false;
|
|
50
51
|
}
|
|
51
52
|
const control = target.closest(CONTROL_SELECTOR);
|
|
53
|
+
// A row's text edited in place (an Editable preview) is the row's for a pointer: a press selects the row and leaves the
|
|
54
|
+
// list its keys; a double-click, its pencil, or Enter once the row is entered still opens it.
|
|
55
|
+
if (control?.matches(EDITABLE_PREVIEW_SELECTOR)) {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
52
58
|
return control !== null && control !== row && row.contains(control);
|
|
53
59
|
};
|
|
54
60
|
|
|
@@ -188,7 +188,8 @@ export const Test: Story = {
|
|
|
188
188
|
await expect(separator).not.toHaveAttribute('tabindex');
|
|
189
189
|
|
|
190
190
|
for (const size of ['md', 'lg'] as const) {
|
|
191
|
-
|
|
191
|
+
// The toolbar adds no gap of its own: items are spaced by their own insets.
|
|
192
|
+
const expected = 2 * GEOMETRY[size].inset;
|
|
192
193
|
const items = ['add', 'remove', 'separator', 'button', 'input', 'select', 'list', 'grid'].map((part) =>
|
|
193
194
|
byTestId(canvasElement, `${part}-${size}`).getBoundingClientRect(),
|
|
194
195
|
);
|
package/src/next/recipes.ts
CHANGED
|
@@ -102,6 +102,7 @@ export const recipes = {
|
|
|
102
102
|
editablePreview: () => `dx-control dx-editable-preview ${FOCUS_RING}`,
|
|
103
103
|
editablePreviewIcon: () => 'dx-editable-preview-icon',
|
|
104
104
|
editablePreviewText: () => 'dx-editable-preview-text',
|
|
105
|
+
editableSubmit: () => 'dx-editable-submit',
|
|
105
106
|
empty: () => 'dx-empty',
|
|
106
107
|
emptyText: () => 'dx-empty-text',
|
|
107
108
|
errorFallback: () => 'dx-error-fallback',
|
|
@@ -36,10 +36,10 @@
|
|
|
36
36
|
.dx-accordion-item-trigger {
|
|
37
37
|
display: flex;
|
|
38
38
|
align-items: center;
|
|
39
|
-
gap: var(--dx-gap-size);
|
|
40
39
|
width: 100%;
|
|
41
40
|
min-height: var(--dx-block-size);
|
|
42
|
-
|
|
41
|
+
/* The icon and the caret are Blocks, which set the row's rhythm; only a row without an icon pads its text. */
|
|
42
|
+
padding: 0;
|
|
43
43
|
border: none;
|
|
44
44
|
border-radius: inherit;
|
|
45
45
|
background: none;
|
|
@@ -73,6 +73,9 @@
|
|
|
73
73
|
flex: 1;
|
|
74
74
|
min-width: 0;
|
|
75
75
|
}
|
|
76
|
+
.dx-accordion-item-trigger > .dx-accordion-item-text:first-child {
|
|
77
|
+
padding-inline-start: var(--dx-gap-size);
|
|
78
|
+
}
|
|
76
79
|
|
|
77
80
|
.dx-accordion-item-indicator {
|
|
78
81
|
transition: transform var(--dx-disclosure-duration) var(--dx-disclosure-ease-open);
|
package/src/next/theme/card.css
CHANGED
|
@@ -141,8 +141,14 @@
|
|
|
141
141
|
color: var(--color-fg-muted);
|
|
142
142
|
}
|
|
143
143
|
|
|
144
|
+
/* Framed by its host, so neither edge nor rounding of its own; the border keeps its width so nothing shifts. */
|
|
144
145
|
.dx-card[data-border='false'] {
|
|
145
146
|
border-color: transparent;
|
|
147
|
+
border-radius: 0;
|
|
148
|
+
}
|
|
149
|
+
.dx-card[data-border='false'] > .dx-card-poster:first-child {
|
|
150
|
+
border-start-start-radius: 0;
|
|
151
|
+
border-start-end-radius: 0;
|
|
146
152
|
}
|
|
147
153
|
.dx-card[data-selected] {
|
|
148
154
|
border-color: var(--color-accent-bg);
|
|
@@ -986,11 +986,9 @@
|
|
|
986
986
|
display: flex;
|
|
987
987
|
flex-shrink: 0;
|
|
988
988
|
align-items: center;
|
|
989
|
-
gap:
|
|
989
|
+
/* No gap or padding of its own: the items are block-sized controls, which carry their own spacing. */
|
|
990
990
|
width: 100%;
|
|
991
991
|
min-height: var(--dx-block-size);
|
|
992
|
-
/* Half a gap of inline padding (px-1 at md), so the end items are not flush with a pane's edge. */
|
|
993
|
-
padding-inline: calc(var(--dx-gap-size) / 2);
|
|
994
992
|
font-size: var(--dx-font-size);
|
|
995
993
|
line-height: var(--dx-line-height);
|
|
996
994
|
}
|
|
@@ -30,6 +30,10 @@
|
|
|
30
30
|
.dx-editable-preview:hover:not([data-disabled]) {
|
|
31
31
|
background: var(--color-hover-surface);
|
|
32
32
|
}
|
|
33
|
+
/* In a list row the row is the hover target: the preview takes the row's background rather than painting its own. */
|
|
34
|
+
.dx-row .dx-editable-preview:hover:not([data-disabled]) {
|
|
35
|
+
background: transparent;
|
|
36
|
+
}
|
|
33
37
|
.dx-editable-preview[data-placeholder-shown] {
|
|
34
38
|
color: var(--color-fg-subtle);
|
|
35
39
|
}
|
|
@@ -50,6 +54,7 @@
|
|
|
50
54
|
.dx-editable-preview-icon {
|
|
51
55
|
color: var(--color-fg-subtle);
|
|
52
56
|
visibility: hidden;
|
|
57
|
+
cursor: pointer;
|
|
53
58
|
}
|
|
54
59
|
.dx-editable-preview:is(:hover, :focus-visible):not([data-disabled]) .dx-editable-preview-icon {
|
|
55
60
|
visibility: visible;
|
|
@@ -57,6 +62,19 @@
|
|
|
57
62
|
|
|
58
63
|
.dx-editable-input {
|
|
59
64
|
color: var(--color-fg);
|
|
65
|
+
/* Room for the save button laid over the input's end. */
|
|
66
|
+
padding-inline-end: var(--dx-block-size);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.dx-editable-submit {
|
|
70
|
+
justify-self: end;
|
|
71
|
+
display: flex;
|
|
72
|
+
align-items: center;
|
|
73
|
+
justify-content: center;
|
|
74
|
+
width: var(--dx-block-size);
|
|
75
|
+
height: var(--dx-block-size);
|
|
76
|
+
color: var(--color-success-text);
|
|
77
|
+
cursor: pointer;
|
|
60
78
|
}
|
|
61
79
|
.dx-editable-input::placeholder {
|
|
62
80
|
color: var(--color-fg-subtle);
|
package/src/next/theme/row.css
CHANGED
|
@@ -10,6 +10,10 @@
|
|
|
10
10
|
border-radius: var(--radius-sm, 0.25rem);
|
|
11
11
|
cursor: default;
|
|
12
12
|
}
|
|
13
|
+
/* Rows that run edge to edge would show their rounding against the container's own edges. */
|
|
14
|
+
.dx-container[data-gutter='none'] > .dx-row {
|
|
15
|
+
border-radius: 0;
|
|
16
|
+
}
|
|
13
17
|
.dx-row[data-selectable] {
|
|
14
18
|
cursor: pointer;
|
|
15
19
|
}
|
|
@@ -22,6 +22,10 @@
|
|
|
22
22
|
flex-direction: column;
|
|
23
23
|
align-items: stretch;
|
|
24
24
|
}
|
|
25
|
+
/* In a toolbar, which adds no gap between its items, a group adds none between its own either. */
|
|
26
|
+
.dx-toolbar > .dx-toggle-group {
|
|
27
|
+
gap: 0;
|
|
28
|
+
}
|
|
25
29
|
.dx-toolbar > .dx-toggle-group > .dx-control:not([data-square]) {
|
|
26
30
|
margin-inline: var(--dx-control-inset);
|
|
27
31
|
}
|