@retailcrm/embed-ui-v1-components 0.9.25 → 0.9.27
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/remote.cjs +18 -4
- package/dist/remote.js +18 -4
- package/docs/AI.md +22 -1
- package/docs/STYLING.md +22 -0
- package/docs/profiles/components/UiAvatar.yml +12 -1
- package/docs/profiles/components/UiButton.yml +5 -1
- package/docs/profiles/components/UiCheckbox.yml +16 -8
- package/docs/profiles/components/UiField.yml +5 -2
- package/docs/profiles/components/UiNumberStepper.yml +3 -0
- package/docs/profiles/components/UiSwitch.yml +3 -0
- package/docs/profiles/components/UiTable.yml +48 -65
- package/docs/profiles/components/UiTableColumn.yml +44 -0
- package/docs/profiles/components/UiTextbox.yml +2 -0
- package/docs/profiles/pages/CardSettingsPage.yml +8 -1
- package/docs/profiles/pages/EntityListPage.yml +3 -0
- package/docs/profiles/pages/PageComposition.yml +4 -0
- package/package.json +4 -4
- package/templates/skills/embed-ui-v1-components-ui/SKILL.md.txt +8 -0
package/dist/remote.cjs
CHANGED
|
@@ -2466,6 +2466,12 @@ const slotNeedsBodyCellWrapper = (children) => {
|
|
|
2466
2466
|
}
|
|
2467
2467
|
return !nodes.every(isCellVNode);
|
|
2468
2468
|
};
|
|
2469
|
+
const hasSlotContent = (children) => {
|
|
2470
|
+
if (!children) {
|
|
2471
|
+
return false;
|
|
2472
|
+
}
|
|
2473
|
+
return normalizeNodes(children).length > 0;
|
|
2474
|
+
};
|
|
2469
2475
|
const renderGroupHead = (slot, props) => {
|
|
2470
2476
|
return slot?.(props) ?? [String(props.group.key)];
|
|
2471
2477
|
};
|
|
@@ -2692,17 +2698,25 @@ const _sfc_main$6 = vue.defineComponent({
|
|
|
2692
2698
|
const pageSize = slots["footer-page-size"]?.(footerData);
|
|
2693
2699
|
const exportControl = slots["footer-export"]?.(footerData);
|
|
2694
2700
|
const pagination = slots["footer-pagination"]?.(footerData);
|
|
2701
|
+
const hasSummary = hasSlotContent(summary);
|
|
2702
|
+
const hasPageSize = hasSlotContent(pageSize);
|
|
2703
|
+
const hasExportControl = hasSlotContent(exportControl);
|
|
2704
|
+
const hasPagination = hasSlotContent(pagination);
|
|
2705
|
+
const hasAnyStructuredFooterContent = hasSummary || hasPageSize || hasExportControl || hasPagination;
|
|
2706
|
+
if (!hasAnyStructuredFooterContent) {
|
|
2707
|
+
return null;
|
|
2708
|
+
}
|
|
2695
2709
|
return vue.h(UiTableSection, { kind: "foot", key: "footer" }, () => vue.h(UiTableRow, () => vue.h(UiTableBodyCell, {
|
|
2696
2710
|
colspan: columnsCount,
|
|
2697
2711
|
class: "ui-v1-table__footer-cell"
|
|
2698
2712
|
}, () => vue.h("div", { class: "ui-v1-table__footer" }, [
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2713
|
+
hasSummary ? vue.h("div", { class: "ui-v1-table__footer-meta" }, [summary]) : null,
|
|
2714
|
+
hasPageSize || hasExportControl || hasPagination ? vue.h("div", { class: "ui-v1-table__footer-controls" }, [
|
|
2715
|
+
hasPageSize || hasExportControl ? vue.h("div", { class: "ui-v1-table__footer-main" }, [
|
|
2702
2716
|
pageSize,
|
|
2703
2717
|
exportControl
|
|
2704
2718
|
]) : null,
|
|
2705
|
-
|
|
2719
|
+
hasPagination ? vue.h("div", { class: "ui-v1-table__footer-side" }, [pagination]) : null
|
|
2706
2720
|
]) : null
|
|
2707
2721
|
]))));
|
|
2708
2722
|
}
|
package/dist/remote.js
CHANGED
|
@@ -2464,6 +2464,12 @@ const slotNeedsBodyCellWrapper = (children) => {
|
|
|
2464
2464
|
}
|
|
2465
2465
|
return !nodes.every(isCellVNode);
|
|
2466
2466
|
};
|
|
2467
|
+
const hasSlotContent = (children) => {
|
|
2468
|
+
if (!children) {
|
|
2469
|
+
return false;
|
|
2470
|
+
}
|
|
2471
|
+
return normalizeNodes(children).length > 0;
|
|
2472
|
+
};
|
|
2467
2473
|
const renderGroupHead = (slot, props) => {
|
|
2468
2474
|
return slot?.(props) ?? [String(props.group.key)];
|
|
2469
2475
|
};
|
|
@@ -2690,17 +2696,25 @@ const _sfc_main$6 = defineComponent({
|
|
|
2690
2696
|
const pageSize = slots["footer-page-size"]?.(footerData);
|
|
2691
2697
|
const exportControl = slots["footer-export"]?.(footerData);
|
|
2692
2698
|
const pagination = slots["footer-pagination"]?.(footerData);
|
|
2699
|
+
const hasSummary = hasSlotContent(summary);
|
|
2700
|
+
const hasPageSize = hasSlotContent(pageSize);
|
|
2701
|
+
const hasExportControl = hasSlotContent(exportControl);
|
|
2702
|
+
const hasPagination = hasSlotContent(pagination);
|
|
2703
|
+
const hasAnyStructuredFooterContent = hasSummary || hasPageSize || hasExportControl || hasPagination;
|
|
2704
|
+
if (!hasAnyStructuredFooterContent) {
|
|
2705
|
+
return null;
|
|
2706
|
+
}
|
|
2693
2707
|
return h(UiTableSection, { kind: "foot", key: "footer" }, () => h(UiTableRow, () => h(UiTableBodyCell, {
|
|
2694
2708
|
colspan: columnsCount,
|
|
2695
2709
|
class: "ui-v1-table__footer-cell"
|
|
2696
2710
|
}, () => h("div", { class: "ui-v1-table__footer" }, [
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2711
|
+
hasSummary ? h("div", { class: "ui-v1-table__footer-meta" }, [summary]) : null,
|
|
2712
|
+
hasPageSize || hasExportControl || hasPagination ? h("div", { class: "ui-v1-table__footer-controls" }, [
|
|
2713
|
+
hasPageSize || hasExportControl ? h("div", { class: "ui-v1-table__footer-main" }, [
|
|
2700
2714
|
pageSize,
|
|
2701
2715
|
exportControl
|
|
2702
2716
|
]) : null,
|
|
2703
|
-
|
|
2717
|
+
hasPagination ? h("div", { class: "ui-v1-table__footer-side" }, [pagination]) : null
|
|
2704
2718
|
]) : null
|
|
2705
2719
|
]))));
|
|
2706
2720
|
}
|
package/docs/AI.md
CHANGED
|
@@ -63,10 +63,13 @@ When building a basic form or settings screen, start from patterns like:
|
|
|
63
63
|
```ts
|
|
64
64
|
import {
|
|
65
65
|
UiButton,
|
|
66
|
+
UiCheckbox,
|
|
66
67
|
UiField,
|
|
68
|
+
UiNumberStepper,
|
|
67
69
|
UiPageFooter,
|
|
68
70
|
UiPageHeader,
|
|
69
71
|
UiSelect,
|
|
72
|
+
UiSwitch,
|
|
70
73
|
UiTextbox,
|
|
71
74
|
} from '@retailcrm/embed-ui-v1-components/remote'
|
|
72
75
|
```
|
|
@@ -75,6 +78,9 @@ Typical compositions:
|
|
|
75
78
|
|
|
76
79
|
- `UiField` + `UiTextbox`
|
|
77
80
|
- `UiField` + `UiSelect`
|
|
81
|
+
- `UiField` + `UiNumberStepper`
|
|
82
|
+
- `UiSwitch` + visible label + hint row
|
|
83
|
+
- `UiCheckbox` + visible label row or checkbox group
|
|
78
84
|
- `UiPageHeader` + `UiButton`
|
|
79
85
|
- `UiSelect` + `UiSelectOption`
|
|
80
86
|
|
|
@@ -91,15 +97,20 @@ Default screen rules:
|
|
|
91
97
|
- use `UiPageFooter` for page-level save/cancel/delete actions instead of recreating a local footer;
|
|
92
98
|
- use one `Success Primary` button for the strongest save/apply/create action that commits a result; use `Default Primary`
|
|
93
99
|
only for another important action with a different meaning;
|
|
100
|
+
- use `Secondary` buttons for real neighboring commands that should still look like buttons;
|
|
101
|
+
- use `Tertiary` buttons for lower-emphasis edit, open, configure, cancel, close, and optional commands near headers,
|
|
102
|
+
cards, tables, and inline content;
|
|
94
103
|
- apply the 24px top and 32px side/bottom padding rule to white content surfaces, not to the page root wrapper;
|
|
95
104
|
- keep filters and controls near the content they affect;
|
|
96
|
-
- use `UiField` around labeled
|
|
105
|
+
- use `UiField` around labeled textbox, select, number, date, and time controls;
|
|
97
106
|
- use `UiTable` for structured result lists;
|
|
98
107
|
- use `UiLink` for navigation and inline links, `UiButton` for commands;
|
|
99
108
|
- use `UiLoader` with `overlay: true` when loading should dim the covered page or module content;
|
|
100
109
|
- keep public imports on `@retailcrm/embed-ui-v1-components/remote`;
|
|
101
110
|
- when a component uses only the default slot, prefer `v-slot` on the component instead of `<template #default>`;
|
|
102
111
|
- avoid custom markup that recreates textbox, select, button, link, or table chrome.
|
|
112
|
+
- before passing enum-like prop values, check the component profile or public type; omit optional props instead of
|
|
113
|
+
inventing neutral values such as `"none"` when they are not listed.
|
|
103
114
|
|
|
104
115
|
## Default Recommendation For Table Screens
|
|
105
116
|
|
|
@@ -115,6 +126,10 @@ screen where users scan and refine datasets:
|
|
|
115
126
|
- reset `page` to `1` when filters or sorting change;
|
|
116
127
|
- debounce free-text search before writing query or fetching data;
|
|
117
128
|
- use `UiTableSorter` for sortable headers;
|
|
129
|
+
- for ordinary `UiTableColumn` cell customization, set header metadata through props such as `label`, `width`,
|
|
130
|
+
and `trim`, then use `v-slot` on `UiTableColumn` instead of `<template #cell>`;
|
|
131
|
+
- use the `#cell` slot only when the column also needs another named slot such as `#label`, or when explicit
|
|
132
|
+
slot naming makes a complex column easier to read;
|
|
118
133
|
- use `UiTable` footer slots for summary, page-size controls, export, and pagination;
|
|
119
134
|
- compose table footer controls with `UiTableFooterSection` and `UiTableFooterButton`, not regular `UiButton`;
|
|
120
135
|
- use chevron icon assets for table footer previous/next controls instead of text glyphs;
|
|
@@ -142,10 +157,16 @@ When building forms with remote controls:
|
|
|
142
157
|
- use `UiField` for labeled text, select, date, and number controls;
|
|
143
158
|
- forward `UiField` slot props into the actual child control when it accepts `id`, especially
|
|
144
159
|
`UiTextbox`, `UiSelect`, and `UiNumberStepper`;
|
|
160
|
+
- put validation errors inside the relevant field composition, not as unrelated sibling blocks;
|
|
145
161
|
- when a component uses only the default slot, prefer `v-slot` on the component instead of
|
|
146
162
|
`<template #default>`;
|
|
147
163
|
- do not wrap `UiSwitch` in `UiField`; place the switch next to a visible label and optional hint,
|
|
148
164
|
and connect `UiSwitch :id` with `label :for`.
|
|
165
|
+
- do not wrap simple `UiCheckbox` rows in `UiField`; place the checkbox next to a visible label and optional hint.
|
|
166
|
+
- use `UiSwitch` for compact immediate boolean settings, feature toggles, and enable/disable flags.
|
|
167
|
+
- use `UiCheckbox` for checkbox groups, table row selection, "select all", acknowledgements, and checkbox-shaped choices.
|
|
168
|
+
- use `UiNumberStepper` for bounded numeric settings, quantities, durations, limits, and values with meaningful increments.
|
|
169
|
+
- use `UiTextbox` for numeric-looking text such as phone numbers, codes, identifiers, and free-form numeric strings.
|
|
149
170
|
|
|
150
171
|
## Default Recommendation For Widgets
|
|
151
172
|
|
package/docs/STYLING.md
CHANGED
|
@@ -157,6 +157,28 @@ For style-sensitive generation:
|
|
|
157
157
|
4. avoid relying on internal descendant selectors unless the profile says that is safe;
|
|
158
158
|
5. state which styling source is being followed when the change was requested by design feedback.
|
|
159
159
|
|
|
160
|
+
## Remote Page Styles
|
|
161
|
+
|
|
162
|
+
For Vue remote pages and page-like components, prefer local CSS Modules:
|
|
163
|
+
|
|
164
|
+
```vue
|
|
165
|
+
<style module lang="less">
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Apply classes through `$style[...]` in the template. Use `@import (reference)` from
|
|
169
|
+
`@retailcrm/embed-ui-v1-components/assets/stylesheets/*` for shared LESS tokens and typography
|
|
170
|
+
mixins.
|
|
171
|
+
|
|
172
|
+
Remote page styles should normally:
|
|
173
|
+
|
|
174
|
+
- use package spacing, palette, geometry, and typography tokens instead of local hardcoded colors,
|
|
175
|
+
font sizes, weights, and spacing;
|
|
176
|
+
- keep styles local to the page or component through CSS Modules;
|
|
177
|
+
- avoid shared global files such as `admin.less` as the default strategy;
|
|
178
|
+
- avoid duplicating CRM host layout padding on the page root;
|
|
179
|
+
- put documented 24px top and 32px side/bottom padding on white content surfaces when such a
|
|
180
|
+
surface exists.
|
|
181
|
+
|
|
160
182
|
## Design Feedback Triage
|
|
161
183
|
|
|
162
184
|
When design feedback does not match the current implementation, resolve the source before editing:
|
|
@@ -16,7 +16,6 @@ examples:
|
|
|
16
16
|
name="Anna Smith"
|
|
17
17
|
src="https://example.com/avatar.jpg"
|
|
18
18
|
size="sm"
|
|
19
|
-
status="none"
|
|
20
19
|
/>
|
|
21
20
|
</template>
|
|
22
21
|
|
|
@@ -37,7 +36,17 @@ api:
|
|
|
37
36
|
- name: src
|
|
38
37
|
- name: name
|
|
39
38
|
- name: status
|
|
39
|
+
values:
|
|
40
|
+
- busy
|
|
41
|
+
- break
|
|
42
|
+
- dinner
|
|
43
|
+
- free
|
|
44
|
+
notes: Omit status when no indicator should be shown; do not pass "none".
|
|
40
45
|
- name: size
|
|
46
|
+
values:
|
|
47
|
+
- xs
|
|
48
|
+
- sm
|
|
49
|
+
- lg
|
|
41
50
|
- name: href
|
|
42
51
|
|
|
43
52
|
rendered_structure:
|
|
@@ -61,8 +70,10 @@ ai_notes:
|
|
|
61
70
|
do:
|
|
62
71
|
- Use UiAvatar for identity display, not for general media.
|
|
63
72
|
- Provide name whenever possible so fallback identity stays meaningful.
|
|
73
|
+
- Omit optional enum-like props when no listed value applies.
|
|
64
74
|
avoid:
|
|
65
75
|
- Do not use it for generic content images or large media blocks.
|
|
76
|
+
- Do not pass unsupported neutral values such as status="none"; check listed values first.
|
|
66
77
|
|
|
67
78
|
composition:
|
|
68
79
|
works_well_with:
|
|
@@ -211,6 +211,8 @@ behavior:
|
|
|
211
211
|
notes: Enables the separate locked state.
|
|
212
212
|
notes:
|
|
213
213
|
- If href is present, the component becomes a link-shaped action.
|
|
214
|
+
- appearance="secondary" is for neighboring commands that should still read as real buttons.
|
|
215
|
+
- appearance="tertiary" is for lower-emphasis actions near headers, cards, tables, and inline content.
|
|
214
216
|
|
|
215
217
|
accessibility:
|
|
216
218
|
notes:
|
|
@@ -231,7 +233,9 @@ ai_notes:
|
|
|
231
233
|
- Start with appearance=primary for the main CTA, then choose variant by meaning.
|
|
232
234
|
- Use variant="success" for the strongest create/save/apply action when it commits a result, and keep only one Success Primary on a page.
|
|
233
235
|
- Use the default variant for an important primary action that is not the strongest commit action.
|
|
234
|
-
- Use secondary
|
|
236
|
+
- Use secondary for a real neighboring command that should still look like a button.
|
|
237
|
+
- Use tertiary for lower-emphasis edit, open, configure, cancel, close, and optional commands that should not compete with the main flow.
|
|
235
238
|
avoid:
|
|
236
239
|
- Do not replace UiButton with UiLink when the action should read as a button.
|
|
237
240
|
- Do not use UiButton for regular inline text navigation.
|
|
241
|
+
- Do not default every non-primary action to secondary; choose tertiary when the action is only supportive.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
component: UiCheckbox
|
|
2
2
|
summary: >
|
|
3
|
-
UiCheckbox is a
|
|
4
|
-
|
|
3
|
+
UiCheckbox is a checkbox or set-membership control. It supports single acknowledgement-like
|
|
4
|
+
choices, table row selection, "select all" states, and model-plus-value patterns for checkbox groups.
|
|
5
5
|
|
|
6
6
|
public_import:
|
|
7
7
|
from: '@retailcrm/embed-ui-v1-components/remote'
|
|
@@ -37,12 +37,14 @@ examples:
|
|
|
37
37
|
const channels = ref<string[]>([])
|
|
38
38
|
</script>
|
|
39
39
|
use_when:
|
|
40
|
-
- You need a
|
|
40
|
+
- You need a checkbox-shaped boolean choice such as an acknowledgement or confirmation.
|
|
41
41
|
- You need one value inside a checkbox group model.
|
|
42
|
+
- You need row selection, mass selection, or an indeterminate "select all" control.
|
|
42
43
|
|
|
43
44
|
avoid_when:
|
|
44
45
|
- You need exclusive single choice, use UiRadio instead.
|
|
45
|
-
- You need a
|
|
46
|
+
- You need a compact immediate on or off setting, use UiSwitch instead.
|
|
47
|
+
- You need a settings toggle row where a switch better communicates enable or disable state.
|
|
46
48
|
|
|
47
49
|
api:
|
|
48
50
|
key_props:
|
|
@@ -73,26 +75,32 @@ behavior:
|
|
|
73
75
|
notes:
|
|
74
76
|
- model plus value supports group-style selection.
|
|
75
77
|
- indeterminate is useful for partial selection states.
|
|
78
|
+
- For ordinary settings toggles, UiSwitch is usually the better control even when the value is boolean.
|
|
76
79
|
|
|
77
80
|
ai_notes:
|
|
78
81
|
do:
|
|
79
|
-
- Use for boolean
|
|
82
|
+
- Use for checkbox-group membership, acknowledgement-like boolean choices, and table row selection.
|
|
80
83
|
- Use indeterminate for parent selection such as "select all visible rows".
|
|
81
84
|
avoid:
|
|
82
85
|
- Do not use for mutually exclusive options.
|
|
86
|
+
- Do not replace UiSwitch settings toggles with UiCheckbox just because the value is boolean.
|
|
83
87
|
|
|
84
88
|
composition:
|
|
85
89
|
works_well_with:
|
|
86
|
-
-
|
|
90
|
+
- visible label
|
|
91
|
+
- hint text
|
|
87
92
|
- UiTable
|
|
88
93
|
- UiTableColumn
|
|
89
94
|
patterns:
|
|
90
|
-
- title:
|
|
91
|
-
notes:
|
|
95
|
+
- title: Checkbox row
|
|
96
|
+
notes: Place UiCheckbox on the left, label and optional hint on the right, similar to a UiSwitch settings row.
|
|
97
|
+
- title: Checkbox group
|
|
98
|
+
notes: Use several UiCheckbox controls with one array model and visible labels for multi-selection.
|
|
92
99
|
- title: Table row selection
|
|
93
100
|
notes: Use in the first narrow UiTableColumn for row selection; use indeterminate in the header checkbox for partial selection.
|
|
94
101
|
avoid:
|
|
95
102
|
- Do not use as a visual switch replacement when the action is an immediate on or off setting.
|
|
103
|
+
- Do not wrap simple checkbox rows in UiField; use an explicit row with visible label and optional hint.
|
|
96
104
|
|
|
97
105
|
accessibility:
|
|
98
106
|
notes:
|
|
@@ -12,7 +12,6 @@ public_import:
|
|
|
12
12
|
related_components:
|
|
13
13
|
- UiTextbox
|
|
14
14
|
- UiSelect
|
|
15
|
-
- UiCheckbox
|
|
16
15
|
- UiNumberStepper
|
|
17
16
|
- UiDatePicker
|
|
18
17
|
|
|
@@ -162,6 +161,8 @@ api:
|
|
|
162
161
|
- UiDatePicker
|
|
163
162
|
avoid:
|
|
164
163
|
- several unrelated controls
|
|
164
|
+
- UiSwitch setting rows
|
|
165
|
+
- UiCheckbox setting rows
|
|
165
166
|
- decorative layout only
|
|
166
167
|
- raw content without using slot props
|
|
167
168
|
layout_effect: The control sits below the headline and inherits field-level semantics through slot props.
|
|
@@ -311,8 +312,10 @@ ai_notes:
|
|
|
311
312
|
- Forward slot props into the actual control in most form scenarios.
|
|
312
313
|
- Pass field.id to child controls that accept id, including UiTextbox, UiSelect, and UiNumberStepper.
|
|
313
314
|
- Use v-slot on UiField when the field uses only the default slot.
|
|
314
|
-
- Use UiField as a semantic wrapper for a single control.
|
|
315
|
+
- Use UiField as a semantic wrapper for a single textbox, select, number, date, or time control.
|
|
316
|
+
- Place validation errors inside the relevant field composition instead of rendering them as unrelated sibling blocks.
|
|
315
317
|
avoid:
|
|
316
318
|
- Do not use UiField as a generic visual container without control semantics.
|
|
317
319
|
- Do not use UiField for UiSwitch settings rows; pair UiSwitch with a visible label and hint text instead.
|
|
320
|
+
- Do not use UiField for simple UiCheckbox rows; pair UiCheckbox with a visible label and optional hint instead.
|
|
318
321
|
- Do not ignore id and ariaLabelledby when accessibility matters.
|
|
@@ -28,9 +28,11 @@ examples:
|
|
|
28
28
|
use_when:
|
|
29
29
|
- You need numeric input with explicit step controls.
|
|
30
30
|
- You need range constraints and nullable numeric state.
|
|
31
|
+
- You need bounded numeric settings, quantities, durations, limits, delays, or values with meaningful increments.
|
|
31
32
|
|
|
32
33
|
avoid_when:
|
|
33
34
|
- You need plain text or decimal input without stepper controls.
|
|
35
|
+
- You need numeric-looking text such as phone numbers, codes, identifiers, or free-form strings.
|
|
34
36
|
|
|
35
37
|
api:
|
|
36
38
|
key_props:
|
|
@@ -82,6 +84,7 @@ ai_notes:
|
|
|
82
84
|
- Prefer nullable when clearing the value is a valid state.
|
|
83
85
|
avoid:
|
|
84
86
|
- Do not use when the value is an identifier, phone, code, or other numeric-looking text.
|
|
87
|
+
- Do not replace with UiTextbox for bounded numeric settings unless a runtime limitation is documented.
|
|
85
88
|
|
|
86
89
|
accessibility:
|
|
87
90
|
notes:
|
|
@@ -120,10 +120,12 @@ examples:
|
|
|
120
120
|
</style>
|
|
121
121
|
use_when:
|
|
122
122
|
- You need a compact on or off toggle.
|
|
123
|
+
- You need an immediate boolean setting, feature flag, enable or disable control, or settings row.
|
|
123
124
|
|
|
124
125
|
avoid_when:
|
|
125
126
|
- You need checkbox-group semantics.
|
|
126
127
|
- You need a UiField-style labeled wrapper; pair UiSwitch with a visible label and hint text instead.
|
|
128
|
+
- You need an acknowledgement, legal confirmation, table selection, or multi-select choice; use UiCheckbox instead.
|
|
127
129
|
|
|
128
130
|
api:
|
|
129
131
|
key_props:
|
|
@@ -170,6 +172,7 @@ ai_notes:
|
|
|
170
172
|
- Put helper text below the label in the settings row, not in UiField hint.
|
|
171
173
|
avoid:
|
|
172
174
|
- Do not use for multi-select lists or legal acknowledgements; use UiCheckbox.
|
|
175
|
+
- Do not replace compact settings toggles with UiCheckbox just because the value is boolean.
|
|
173
176
|
- Do not use UiField around UiSwitch.
|
|
174
177
|
|
|
175
178
|
accessibility:
|
|
@@ -32,24 +32,18 @@ examples:
|
|
|
32
32
|
:rows="rows"
|
|
33
33
|
row-key="id"
|
|
34
34
|
>
|
|
35
|
-
<UiTableColumn label="Title">
|
|
36
|
-
<
|
|
37
|
-
<strong>{{ row.title }}</strong>
|
|
38
|
-
</template>
|
|
35
|
+
<UiTableColumn v-slot="{ row }" label="Title">
|
|
36
|
+
<strong>{{ row.title }}</strong>
|
|
39
37
|
</UiTableColumn>
|
|
40
38
|
|
|
41
|
-
<UiTableColumn label="Customer" width="180">
|
|
42
|
-
|
|
43
|
-
{{ row.customer }}
|
|
44
|
-
</template>
|
|
39
|
+
<UiTableColumn v-slot="{ row }" label="Customer" width="180">
|
|
40
|
+
{{ row.customer }}
|
|
45
41
|
</UiTableColumn>
|
|
46
42
|
|
|
47
|
-
<UiTableColumn label="Status" width="160">
|
|
48
|
-
<
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
</UiTag>
|
|
52
|
-
</template>
|
|
43
|
+
<UiTableColumn v-slot="{ row }" label="Status" width="160">
|
|
44
|
+
<UiTag :background="statusBackgroundByName[row.status]" size="md" saturated :ticker="false">
|
|
45
|
+
{{ row.status }}
|
|
46
|
+
</UiTag>
|
|
53
47
|
</UiTableColumn>
|
|
54
48
|
</UiTable>
|
|
55
49
|
</template>
|
|
@@ -78,29 +72,25 @@ examples:
|
|
|
78
72
|
:rows="rows"
|
|
79
73
|
row-key="id"
|
|
80
74
|
>
|
|
81
|
-
<UiTableColumn label="Name">
|
|
82
|
-
|
|
83
|
-
{{ row.name }}
|
|
84
|
-
</template>
|
|
75
|
+
<UiTableColumn v-slot="{ row }" label="Name">
|
|
76
|
+
{{ row.name }}
|
|
85
77
|
</UiTableColumn>
|
|
86
78
|
|
|
87
|
-
<UiTableColumn :width="48" label="" trim>
|
|
88
|
-
<
|
|
89
|
-
<
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
</UiPopperConnector>
|
|
103
|
-
</template>
|
|
79
|
+
<UiTableColumn v-slot="{ row }" :width="48" label="" trim>
|
|
80
|
+
<UiPopperConnector>
|
|
81
|
+
<UiButton
|
|
82
|
+
:aria-label="`Edit ${row.name}`"
|
|
83
|
+
appearance="tertiary"
|
|
84
|
+
size="sm"
|
|
85
|
+
@click="edit(row)"
|
|
86
|
+
>
|
|
87
|
+
<IconEdit aria-hidden="true" />
|
|
88
|
+
</UiButton>
|
|
89
|
+
|
|
90
|
+
<UiTooltip>
|
|
91
|
+
Edit {{ row.name }}
|
|
92
|
+
</UiTooltip>
|
|
93
|
+
</UiPopperConnector>
|
|
104
94
|
</UiTableColumn>
|
|
105
95
|
</UiTable>
|
|
106
96
|
</template>
|
|
@@ -139,18 +129,14 @@ examples:
|
|
|
139
129
|
:rows="rows"
|
|
140
130
|
row-key="id"
|
|
141
131
|
>
|
|
142
|
-
<UiTableColumn :width="44" label="" trim>
|
|
143
|
-
<
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
</button>
|
|
147
|
-
</template>
|
|
132
|
+
<UiTableColumn v-slot="{ expanded, toggle }" :width="44" label="" trim>
|
|
133
|
+
<button class="table-expand" type="button" @click="toggle">
|
|
134
|
+
{{ expanded ? '-' : '+' }}
|
|
135
|
+
</button>
|
|
148
136
|
</UiTableColumn>
|
|
149
137
|
|
|
150
|
-
<UiTableColumn label="Title">
|
|
151
|
-
|
|
152
|
-
{{ row.title }}
|
|
153
|
-
</template>
|
|
138
|
+
<UiTableColumn v-slot="{ row }" label="Title">
|
|
139
|
+
{{ row.title }}
|
|
154
140
|
</UiTableColumn>
|
|
155
141
|
|
|
156
142
|
<template #expand="{ row }">
|
|
@@ -186,16 +172,12 @@ examples:
|
|
|
186
172
|
row-key="id"
|
|
187
173
|
:group-by="groupByStatus"
|
|
188
174
|
>
|
|
189
|
-
<UiTableColumn label="Title">
|
|
190
|
-
|
|
191
|
-
{{ row.title }}
|
|
192
|
-
</template>
|
|
175
|
+
<UiTableColumn v-slot="{ row }" label="Title">
|
|
176
|
+
{{ row.title }}
|
|
193
177
|
</UiTableColumn>
|
|
194
178
|
|
|
195
|
-
<UiTableColumn label="Status" width="160">
|
|
196
|
-
|
|
197
|
-
{{ row.status }}
|
|
198
|
-
</template>
|
|
179
|
+
<UiTableColumn v-slot="{ row }" label="Status" width="160">
|
|
180
|
+
{{ row.status }}
|
|
199
181
|
</UiTableColumn>
|
|
200
182
|
|
|
201
183
|
<template #group-head="{ group }">
|
|
@@ -241,10 +223,8 @@ examples:
|
|
|
241
223
|
:rows="rows"
|
|
242
224
|
row-key="id"
|
|
243
225
|
>
|
|
244
|
-
<UiTableColumn label="Title">
|
|
245
|
-
|
|
246
|
-
{{ row.title }}
|
|
247
|
-
</template>
|
|
226
|
+
<UiTableColumn v-slot="{ row }" label="Title">
|
|
227
|
+
{{ row.title }}
|
|
248
228
|
</UiTableColumn>
|
|
249
229
|
|
|
250
230
|
<template #footer-summary="{ rowsCount }">
|
|
@@ -440,16 +420,12 @@ examples:
|
|
|
440
420
|
:rows="rows"
|
|
441
421
|
row-key="id"
|
|
442
422
|
>
|
|
443
|
-
<UiTableColumn label="Title">
|
|
444
|
-
|
|
445
|
-
{{ row.title }}
|
|
446
|
-
</template>
|
|
423
|
+
<UiTableColumn v-slot="{ row }" label="Title">
|
|
424
|
+
{{ row.title }}
|
|
447
425
|
</UiTableColumn>
|
|
448
426
|
|
|
449
|
-
<UiTableColumn label="Status" width="160">
|
|
450
|
-
|
|
451
|
-
{{ row.status }}
|
|
452
|
-
</template>
|
|
427
|
+
<UiTableColumn v-slot="{ row }" label="Status" width="160">
|
|
428
|
+
{{ row.status }}
|
|
453
429
|
</UiTableColumn>
|
|
454
430
|
</UiTable>
|
|
455
431
|
</template>
|
|
@@ -649,6 +625,11 @@ composition:
|
|
|
649
625
|
- Use UiTableSorter inside a UiTableColumn label slot for sortable columns.
|
|
650
626
|
- Reset pagination to the first page when sorting changes.
|
|
651
627
|
- Persist sort key and direction in URL query parameters when routing is available.
|
|
628
|
+
columns:
|
|
629
|
+
notes:
|
|
630
|
+
- Configure header metadata with UiTableColumn props such as label, width, align, valign, and trim.
|
|
631
|
+
- Prefer `v-slot` on UiTableColumn for ordinary cell content when the header is configured through props.
|
|
632
|
+
- Use `#cell` only when the column also needs another named slot such as `#label`, or when explicit naming improves readability.
|
|
652
633
|
pagination:
|
|
653
634
|
notes:
|
|
654
635
|
- Use footer-summary, footer-page-size, footer-export, and footer-pagination slots for structured footer controls.
|
|
@@ -676,6 +657,7 @@ ai_notes:
|
|
|
676
657
|
- Compose footer controls with UiTableFooterSection and UiTableFooterButton.
|
|
677
658
|
- Copy the Entity list table footer example when building a realistic entity-list footer.
|
|
678
659
|
- Use UiTableSorter for sortable headers.
|
|
660
|
+
- Prefer UiTableColumn `v-slot` for ordinary custom cells when label and sizing are configured through props.
|
|
679
661
|
- Use icon-only row action buttons with aria-label and UiTooltip in action columns.
|
|
680
662
|
avoid:
|
|
681
663
|
- Do not hide table filters in page header actions.
|
|
@@ -684,6 +666,7 @@ ai_notes:
|
|
|
684
666
|
- Do not put visible text buttons in dense table action columns.
|
|
685
667
|
- Do not put pagination only in local state when the screen has routable result sets.
|
|
686
668
|
- Do not import table internals from host or src paths.
|
|
669
|
+
- Do not use `<template #cell>` for every column when the default slot would be simpler.
|
|
687
670
|
|
|
688
671
|
behavior:
|
|
689
672
|
notes:
|
|
@@ -11,6 +11,35 @@ public_import:
|
|
|
11
11
|
related_components:
|
|
12
12
|
- UiTable
|
|
13
13
|
|
|
14
|
+
examples:
|
|
15
|
+
- title: Prop label with default cell slot
|
|
16
|
+
notes:
|
|
17
|
+
- Use this shape when header metadata is covered by props and only the cell body is custom.
|
|
18
|
+
code: |
|
|
19
|
+
<UiTableColumn v-slot="{ row }" label="Name" min-width="180">
|
|
20
|
+
<UiLink size="small">{{ row.name }}</UiLink>
|
|
21
|
+
</UiTableColumn>
|
|
22
|
+
- title: Custom label and explicit cell slot
|
|
23
|
+
notes:
|
|
24
|
+
- Use named slots when the column uses a custom label, for example a sorter.
|
|
25
|
+
- Keeping `#cell` explicit makes the two slot zones easier to scan.
|
|
26
|
+
code: |
|
|
27
|
+
<UiTableColumn min-width="180">
|
|
28
|
+
<template #label>
|
|
29
|
+
<UiTableSorter
|
|
30
|
+
:direction="sort.direction"
|
|
31
|
+
:active="sort.field === 'name'"
|
|
32
|
+
@click="setSort('name')"
|
|
33
|
+
>
|
|
34
|
+
Name
|
|
35
|
+
</UiTableSorter>
|
|
36
|
+
</template>
|
|
37
|
+
|
|
38
|
+
<template #cell="{ row }">
|
|
39
|
+
<UiLink size="small">{{ row.name }}</UiLink>
|
|
40
|
+
</template>
|
|
41
|
+
</UiTableColumn>
|
|
42
|
+
|
|
14
43
|
use_when:
|
|
15
44
|
- You define columns for UiTable.
|
|
16
45
|
|
|
@@ -30,12 +59,19 @@ api:
|
|
|
30
59
|
- name: default
|
|
31
60
|
zone: cell
|
|
32
61
|
creates: Cell content.
|
|
62
|
+
notes: >
|
|
63
|
+
Prefer `v-slot` on UiTableColumn when the header label is already supplied by the `label`
|
|
64
|
+
prop and only the cell body is customized.
|
|
33
65
|
- name: cell
|
|
34
66
|
zone: cell
|
|
35
67
|
creates: Explicit cell content.
|
|
68
|
+
notes: >
|
|
69
|
+
Use `#cell` when the column also needs another named slot such as `#label`, or when explicit
|
|
70
|
+
naming makes a complex column easier to read.
|
|
36
71
|
- name: label
|
|
37
72
|
zone: header-label
|
|
38
73
|
creates: Header label content.
|
|
74
|
+
notes: Use only when the header label needs custom markup, for example UiTableSorter.
|
|
39
75
|
|
|
40
76
|
rendered_structure:
|
|
41
77
|
descriptive_only: true
|
|
@@ -60,6 +96,10 @@ composition:
|
|
|
60
96
|
notes: Put the row's main UiLink in the first meaningful text column and set link size to small.
|
|
61
97
|
- title: Sortable header
|
|
62
98
|
notes: Use the label slot with UiTableSorter when the column supports sorting.
|
|
99
|
+
- title: Prop label plus custom cell
|
|
100
|
+
notes: Use `<UiTableColumn v-slot="{ row }" label="Name">` instead of `<template #cell>` when `label` prop is enough for the header.
|
|
101
|
+
- title: Custom label plus custom cell
|
|
102
|
+
notes: Use separate `<template #label>` and `<template #cell>` blocks when the header is built with a slot.
|
|
63
103
|
- title: Status column
|
|
64
104
|
notes: Use UiTag for categorical statuses instead of raw colored text.
|
|
65
105
|
|
|
@@ -68,8 +108,12 @@ ai_notes:
|
|
|
68
108
|
- Set minWidth for important text columns so generated tables remain scannable.
|
|
69
109
|
- Use align="right" for numbers, money, and percentages.
|
|
70
110
|
- Use trim only for narrow checkbox, icon, or action columns.
|
|
111
|
+
- Prefer `v-slot` on UiTableColumn for ordinary custom cell content when the header is configured through props.
|
|
112
|
+
- Use the label slot only when the header itself needs custom markup.
|
|
113
|
+
- Use explicit `#cell` together with `#label` when both header and cell content are custom.
|
|
71
114
|
avoid:
|
|
72
115
|
- Do not place filters inside column labels; filters belong above the table.
|
|
116
|
+
- Do not wrap every cell body in `<template #cell>` when the default slot would express the same thing more simply.
|
|
73
117
|
|
|
74
118
|
behavior:
|
|
75
119
|
notes:
|
|
@@ -37,12 +37,14 @@ use_when:
|
|
|
37
37
|
- You need a standard text input.
|
|
38
38
|
- You need search, email, phone, url, or password input.
|
|
39
39
|
- You need decimal or numeric input without a separate stepper.
|
|
40
|
+
- You need numeric-looking text such as a phone, code, identifier, article, external id, or free-form numeric string.
|
|
40
41
|
- You need a multiline control in the same visual language.
|
|
41
42
|
- You need an input that composes well with UiField and inline editing.
|
|
42
43
|
|
|
43
44
|
avoid_when:
|
|
44
45
|
- You need value selection, not free-form input.
|
|
45
46
|
- You need explicit increment and decrement UX.
|
|
47
|
+
- You need bounded numeric settings, quantities, durations, limits, or values with meaningful increments; use UiNumberStepper instead.
|
|
46
48
|
- You need advanced formatting beyond the prefix or suffix model.
|
|
47
49
|
|
|
48
50
|
api:
|
|
@@ -23,6 +23,8 @@ expected_structure:
|
|
|
23
23
|
- The white content surface owns the 24px top and 32px side/bottom padding.
|
|
24
24
|
- The page root wrapper should not repeat that padding.
|
|
25
25
|
- Content can include text, buttons, fields, checkboxes, radio groups, switches, and other form controls.
|
|
26
|
+
- Textbox, select, number, date, and time controls usually live inside UiField.
|
|
27
|
+
- UiSwitch and simple UiCheckbox rows should use their own row layout with a visible label and optional hint, not UiField.
|
|
26
28
|
- A bottom save panel is optional.
|
|
27
29
|
- If a bottom save panel is used, its main save or apply action should be Success Primary.
|
|
28
30
|
|
|
@@ -55,9 +57,14 @@ recommended_components:
|
|
|
55
57
|
ai_notes:
|
|
56
58
|
do:
|
|
57
59
|
- Keep form controls grouped by task or semantic section.
|
|
58
|
-
- Use UiField around
|
|
60
|
+
- Use UiField around labeled textbox, select, number, date, and time controls.
|
|
61
|
+
- Use UiSwitch for compact enable or disable settings.
|
|
62
|
+
- Use UiCheckbox for checkbox groups, table selection, acknowledgements, and checkbox-shaped boolean choices.
|
|
59
63
|
- Use tabs only when they reduce visible complexity without hiding required work.
|
|
60
64
|
- Keep save/apply as the strongest footer action and move neighboring actions to secondary or tertiary appearances unless their variant has a distinct page-level meaning.
|
|
65
|
+
- Prefer tertiary for low-emphasis local actions that should not compete with the settings save flow.
|
|
61
66
|
avoid:
|
|
62
67
|
- Do not create a decorative landing page for operational settings.
|
|
63
68
|
- Do not put content-surface padding on the root page wrapper.
|
|
69
|
+
- Do not use UiCheckbox as the default replacement for UiSwitch just because a setting is boolean.
|
|
70
|
+
- Do not wrap UiSwitch or simple UiCheckbox rows in UiField.
|
|
@@ -18,6 +18,7 @@ expected_structure:
|
|
|
18
18
|
- One or more 48px page buttons on the right side of the header.
|
|
19
19
|
- If there are multiple header buttons, one should be primary and the rest can be secondary or tertiary.
|
|
20
20
|
- The main Default Primary button usually creates a new entity for the list when there is no stronger Success Primary action on the page.
|
|
21
|
+
- Supporting header actions that should not compete with the create action are usually tertiary rather than secondary.
|
|
21
22
|
- Filters above the table, built from controls such as select, textbox, and combobox-like selection components.
|
|
22
23
|
- Filter controls should run in rows of roughly 4-5 fields, then wrap to the next row.
|
|
23
24
|
- Filters can be collapsible.
|
|
@@ -65,8 +66,10 @@ ai_notes:
|
|
|
65
66
|
- Persist filters, sorting, page, and page size in query parameters when routing is available.
|
|
66
67
|
- Reset page to 1 when filters or sorting change.
|
|
67
68
|
- Use narrow icon-only row action columns with matching aria-label and UiTooltip text.
|
|
69
|
+
- Use tertiary for low-emphasis header or row-adjacent commands when a secondary button would be too visually heavy.
|
|
68
70
|
avoid:
|
|
69
71
|
- Do not hide filters in page header actions.
|
|
70
72
|
- Do not put pagination only in local state when the result set is routable.
|
|
71
73
|
- Do not place UiTable inside an additional white card or padded content surface.
|
|
72
74
|
- Do not use visible text buttons for dense table row actions.
|
|
75
|
+
- Do not make every header action secondary when only one action should draw attention.
|
|
@@ -22,6 +22,8 @@ shared_rules:
|
|
|
22
22
|
- Use Default Primary for an important action that is secondary to the Success Primary or does not commit a result.
|
|
23
23
|
- Use Danger Primary only for one critical destructive action when the page needs it.
|
|
24
24
|
- If several actions compete for attention, choose the main one for Success Primary and move the rest to Default Primary, secondary, tertiary, or dropdown actions by meaning.
|
|
25
|
+
- Use secondary for a real neighboring command that should still read as a button.
|
|
26
|
+
- Use tertiary for lower-emphasis header, card, inline, configure, edit, open, cancel, and close actions that should not compete with the main flow.
|
|
25
27
|
- UiPageHeader actions and UiPageFooter actions belong to the same page-level action scope.
|
|
26
28
|
- Treat each UiCollapseBox footer as a separate local action scope with the same rules scoped to that section.
|
|
27
29
|
- A page can have several Default Primary buttons when each one belongs to a different UiCollapseBox footer.
|
|
@@ -60,8 +62,10 @@ ai_notes:
|
|
|
60
62
|
- Choose ModalWindow or a full page when content needs wide tables, several sections, or complex controls.
|
|
61
63
|
- Keep operational CRM screens dense, scannable, and work-focused.
|
|
62
64
|
- Prefer variant="success" for the main save/apply/create action and keep it visually dominant.
|
|
65
|
+
- Prefer appearance="tertiary" for supportive actions that sit near content but should stay visually quiet.
|
|
63
66
|
avoid:
|
|
64
67
|
- Do not turn internal CRM work screens into marketing-style layouts.
|
|
65
68
|
- Do not hide filters or primary workflow controls away from the content they affect.
|
|
66
69
|
- Do not place multiple Success Primary, Default Primary, or Danger Primary actions with the same variant in the page-level scope or inside one collapse-box scope.
|
|
70
|
+
- Do not default every non-primary action to appearance="secondary"; choose by local action importance.
|
|
67
71
|
- Do not add page-surface padding to both the page root and the white content surface.
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@retailcrm/embed-ui-v1-components",
|
|
3
3
|
"bin": "bin/embed-ui-v1-components.mjs",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"version": "0.9.
|
|
5
|
+
"version": "0.9.27",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "RetailDriverLLC <integration@retailcrm.ru>",
|
|
8
8
|
"repository": {
|
|
@@ -82,8 +82,8 @@
|
|
|
82
82
|
"@storybook/vue3": "^10.3.5",
|
|
83
83
|
"@storybook/vue3-vite": "^10.3.5",
|
|
84
84
|
"@vitejs/plugin-vue": "^6.0.2",
|
|
85
|
-
"@vitest/browser": "4.1.
|
|
86
|
-
"@vitest/browser-playwright": "4.1.
|
|
85
|
+
"@vitest/browser": "4.1.8",
|
|
86
|
+
"@vitest/browser-playwright": "4.1.8",
|
|
87
87
|
"@vue/compiler-sfc": "^3.5.25",
|
|
88
88
|
"@vue/test-utils": "^2.4.6",
|
|
89
89
|
"@yandex/ymaps3-types": "^1.0.19072130",
|
|
@@ -100,7 +100,7 @@
|
|
|
100
100
|
"vite": "^7.3.2",
|
|
101
101
|
"vite-plugin-dts": "^4.5.4",
|
|
102
102
|
"vite-svg-loader": "^5.1.0",
|
|
103
|
-
"vitest": "
|
|
103
|
+
"vitest": "4.1.8",
|
|
104
104
|
"vue": "^3.5.32",
|
|
105
105
|
"vue-i18n": "10.0.8"
|
|
106
106
|
}
|
|
@@ -26,13 +26,21 @@ Use this skill before changing frontend UI built with `@retailcrm/embed-ui-v1-co
|
|
|
26
26
|
5. Do not hand-roll buttons, inputs, selects, tables, or links when a documented Embed UI component exists.
|
|
27
27
|
6. Do not set arbitrary `font-family`; inherit the host/component stack unless a project-specific design requirement says otherwise.
|
|
28
28
|
7. Do not style internal `.ui-v1-*` selectors unless the profile or `STYLING.md` explicitly documents that override pattern.
|
|
29
|
+
8. For Vue remote pages, prefer `<style module lang="less">` with `$style[...]` and package LESS tokens/mixins.
|
|
30
|
+
9. Before passing enum-like prop values, check the profile or public type; omit optional props instead of inventing neutral values.
|
|
29
31
|
|
|
30
32
|
## High-signal checks
|
|
31
33
|
|
|
32
34
|
- For entity lists, use `UiTable`, `UiTableColumn`, and table footer slots.
|
|
35
|
+
- For ordinary table cells, configure `UiTableColumn` headers through props and use `v-slot` on the column; reserve `#cell` for columns that also need another named slot or extra clarity.
|
|
33
36
|
- For table pagination, follow the reference example in `UiTable.yml`: button sizes, active state, dividers, arrow assets, and scoped selector pattern.
|
|
34
37
|
- For form fields, use `UiField` with the matching control and forward slot props such as `id` when the control accepts them.
|
|
38
|
+
- Put validation errors inside the relevant field composition, not as unrelated sibling blocks.
|
|
35
39
|
- For `UiSwitch`, use the documented switch + visible label + hint row instead of wrapping it in `UiField`.
|
|
40
|
+
- For simple `UiCheckbox` rows, use checkbox + visible label + optional hint instead of wrapping it in `UiField`.
|
|
41
|
+
- Use `UiSwitch` for compact boolean settings and `UiCheckbox` for checkbox groups, table row selection, select-all, and acknowledgements.
|
|
42
|
+
- Use `UiNumberStepper` for bounded numeric settings and `UiTextbox` for numeric-looking text such as phones, codes, and identifiers.
|
|
43
|
+
- Use `Secondary` buttons for real neighboring commands and `Tertiary` buttons for lower-emphasis edit/open/configure/cancel/close actions.
|
|
36
44
|
- For widgets mounted into CRM targets, keep inline UI compact; move complex UI into `UiModalSidebar` or `UiModalWindow`.
|
|
37
45
|
- For sidebar footers, group related actions, use 12px or 16px gaps inside groups, and confirm destructive icon-only actions with `UiPopconfirm okVariant="danger"`.
|
|
38
46
|
|