@retailcrm/embed-ui-v1-components 0.9.24 → 0.9.26
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/AGENTS.md +1 -0
- package/README.md +9 -0
- package/bin/embed-ui-v1-components.mjs +52 -3
- package/bin/postinstall.mjs +3 -0
- package/docs/AI.md +41 -2
- package/docs/FORMAT.md +1 -0
- package/docs/STYLING.md +59 -1
- 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 +69 -12
- package/docs/profiles/components/UiModalSidebar.yml +22 -6
- package/docs/profiles/components/UiNumberStepper.yml +3 -0
- package/docs/profiles/components/UiSelect.yml +8 -0
- package/docs/profiles/components/UiSelectOption.yml +2 -0
- package/docs/profiles/components/UiSwitch.yml +71 -2
- package/docs/profiles/components/UiTable.yml +76 -0
- package/docs/profiles/components/UiTextbox.yml +2 -0
- package/docs/profiles/components/UiTooltip.yml +1 -0
- package/docs/profiles/pages/CardSettingsPage.yml +11 -1
- package/docs/profiles/pages/EntityListPage.yml +9 -0
- package/docs/profiles/pages/ModalSidebar.yml +40 -12
- package/docs/profiles/pages/MultiColumnPage.yml +2 -0
- package/docs/profiles/pages/PageComposition.yml +7 -0
- package/package.json +3 -2
- package/templates/skills/embed-ui-v1-components-ui/SKILL.md.txt +53 -0
package/AGENTS.md
CHANGED
|
@@ -70,6 +70,7 @@ Commonly used exports from `remote` include:
|
|
|
70
70
|
- Prefer package public exports over reimplementing CRM-styled controls manually.
|
|
71
71
|
- Match component choice to semantics:
|
|
72
72
|
use `UiField` for labeled form controls, `UiAlert` for state messages, `UiPageHeader` for page-level headings.
|
|
73
|
+
- When a component uses only the default slot, prefer the `v-slot` directive on the component instead of `<template #default>`.
|
|
73
74
|
- For widget targets, keep inline UI compact: prefer `UiToolbarButton`, `UiToolbarLink`, short text, and icons.
|
|
74
75
|
- Move complex widget UI into `UiModalSidebar` or `UiModalWindow` instead of expanding the target slot.
|
|
75
76
|
- Keep imports on the public package boundary.
|
package/README.md
CHANGED
|
@@ -68,3 +68,12 @@ npx @retailcrm/embed-ui-v1-components init-agents
|
|
|
68
68
|
Если `AGENTS.md` уже существует, команда допишет в конец инструкции для
|
|
69
69
|
`@retailcrm/embed-ui-v1-components`, если такого блока там еще нет. С `--force`
|
|
70
70
|
можно обновить уже существующий блок пакета.
|
|
71
|
+
|
|
72
|
+
Для project-level skills можно создать `.agents/skills/embed-ui-v1-components-ui/SKILL.md`:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
npx @retailcrm/embed-ui-v1-components init-skills
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Skill описывает повторяемый workflow для выбора page pattern, чтения профилей компонентов,
|
|
79
|
+
проверки styling constraints и ревью table pagination/form/widget composition.
|
|
@@ -10,9 +10,12 @@ const DEFAULT_NEWLINE = '\n'
|
|
|
10
10
|
const AGENTS_SECTION_HEADER = '## @retailcrm/embed-ui-v1-components'
|
|
11
11
|
const AGENTS_SECTION_START = '<!-- embed-ui-agents:start -->'
|
|
12
12
|
const AGENTS_SECTION_END = '<!-- embed-ui-agents:end -->'
|
|
13
|
+
const SKILL_NAME = 'embed-ui-v1-components-ui'
|
|
14
|
+
const SKILL_TEMPLATE_PATH = `templates/skills/${SKILL_NAME}/SKILL.md.txt`
|
|
13
15
|
|
|
14
16
|
const HELP_TEXT = `Usage:
|
|
15
17
|
npx ${PACKAGE_NAME} init-agents [target] [options]
|
|
18
|
+
npx ${PACKAGE_NAME} init-skills [target] [options]
|
|
16
19
|
|
|
17
20
|
Options:
|
|
18
21
|
-f, --force Replace existing package section in AGENTS.md
|
|
@@ -20,6 +23,7 @@ Options:
|
|
|
20
23
|
|
|
21
24
|
Examples:
|
|
22
25
|
npx ${PACKAGE_NAME} init-agents
|
|
26
|
+
npx ${PACKAGE_NAME} init-skills
|
|
23
27
|
npx ${PACKAGE_NAME} init-agents ./my-project
|
|
24
28
|
npx ${PACKAGE_NAME} init-agents --force
|
|
25
29
|
`
|
|
@@ -250,6 +254,14 @@ ${AGENTS_SECTION_END}
|
|
|
250
254
|
`
|
|
251
255
|
}
|
|
252
256
|
|
|
257
|
+
const createSkill = (target, packageDocsPath) => {
|
|
258
|
+
const packageRoot = getCurrentPackageRoot() ?? findPackageRoot(target)
|
|
259
|
+
const templatePath = path.join(packageRoot, SKILL_TEMPLATE_PATH)
|
|
260
|
+
const template = fs.readFileSync(templatePath, 'utf8')
|
|
261
|
+
|
|
262
|
+
return template.replaceAll('__PACKAGE_DOCS_PATH__', packageDocsPath)
|
|
263
|
+
}
|
|
264
|
+
|
|
253
265
|
const findMarkedSectionRange = (content) => {
|
|
254
266
|
const start = content.indexOf(AGENTS_SECTION_START)
|
|
255
267
|
const end = content.indexOf(AGENTS_SECTION_END, start + AGENTS_SECTION_START.length)
|
|
@@ -381,15 +393,52 @@ const initAgents = (target, force) => {
|
|
|
381
393
|
console.log(`The ${PACKAGE_NAME} instructions were appended to the end of the file.`)
|
|
382
394
|
}
|
|
383
395
|
|
|
396
|
+
const initSkills = (target, force) => {
|
|
397
|
+
if (!fs.existsSync(target)) {
|
|
398
|
+
throw new Error(`Target path does not exist: ${target}`)
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
const stat = fs.statSync(target)
|
|
402
|
+
|
|
403
|
+
if (!stat.isDirectory()) {
|
|
404
|
+
throw new Error(`Target path is not a directory: ${target}`)
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
const packageDocsPath = createPackageDocsPath(target)
|
|
408
|
+
const skillPath = path.join(target, '.agents', 'skills', SKILL_NAME, 'SKILL.md')
|
|
409
|
+
const fileExists = fs.existsSync(skillPath)
|
|
410
|
+
|
|
411
|
+
if (fileExists && !force) {
|
|
412
|
+
console.log(`${skillPath} already exists`)
|
|
413
|
+
console.log('Nothing was changed. Re-run with --force to refresh that skill.')
|
|
414
|
+
return
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
if (!fs.existsSync(path.dirname(skillPath))) {
|
|
418
|
+
fs.mkdirSync(path.dirname(skillPath), { recursive: true })
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
fs.writeFileSync(skillPath, createSkill(target, packageDocsPath), 'utf8')
|
|
422
|
+
|
|
423
|
+
const action = fileExists ? 'updated' : 'created'
|
|
424
|
+
console.log(`SKILL: ${action} ${skillPath}`)
|
|
425
|
+
}
|
|
426
|
+
|
|
384
427
|
const main = () => {
|
|
385
428
|
try {
|
|
386
429
|
const options = parseArgs(process.argv.slice(2))
|
|
387
430
|
|
|
388
|
-
if (options.command
|
|
389
|
-
|
|
431
|
+
if (options.command === 'init-agents') {
|
|
432
|
+
initAgents(options.target, options.force)
|
|
433
|
+
return
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
if (options.command === 'init-skills') {
|
|
437
|
+
initSkills(options.target, options.force)
|
|
438
|
+
return
|
|
390
439
|
}
|
|
391
440
|
|
|
392
|
-
|
|
441
|
+
throw new Error(`Unknown command: ${options.command}`)
|
|
393
442
|
} catch (error) {
|
|
394
443
|
console.error(error instanceof Error ? error.message : String(error))
|
|
395
444
|
console.error('')
|
package/bin/postinstall.mjs
CHANGED
|
@@ -33,6 +33,9 @@ console.log(`[${PACKAGE_NAME}] Component profiles live in docs/profiles/componen
|
|
|
33
33
|
if (!hasAgentsFile) {
|
|
34
34
|
console.log(`[${PACKAGE_NAME}] To scaffold AGENTS.md for this project, run:`)
|
|
35
35
|
console.log(` npx ${PACKAGE_NAME} init-agents`)
|
|
36
|
+
console.log(`[${PACKAGE_NAME}] To install project-level skills, run:`)
|
|
37
|
+
console.log(` npx ${PACKAGE_NAME} init-skills`)
|
|
36
38
|
} else {
|
|
37
39
|
console.log(`[${PACKAGE_NAME}] AGENTS.md already exists in this project, so no scaffold was created automatically`)
|
|
40
|
+
console.log(`[${PACKAGE_NAME}] Project-level skills can be installed with: npx ${PACKAGE_NAME} init-skills`)
|
|
38
41
|
}
|
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,13 +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;
|
|
103
|
+
- apply the 24px top and 32px side/bottom padding rule to white content surfaces, not to the page root wrapper;
|
|
94
104
|
- keep filters and controls near the content they affect;
|
|
95
|
-
- use `UiField` around labeled
|
|
105
|
+
- use `UiField` around labeled textbox, select, number, date, and time controls;
|
|
96
106
|
- use `UiTable` for structured result lists;
|
|
97
107
|
- use `UiLink` for navigation and inline links, `UiButton` for commands;
|
|
98
108
|
- use `UiLoader` with `overlay: true` when loading should dim the covered page or module content;
|
|
99
109
|
- keep public imports on `@retailcrm/embed-ui-v1-components/remote`;
|
|
110
|
+
- when a component uses only the default slot, prefer `v-slot` on the component instead of `<template #default>`;
|
|
100
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.
|
|
101
114
|
|
|
102
115
|
## Default Recommendation For Table Screens
|
|
103
116
|
|
|
@@ -105,6 +118,8 @@ When building a registry, catalog, journal, search result, order list, customer
|
|
|
105
118
|
screen where users scan and refine datasets:
|
|
106
119
|
|
|
107
120
|
- put search and filters directly above `UiTable`;
|
|
121
|
+
- do not wrap `UiTable` in an extra white card or padded content surface;
|
|
122
|
+
- use a plain layout or scroll wrapper around `UiTable` only when width or overflow control is needed;
|
|
108
123
|
- use `UiTextbox` for free-text search and `UiSelect` or compact toggle controls for finite filters;
|
|
109
124
|
- keep filters, sorting, page, and page size in GET query parameters when the host app has routing;
|
|
110
125
|
- hydrate initial filter and pagination state from the current query;
|
|
@@ -116,7 +131,9 @@ screen where users scan and refine datasets:
|
|
|
116
131
|
- use chevron icon assets for table footer previous/next controls instead of text glyphs;
|
|
117
132
|
- add local CSS for table footer layout and states; use [`UiTable.yml`](./profiles/components/UiTable.yml)
|
|
118
133
|
for the reference table footer example;
|
|
119
|
-
- set `size="small"` on `UiLink` inside table cells so links match table body typography
|
|
134
|
+
- set `size="small"` on `UiLink` inside table cells so links match table body typography;
|
|
135
|
+
- prefer icon-only row action buttons inside dense table rows; keep the same action text in
|
|
136
|
+
`aria-label` and `UiTooltip`.
|
|
120
137
|
|
|
121
138
|
Suggested query names:
|
|
122
139
|
|
|
@@ -125,6 +142,28 @@ Suggested query names:
|
|
|
125
142
|
- `sort` and `direction` for sorting;
|
|
126
143
|
- `page` and `pageSize` for pagination.
|
|
127
144
|
|
|
145
|
+
## Default Recommendation For Forms
|
|
146
|
+
|
|
147
|
+
When building forms with remote controls:
|
|
148
|
+
|
|
149
|
+
- use `v-model:value` for value-bearing controls such as `UiTextbox`, `UiSelect`, `UiNumberStepper`,
|
|
150
|
+
and `UiSwitch`;
|
|
151
|
+
- if a field looks filled but backend validation receives an empty value, check the Network payload
|
|
152
|
+
before changing the component binding;
|
|
153
|
+
- use `UiField` for labeled text, select, date, and number controls;
|
|
154
|
+
- forward `UiField` slot props into the actual child control when it accepts `id`, especially
|
|
155
|
+
`UiTextbox`, `UiSelect`, and `UiNumberStepper`;
|
|
156
|
+
- put validation errors inside the relevant field composition, not as unrelated sibling blocks;
|
|
157
|
+
- when a component uses only the default slot, prefer `v-slot` on the component instead of
|
|
158
|
+
`<template #default>`;
|
|
159
|
+
- do not wrap `UiSwitch` in `UiField`; place the switch next to a visible label and optional hint,
|
|
160
|
+
and connect `UiSwitch :id` with `label :for`.
|
|
161
|
+
- do not wrap simple `UiCheckbox` rows in `UiField`; place the checkbox next to a visible label and optional hint.
|
|
162
|
+
- use `UiSwitch` for compact immediate boolean settings, feature toggles, and enable/disable flags.
|
|
163
|
+
- use `UiCheckbox` for checkbox groups, table row selection, "select all", acknowledgements, and checkbox-shaped choices.
|
|
164
|
+
- use `UiNumberStepper` for bounded numeric settings, quantities, durations, limits, and values with meaningful increments.
|
|
165
|
+
- use `UiTextbox` for numeric-looking text such as phone numbers, codes, identifiers, and free-form numeric strings.
|
|
166
|
+
|
|
128
167
|
## Default Recommendation For Widgets
|
|
129
168
|
|
|
130
169
|
When building a widget mounted into a CRM target, keep the inline target UI compact and predictable:
|
package/docs/FORMAT.md
CHANGED
|
@@ -242,6 +242,7 @@ A short list of rules specifically for code generation:
|
|
|
242
242
|
- Use short, concrete statements instead of vague praise.
|
|
243
243
|
- Use the exact names of props, emits, and slots.
|
|
244
244
|
- For slots, describe not only the name, but also what the slot does and which content restrictions exist.
|
|
245
|
+
- In examples with only a default slot, prefer `v-slot` on the component instead of `<template #default>`.
|
|
245
246
|
- For styling, distinguish between safe CSS variables and descriptive class names.
|
|
246
247
|
- Keep runnable examples in YAML profiles when they clarify safe public usage.
|
|
247
248
|
- Do not mix "how the component looks right now" with "what is publicly guaranteed".
|
package/docs/STYLING.md
CHANGED
|
@@ -77,6 +77,13 @@ Base font family:
|
|
|
77
77
|
|
|
78
78
|
- `-apple-system, BlinkMacSystemFont, "Segoe UI", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", "Roboto", sans-serif`
|
|
79
79
|
|
|
80
|
+
Extension UI should normally not set `font-family` in local styles. Let the CRM host and Embed UI
|
|
81
|
+
components inherit the shared stack.
|
|
82
|
+
|
|
83
|
+
If a local style must set `font-family`, use the shared stack above exactly. Do not replace it with
|
|
84
|
+
Arial, Inter, Roboto-only, or another custom stack unless the task or project `AGENTS.md` records a
|
|
85
|
+
project-specific design requirement.
|
|
86
|
+
|
|
80
87
|
Main sizes:
|
|
81
88
|
|
|
82
89
|
- `h1`: `40px / 44px`
|
|
@@ -125,6 +132,21 @@ Profiles split CSS variables into practical groups:
|
|
|
125
132
|
- `internal_layout_variables`
|
|
126
133
|
useful for reasoning and debugging, but not recommended as external override points.
|
|
127
134
|
|
|
135
|
+
## State Styling
|
|
136
|
+
|
|
137
|
+
Interactive states should come from the component implementation, documented props, and documented
|
|
138
|
+
CSS variables.
|
|
139
|
+
|
|
140
|
+
For controls such as `UiSelect`, `UiTextbox`, and buttons:
|
|
141
|
+
|
|
142
|
+
- do not add custom selected, active, focused, pressed, or invalid outlines/borders just because a
|
|
143
|
+
state is visible in Figma or CRM;
|
|
144
|
+
- first check the component profile for the exact root/state classes, zones, CSS variables, and
|
|
145
|
+
notes about safe overrides;
|
|
146
|
+
- use documented variables or local wrapper styles only when the profile says that pattern is safe;
|
|
147
|
+
- if design review asks for a state that the profile does not document, treat it as a project-specific
|
|
148
|
+
requirement and record that requirement before changing CSS.
|
|
149
|
+
|
|
128
150
|
## Typical Safe Strategy
|
|
129
151
|
|
|
130
152
|
For style-sensitive generation:
|
|
@@ -132,7 +154,43 @@ For style-sensitive generation:
|
|
|
132
154
|
1. choose the correct component and size prop first;
|
|
133
155
|
2. use documented slots to create the right visual zones;
|
|
134
156
|
3. use documented CSS variables if a theme override is needed;
|
|
135
|
-
4. avoid relying on internal descendant selectors unless the profile says that is safe
|
|
157
|
+
4. avoid relying on internal descendant selectors unless the profile says that is safe;
|
|
158
|
+
5. state which styling source is being followed when the change was requested by design feedback.
|
|
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
|
+
|
|
182
|
+
## Design Feedback Triage
|
|
183
|
+
|
|
184
|
+
When design feedback does not match the current implementation, resolve the source before editing:
|
|
185
|
+
|
|
186
|
+
1. check public RetailCRM docs, local package docs, the component profile, the page profile, this
|
|
187
|
+
`STYLING.md`, Figma, and CRM computed styles when available;
|
|
188
|
+
2. if Embed UI docs describe the rule, follow the documented rule or update the component profile
|
|
189
|
+
before relying on source-code internals;
|
|
190
|
+
3. if Embed UI docs do not describe the requested rule, treat it as a project-specific design
|
|
191
|
+
requirement;
|
|
192
|
+
4. record project-specific values in the task text or project `AGENTS.md`;
|
|
193
|
+
5. when sources conflict, state the chosen source before changing markup or CSS.
|
|
136
194
|
|
|
137
195
|
## How To Mention Styles In Profiles
|
|
138
196
|
|
|
@@ -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,23 +12,21 @@ public_import:
|
|
|
12
12
|
related_components:
|
|
13
13
|
- UiTextbox
|
|
14
14
|
- UiSelect
|
|
15
|
-
-
|
|
15
|
+
- UiNumberStepper
|
|
16
16
|
- UiDatePicker
|
|
17
17
|
|
|
18
18
|
examples:
|
|
19
19
|
- title: Basic usage
|
|
20
20
|
code: |
|
|
21
21
|
<template>
|
|
22
|
-
<UiField id="name-field" label="Name" hint="At least 3 characters">
|
|
23
|
-
<
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
:
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
/>
|
|
31
|
-
</template>
|
|
22
|
+
<UiField v-slot="field" id="name-field" label="Name" hint="At least 3 characters">
|
|
23
|
+
<UiTextbox
|
|
24
|
+
:id="field.id"
|
|
25
|
+
:input-attributes="{
|
|
26
|
+
'aria-labelledby': field.ariaLabelledby,
|
|
27
|
+
'aria-invalid': field.ariaInvalid,
|
|
28
|
+
}"
|
|
29
|
+
/>
|
|
32
30
|
</UiField>
|
|
33
31
|
</template>
|
|
34
32
|
|
|
@@ -61,6 +59,56 @@ examples:
|
|
|
61
59
|
<script lang="ts" setup>
|
|
62
60
|
import { UiField, UiTextbox } from '@retailcrm/embed-ui-v1-components/remote'
|
|
63
61
|
</script>
|
|
62
|
+
- title: Select inside a field
|
|
63
|
+
code: |
|
|
64
|
+
<template>
|
|
65
|
+
<UiField v-slot="field" id="manager-field" label="Manager">
|
|
66
|
+
<UiSelect
|
|
67
|
+
:id="field.id"
|
|
68
|
+
v-model:value="manager"
|
|
69
|
+
:invalid="field.ariaInvalid === 'true'"
|
|
70
|
+
placeholder="Select manager"
|
|
71
|
+
>
|
|
72
|
+
<UiSelectOption value="anna" label="Anna Smith" />
|
|
73
|
+
<UiSelectOption value="ilya" label="Ilya Johnson" />
|
|
74
|
+
</UiSelect>
|
|
75
|
+
</UiField>
|
|
76
|
+
</template>
|
|
77
|
+
|
|
78
|
+
<script lang="ts" setup>
|
|
79
|
+
import { ref } from 'vue'
|
|
80
|
+
|
|
81
|
+
import {
|
|
82
|
+
UiField,
|
|
83
|
+
UiSelect,
|
|
84
|
+
UiSelectOption,
|
|
85
|
+
} from '@retailcrm/embed-ui-v1-components/remote'
|
|
86
|
+
|
|
87
|
+
const manager = ref<string | null>(null)
|
|
88
|
+
</script>
|
|
89
|
+
- title: Number stepper inside a field
|
|
90
|
+
code: |
|
|
91
|
+
<template>
|
|
92
|
+
<UiField v-slot="field" id="duration-field" label="Duration, minutes">
|
|
93
|
+
<UiNumberStepper
|
|
94
|
+
:id="field.id"
|
|
95
|
+
v-model:value="duration"
|
|
96
|
+
:min="0"
|
|
97
|
+
:step="15"
|
|
98
|
+
/>
|
|
99
|
+
</UiField>
|
|
100
|
+
</template>
|
|
101
|
+
|
|
102
|
+
<script lang="ts" setup>
|
|
103
|
+
import { ref } from 'vue'
|
|
104
|
+
|
|
105
|
+
import {
|
|
106
|
+
UiField,
|
|
107
|
+
UiNumberStepper,
|
|
108
|
+
} from '@retailcrm/embed-ui-v1-components/remote'
|
|
109
|
+
|
|
110
|
+
const duration = ref(30)
|
|
111
|
+
</script>
|
|
64
112
|
use_when:
|
|
65
113
|
- You need a labeled form control with consistent field semantics.
|
|
66
114
|
- You need to pass id, aria-labelledby, and aria-invalid into an inner control.
|
|
@@ -109,9 +157,12 @@ api:
|
|
|
109
157
|
- one form control
|
|
110
158
|
- UiTextbox
|
|
111
159
|
- UiSelect
|
|
160
|
+
- UiNumberStepper
|
|
112
161
|
- UiDatePicker
|
|
113
162
|
avoid:
|
|
114
163
|
- several unrelated controls
|
|
164
|
+
- UiSwitch setting rows
|
|
165
|
+
- UiCheckbox setting rows
|
|
115
166
|
- decorative layout only
|
|
116
167
|
- raw content without using slot props
|
|
117
168
|
layout_effect: The control sits below the headline and inherits field-level semantics through slot props.
|
|
@@ -248,6 +299,7 @@ composition:
|
|
|
248
299
|
works_well_with:
|
|
249
300
|
- UiTextbox
|
|
250
301
|
- UiSelect
|
|
302
|
+
- UiNumberStepper
|
|
251
303
|
- UiDatePicker
|
|
252
304
|
- UiTimePicker
|
|
253
305
|
patterns:
|
|
@@ -258,7 +310,12 @@ composition:
|
|
|
258
310
|
ai_notes:
|
|
259
311
|
do:
|
|
260
312
|
- Forward slot props into the actual control in most form scenarios.
|
|
261
|
-
-
|
|
313
|
+
- Pass field.id to child controls that accept id, including UiTextbox, UiSelect, and UiNumberStepper.
|
|
314
|
+
- Use v-slot on UiField when the field uses only the default slot.
|
|
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.
|
|
262
317
|
avoid:
|
|
263
318
|
- Do not use UiField as a generic visual container without control semantics.
|
|
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.
|
|
264
321
|
- Do not ignore id and ariaLabelledby when accessibility matters.
|
|
@@ -44,18 +44,30 @@ examples:
|
|
|
44
44
|
</div>
|
|
45
45
|
|
|
46
46
|
<template #footer>
|
|
47
|
-
<
|
|
48
|
-
|
|
49
|
-
|
|
47
|
+
<div style="display: flex; align-items: center; gap: 12px;">
|
|
48
|
+
<UiButton @click="modalSidebarInner = false">
|
|
49
|
+
Save
|
|
50
|
+
</UiButton>
|
|
51
|
+
|
|
52
|
+
<UiButton appearance="secondary" @click="modalSidebarInner = false">
|
|
53
|
+
Close
|
|
54
|
+
</UiButton>
|
|
55
|
+
</div>
|
|
50
56
|
</template>
|
|
51
57
|
</UiModalSidebar>
|
|
52
58
|
</div>
|
|
53
59
|
</div>
|
|
54
60
|
|
|
55
61
|
<template v-if="footer" #footer>
|
|
56
|
-
<
|
|
57
|
-
|
|
58
|
-
|
|
62
|
+
<div style="display: flex; align-items: center; gap: 12px;">
|
|
63
|
+
<UiButton @click="open = false">
|
|
64
|
+
Save
|
|
65
|
+
</UiButton>
|
|
66
|
+
|
|
67
|
+
<UiButton appearance="secondary" @click="open = false">
|
|
68
|
+
Close
|
|
69
|
+
</UiButton>
|
|
70
|
+
</div>
|
|
59
71
|
</template>
|
|
60
72
|
</UiModalSidebar>
|
|
61
73
|
</div>
|
|
@@ -125,6 +137,9 @@ ai_notes:
|
|
|
125
137
|
do:
|
|
126
138
|
- Use UiModalSidebar when page context should stay visually connected to the modal content.
|
|
127
139
|
- Use for inspect, edit, or secondary workflows that relate to the current list or page.
|
|
140
|
+
- Group several footer buttons in flex rows; 12px and 16px are the common gaps inside a group.
|
|
141
|
+
- Footer actions may be split into left and right groups when their meanings differ.
|
|
142
|
+
- Put destructive icon-only actions on the right and confirm them with UiPopconfirm okVariant="danger"; the confirmation button is primary by default.
|
|
128
143
|
avoid:
|
|
129
144
|
- Do not use for short confirmations; use UiModalWindow.
|
|
130
145
|
|
|
@@ -133,6 +148,7 @@ composition:
|
|
|
133
148
|
- UiTable
|
|
134
149
|
- UiField
|
|
135
150
|
- UiButton
|
|
151
|
+
- UiPopconfirm
|
|
136
152
|
patterns:
|
|
137
153
|
- title: Row detail side panel
|
|
138
154
|
notes: Open from a table row when the user should keep list context visible.
|
|
@@ -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:
|
|
@@ -312,6 +312,10 @@ styling:
|
|
|
312
312
|
- The trigger reuses the textbox visual model.
|
|
313
313
|
- The dropdown reuses popper variables for padding, radius, and floating surface geometry.
|
|
314
314
|
- Classes are descriptive implementation hooks, not a stable external styling contract.
|
|
315
|
+
- Selected, active, focused, pressed, invalid, and disabled states should come from UiSelect and
|
|
316
|
+
UiSelectOption implementation styles or documented CSS variables.
|
|
317
|
+
- Do not add custom outlines or borders for selected options unless the task records a
|
|
318
|
+
project-specific design requirement.
|
|
315
319
|
root_classes:
|
|
316
320
|
- .ui-v1-select
|
|
317
321
|
- .ui-v1-select__trigger
|
|
@@ -375,6 +379,8 @@ behavior:
|
|
|
375
379
|
- In multiple mode values are toggled inside the array model.
|
|
376
380
|
- Filtering matches option labels and descriptions.
|
|
377
381
|
- If nothing matches, a no-result block is shown.
|
|
382
|
+
- Selected option styling is owned by the select option implementation; do not recreate it with
|
|
383
|
+
local borders or outline styles.
|
|
378
384
|
keyboard:
|
|
379
385
|
- Arrow keys move the active highlight.
|
|
380
386
|
- Escape closes the dropdown.
|
|
@@ -414,3 +420,5 @@ ai_notes:
|
|
|
414
420
|
- Do not place arbitrary div wrappers inside the option tree.
|
|
415
421
|
- Do not choose UiSelect when free text input is the real need.
|
|
416
422
|
- Do not assume the dropdown lives in normal document flow next to the trigger.
|
|
423
|
+
- Do not add custom selected-option outlines, borders, or active-state chrome unless a
|
|
424
|
+
project-specific design requirement explicitly asks for it.
|
|
@@ -70,5 +70,7 @@ styling:
|
|
|
70
70
|
notes:
|
|
71
71
|
- Use documented props and slots as the primary styling API.
|
|
72
72
|
- Internal .ui-v1-* classes are descriptive implementation details for reasoning and debugging unless a profile marks them as public theme hooks.
|
|
73
|
+
- Selected, active, focused, pressed, and disabled option states are owned by UiSelect/UiSelectOption implementation styles.
|
|
74
|
+
- Do not add custom outlines or borders for selected options unless the task records a project-specific design requirement.
|
|
73
75
|
root_classes:
|
|
74
76
|
- .ui-v1-select-option
|
|
@@ -58,11 +58,74 @@ examples:
|
|
|
58
58
|
|
|
59
59
|
const enabled = ref(false)
|
|
60
60
|
</script>
|
|
61
|
+
- title: Settings row with label and hint
|
|
62
|
+
code: |
|
|
63
|
+
<template>
|
|
64
|
+
<div class="settings-switch-row">
|
|
65
|
+
<UiSwitch
|
|
66
|
+
:id="switchId"
|
|
67
|
+
v-model:value="enabled"
|
|
68
|
+
/>
|
|
69
|
+
|
|
70
|
+
<div class="settings-switch-row__content">
|
|
71
|
+
<label
|
|
72
|
+
class="settings-switch-row__label"
|
|
73
|
+
:for="switchId"
|
|
74
|
+
>
|
|
75
|
+
Enable automatic assignment
|
|
76
|
+
</label>
|
|
77
|
+
|
|
78
|
+
<div class="settings-switch-row__hint">
|
|
79
|
+
New requests will be assigned to available managers.
|
|
80
|
+
</div>
|
|
81
|
+
</div>
|
|
82
|
+
</div>
|
|
83
|
+
</template>
|
|
84
|
+
|
|
85
|
+
<script lang="ts" setup>
|
|
86
|
+
import { ref, useId } from 'vue'
|
|
87
|
+
|
|
88
|
+
import { UiSwitch } from '@retailcrm/embed-ui-v1-components/remote'
|
|
89
|
+
|
|
90
|
+
const enabled = ref(false)
|
|
91
|
+
const switchId = useId()
|
|
92
|
+
</script>
|
|
93
|
+
|
|
94
|
+
<style lang="less" scoped>
|
|
95
|
+
@import (reference) '@retailcrm/embed-ui-v1-components/assets/stylesheets/layout.less';
|
|
96
|
+
@import (reference) '@retailcrm/embed-ui-v1-components/assets/stylesheets/palette.less';
|
|
97
|
+
@import (reference) '@retailcrm/embed-ui-v1-components/assets/stylesheets/typography.less';
|
|
98
|
+
|
|
99
|
+
.settings-switch-row {
|
|
100
|
+
display: flex;
|
|
101
|
+
align-items: flex-start;
|
|
102
|
+
gap: 3 * @spacing-unit;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.settings-switch-row__content {
|
|
106
|
+
display: grid;
|
|
107
|
+
gap: @spacing-xxs;
|
|
108
|
+
min-width: 0;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.settings-switch-row__label {
|
|
112
|
+
color: @black-500;
|
|
113
|
+
.text-small-accent();
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.settings-switch-row__hint {
|
|
117
|
+
color: @grey-800;
|
|
118
|
+
.text-small();
|
|
119
|
+
}
|
|
120
|
+
</style>
|
|
61
121
|
use_when:
|
|
62
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.
|
|
63
124
|
|
|
64
125
|
avoid_when:
|
|
65
126
|
- You need checkbox-group semantics.
|
|
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.
|
|
66
129
|
|
|
67
130
|
api:
|
|
68
131
|
key_props:
|
|
@@ -93,18 +156,24 @@ behavior:
|
|
|
93
156
|
|
|
94
157
|
composition:
|
|
95
158
|
works_well_with:
|
|
96
|
-
-
|
|
159
|
+
- visible label
|
|
160
|
+
- hint text
|
|
97
161
|
patterns:
|
|
98
162
|
- title: Settings row
|
|
99
|
-
notes:
|
|
163
|
+
notes: Place UiSwitch on the left, label and hint on the right, and connect UiSwitch id with label for.
|
|
100
164
|
avoid:
|
|
101
165
|
- Do not use without a nearby label; the switch alone does not explain what will change.
|
|
166
|
+
- Do not wrap UiSwitch in UiField.
|
|
102
167
|
|
|
103
168
|
ai_notes:
|
|
104
169
|
do:
|
|
105
170
|
- Use for compact feature toggles and enable or disable settings.
|
|
171
|
+
- Generate an id, pass it to UiSwitch, and connect the visible label with label for.
|
|
172
|
+
- Put helper text below the label in the settings row, not in UiField hint.
|
|
106
173
|
avoid:
|
|
107
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.
|
|
176
|
+
- Do not use UiField around UiSwitch.
|
|
108
177
|
|
|
109
178
|
accessibility:
|
|
110
179
|
notes:
|
|
@@ -10,13 +10,16 @@ public_import:
|
|
|
10
10
|
|
|
11
11
|
related_components:
|
|
12
12
|
- UiAvatar
|
|
13
|
+
- UiButton
|
|
13
14
|
- UiCheckbox
|
|
14
15
|
- UiLink
|
|
16
|
+
- UiPopperConnector
|
|
15
17
|
- UiTableColumn
|
|
16
18
|
- UiTableFooterButton
|
|
17
19
|
- UiTableFooterSection
|
|
18
20
|
- UiTableSorter
|
|
19
21
|
- UiTag
|
|
22
|
+
- UiTooltip
|
|
20
23
|
|
|
21
24
|
examples:
|
|
22
25
|
- title: Basic table
|
|
@@ -64,6 +67,68 @@ examples:
|
|
|
64
67
|
Sent: '#1FA971',
|
|
65
68
|
}
|
|
66
69
|
</script>
|
|
70
|
+
- title: Icon-only row action with tooltip
|
|
71
|
+
notes:
|
|
72
|
+
- Use narrow action columns for row commands.
|
|
73
|
+
- Keep the visible control icon-only; put the action text in aria-label and UiTooltip.
|
|
74
|
+
code: |
|
|
75
|
+
<template>
|
|
76
|
+
<UiTable
|
|
77
|
+
bordered
|
|
78
|
+
:rows="rows"
|
|
79
|
+
row-key="id"
|
|
80
|
+
>
|
|
81
|
+
<UiTableColumn label="Name">
|
|
82
|
+
<template #cell="{ row }">
|
|
83
|
+
{{ row.name }}
|
|
84
|
+
</template>
|
|
85
|
+
</UiTableColumn>
|
|
86
|
+
|
|
87
|
+
<UiTableColumn :width="48" label="" trim>
|
|
88
|
+
<template #cell="{ row }">
|
|
89
|
+
<UiPopperConnector>
|
|
90
|
+
<UiButton
|
|
91
|
+
:aria-label="`Edit ${row.name}`"
|
|
92
|
+
appearance="tertiary"
|
|
93
|
+
size="sm"
|
|
94
|
+
@click="edit(row)"
|
|
95
|
+
>
|
|
96
|
+
<IconEdit aria-hidden="true" />
|
|
97
|
+
</UiButton>
|
|
98
|
+
|
|
99
|
+
<UiTooltip>
|
|
100
|
+
Edit {{ row.name }}
|
|
101
|
+
</UiTooltip>
|
|
102
|
+
</UiPopperConnector>
|
|
103
|
+
</template>
|
|
104
|
+
</UiTableColumn>
|
|
105
|
+
</UiTable>
|
|
106
|
+
</template>
|
|
107
|
+
|
|
108
|
+
<script lang="ts" setup>
|
|
109
|
+
import IconEdit from '@retailcrm/embed-ui-v1-components/assets/sprites/ui/edit.svg'
|
|
110
|
+
import {
|
|
111
|
+
UiButton,
|
|
112
|
+
UiPopperConnector,
|
|
113
|
+
UiTable,
|
|
114
|
+
UiTableColumn,
|
|
115
|
+
UiTooltip,
|
|
116
|
+
} from '@retailcrm/embed-ui-v1-components/remote'
|
|
117
|
+
|
|
118
|
+
type Row = {
|
|
119
|
+
id: number
|
|
120
|
+
name: string
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const rows: Row[] = [
|
|
124
|
+
{ id: 101, name: 'Anna Smith' },
|
|
125
|
+
{ id: 102, name: 'Ilya Johnson' },
|
|
126
|
+
]
|
|
127
|
+
|
|
128
|
+
const edit = (row: Row) => {
|
|
129
|
+
console.log(row)
|
|
130
|
+
}
|
|
131
|
+
</script>
|
|
67
132
|
- title: Expandable rows table
|
|
68
133
|
notes:
|
|
69
134
|
- Use the expand slot and cell toggle helper for expandable row details.
|
|
@@ -421,6 +486,7 @@ use_when:
|
|
|
421
486
|
|
|
422
487
|
avoid_when:
|
|
423
488
|
- You need a simple list or card layout.
|
|
489
|
+
- You need content inside a generic white card surface instead of a table.
|
|
424
490
|
|
|
425
491
|
api:
|
|
426
492
|
key_props:
|
|
@@ -491,6 +557,7 @@ geometry:
|
|
|
491
557
|
- Root fills available width.
|
|
492
558
|
- fixed=true switches table-layout from auto to fixed.
|
|
493
559
|
- bordered=true adds border and corner rounding through CSS variables.
|
|
560
|
+
- Do not wrap UiTable in an extra white content surface; use a plain layout or scroll wrapper only when needed.
|
|
494
561
|
- Filters and search controls should usually be placed above the table, not in the table root.
|
|
495
562
|
- Table-scoped pagination belongs in footer slots when custom controls are needed.
|
|
496
563
|
|
|
@@ -593,6 +660,12 @@ composition:
|
|
|
593
660
|
- Add scoped CSS for footer meta height, control-row background, pagination button size, active page state, arrow icon size, and vertical dividers.
|
|
594
661
|
- Persist page and page size in URL query parameters when routing is available.
|
|
595
662
|
- Reset page to 1 when filters or sorting change.
|
|
663
|
+
actions:
|
|
664
|
+
notes:
|
|
665
|
+
- Prefer narrow action columns with icon-only UiButton controls for row commands.
|
|
666
|
+
- Keep visible button text out of dense table rows; provide aria-label and matching UiTooltip text.
|
|
667
|
+
- Use package sprite icons and set aria-hidden on the icon itself.
|
|
668
|
+
- Wrap action buttons and UiTooltip in UiPopperConnector. Add UiPopperTarget only for custom non-component targets.
|
|
596
669
|
|
|
597
670
|
ai_notes:
|
|
598
671
|
do:
|
|
@@ -603,9 +676,12 @@ ai_notes:
|
|
|
603
676
|
- Compose footer controls with UiTableFooterSection and UiTableFooterButton.
|
|
604
677
|
- Copy the Entity list table footer example when building a realistic entity-list footer.
|
|
605
678
|
- Use UiTableSorter for sortable headers.
|
|
679
|
+
- Use icon-only row action buttons with aria-label and UiTooltip in action columns.
|
|
606
680
|
avoid:
|
|
607
681
|
- Do not hide table filters in page header actions.
|
|
682
|
+
- Do not wrap UiTable in an additional white card, panel, or content-surface container.
|
|
608
683
|
- Do not use UiButton inside table footer pagination.
|
|
684
|
+
- Do not put visible text buttons in dense table action columns.
|
|
609
685
|
- Do not put pagination only in local state when the screen has routable result sets.
|
|
610
686
|
- Do not import table internals from host or src paths.
|
|
611
687
|
|
|
@@ -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:
|
|
@@ -60,6 +60,7 @@ ai_notes:
|
|
|
60
60
|
do:
|
|
61
61
|
- Use UiTooltip for tooltip semantics and UiPopper for lower-level floating behavior.
|
|
62
62
|
- Keep tooltip content short and non-essential.
|
|
63
|
+
- For icon-only controls, keep tooltip text aligned with the control aria-label.
|
|
63
64
|
avoid:
|
|
64
65
|
- Do not put forms, tables, or multi-step interactions in tooltips.
|
|
65
66
|
|
|
@@ -20,7 +20,11 @@ expected_structure:
|
|
|
20
20
|
- Header actions can be accompanied by text information or status labels.
|
|
21
21
|
- Optional top tabs.
|
|
22
22
|
- Main content sits on a white content surface.
|
|
23
|
+
- The white content surface owns the 24px top and 32px side/bottom padding.
|
|
24
|
+
- The page root wrapper should not repeat that padding.
|
|
23
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.
|
|
24
28
|
- A bottom save panel is optional.
|
|
25
29
|
- If a bottom save panel is used, its main save or apply action should be Success Primary.
|
|
26
30
|
|
|
@@ -53,8 +57,14 @@ recommended_components:
|
|
|
53
57
|
ai_notes:
|
|
54
58
|
do:
|
|
55
59
|
- Keep form controls grouped by task or semantic section.
|
|
56
|
-
- 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.
|
|
57
63
|
- Use tabs only when they reduce visible complexity without hiding required work.
|
|
58
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.
|
|
59
66
|
avoid:
|
|
60
67
|
- Do not create a decorative landing page for operational settings.
|
|
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,12 +18,16 @@ 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.
|
|
24
25
|
- Filter apply action uses a default secondary 36px text button.
|
|
25
26
|
- Filter reset action uses a danger secondary 36px icon button.
|
|
26
27
|
- Entity data is shown in a table.
|
|
28
|
+
- UiTable should not be wrapped in an extra white content surface.
|
|
29
|
+
- A wrapper around UiTable should only handle layout, width, or scrolling, without card chrome or content-surface padding.
|
|
30
|
+
- Row action columns should use icon-only buttons with aria-label and UiTooltip instead of visible text buttons.
|
|
27
31
|
- The table may scroll and may support export.
|
|
28
32
|
|
|
29
33
|
recommended_components:
|
|
@@ -61,6 +65,11 @@ ai_notes:
|
|
|
61
65
|
- Keep filters and table controls directly above the table.
|
|
62
66
|
- Persist filters, sorting, page, and page size in query parameters when routing is available.
|
|
63
67
|
- Reset page to 1 when filters or sorting change.
|
|
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.
|
|
64
70
|
avoid:
|
|
65
71
|
- Do not hide filters in page header actions.
|
|
66
72
|
- Do not put pagination only in local state when the result set is routable.
|
|
73
|
+
- Do not place UiTable inside an additional white card or padded content surface.
|
|
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.
|
|
@@ -44,18 +44,30 @@ examples:
|
|
|
44
44
|
</UiButton>
|
|
45
45
|
</div>
|
|
46
46
|
|
|
47
|
-
<
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
47
|
+
<div class="modal-sidebar-footer__aside">
|
|
48
|
+
<UiPopconfirm
|
|
49
|
+
ok-variant="danger"
|
|
50
|
+
title="Delete item?"
|
|
51
|
+
@ok="deleteItem"
|
|
52
|
+
>
|
|
53
|
+
<template #trigger="{ open: popconfirmOpen }">
|
|
54
|
+
<UiButton
|
|
55
|
+
:active="popconfirmOpen"
|
|
56
|
+
aria-label="Delete"
|
|
57
|
+
appearance="tertiary"
|
|
58
|
+
class="modal-sidebar-footer__delete"
|
|
59
|
+
variant="danger"
|
|
60
|
+
>
|
|
61
|
+
<IconDelete
|
|
62
|
+
aria-hidden="true"
|
|
63
|
+
class="modal-sidebar-footer__delete-icon"
|
|
64
|
+
/>
|
|
65
|
+
</UiButton>
|
|
66
|
+
</template>
|
|
67
|
+
|
|
68
|
+
This action cannot be undone.
|
|
69
|
+
</UiPopconfirm>
|
|
70
|
+
</div>
|
|
59
71
|
</div>
|
|
60
72
|
</template>
|
|
61
73
|
</UiModalSidebar>
|
|
@@ -69,6 +81,7 @@ examples:
|
|
|
69
81
|
import {
|
|
70
82
|
UiButton,
|
|
71
83
|
UiModalSidebar,
|
|
84
|
+
UiPopconfirm,
|
|
72
85
|
} from '@retailcrm/embed-ui-v1-components/remote'
|
|
73
86
|
|
|
74
87
|
const open = ref(false)
|
|
@@ -95,6 +108,12 @@ examples:
|
|
|
95
108
|
}
|
|
96
109
|
|
|
97
110
|
.modal-sidebar-footer__main {
|
|
111
|
+
display: flex;
|
|
112
|
+
align-items: center;
|
|
113
|
+
gap: 12px;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.modal-sidebar-footer__aside {
|
|
98
117
|
display: flex;
|
|
99
118
|
align-items: center;
|
|
100
119
|
gap: 16px;
|
|
@@ -127,6 +146,10 @@ expected_structure:
|
|
|
127
146
|
- Fixed footer at the bottom.
|
|
128
147
|
- Footer usually contains save, cancel or close, and delete actions.
|
|
129
148
|
- Footer can also contain a copy button or auxiliary text.
|
|
149
|
+
- Footer actions may be split into left and right groups.
|
|
150
|
+
- Footer action groups usually use a 12px or 16px gap between neighboring buttons.
|
|
151
|
+
- Destructive footer actions are often icon-only buttons aligned to the right edge.
|
|
152
|
+
- Destructive icon-only actions should usually open UiPopconfirm with okVariant="danger"; its confirmation button is primary by default.
|
|
130
153
|
- Content can be flexible, but should stay compact.
|
|
131
154
|
- Small tables can be placed in sidebars.
|
|
132
155
|
|
|
@@ -135,6 +158,8 @@ recommended_components:
|
|
|
135
158
|
profile: ../components/UiModalSidebar.yml
|
|
136
159
|
- name: UiButton
|
|
137
160
|
profile: ../components/UiButton.yml
|
|
161
|
+
- name: UiPopconfirm
|
|
162
|
+
profile: ../components/UiPopconfirm.yml
|
|
138
163
|
- name: UiTag
|
|
139
164
|
profile: ../components/UiTag.yml
|
|
140
165
|
- name: UiField
|
|
@@ -151,6 +176,9 @@ ai_notes:
|
|
|
151
176
|
do:
|
|
152
177
|
- Use for inspect, edit, or secondary workflows related to the current list or page.
|
|
153
178
|
- Keep the flow compact.
|
|
179
|
+
- Split footer actions into meaningful left and right groups when secondary or destructive actions need separation.
|
|
180
|
+
- Use 12px or 16px gaps inside each footer action group.
|
|
181
|
+
- Put destructive icon-only actions on the right and confirm them with UiPopconfirm okVariant="danger"; the confirmation button is primary by default.
|
|
154
182
|
avoid:
|
|
155
183
|
- Do not use for bulky or complex interfaces.
|
|
156
184
|
- Do not use collapse blocks inside sidebars.
|
|
@@ -14,6 +14,7 @@ examples_from_design:
|
|
|
14
14
|
layout_rules:
|
|
15
15
|
- Content is placed on surfaces.
|
|
16
16
|
- The most common surface spans the full screen width.
|
|
17
|
+
- Surface padding belongs to each content surface, not to the page root wrapper.
|
|
17
18
|
- Semantic blocks can be arranged vertically and horizontally.
|
|
18
19
|
- Distance between blocks is 24px.
|
|
19
20
|
- Allowed width distributions are 100%, 50% / 50%, and 30% / 70%.
|
|
@@ -25,3 +26,4 @@ ai_notes:
|
|
|
25
26
|
avoid:
|
|
26
27
|
- Do not use marketing-style layouts.
|
|
27
28
|
- Do not split content into columns when a single linear form is easier to complete.
|
|
29
|
+
- Do not duplicate content-surface padding on the page root wrapper.
|
|
@@ -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.
|
|
@@ -29,6 +31,8 @@ shared_rules:
|
|
|
29
31
|
- Components inside pages and blocks should be spaced by values divisible by 4px.
|
|
30
32
|
- Common spacing values are 4, 8, 12, 16, 20, 24, 28, and 32px.
|
|
31
33
|
- Content blocks commonly use 32px left, right, and bottom padding and 24px top padding.
|
|
34
|
+
- The 24px top and 32px side/bottom padding rule belongs to white content surfaces, not to the page root wrapper.
|
|
35
|
+
- Do not duplicate content-surface padding on the page root; outer page spacing is controlled by the CRM host layout.
|
|
32
36
|
- Prefer public components from @retailcrm/embed-ui-v1-components/remote.
|
|
33
37
|
- Do not invent custom chrome for pages, forms, tables, tabs, buttons, modals, or sidebars when a documented component fits.
|
|
34
38
|
|
|
@@ -58,7 +62,10 @@ ai_notes:
|
|
|
58
62
|
- Choose ModalWindow or a full page when content needs wide tables, several sections, or complex controls.
|
|
59
63
|
- Keep operational CRM screens dense, scannable, and work-focused.
|
|
60
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.
|
|
61
66
|
avoid:
|
|
62
67
|
- Do not turn internal CRM work screens into marketing-style layouts.
|
|
63
68
|
- Do not hide filters or primary workflow controls away from the content they affect.
|
|
64
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.
|
|
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.26",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "RetailDriverLLC <integration@retailcrm.ru>",
|
|
8
8
|
"repository": {
|
|
@@ -48,7 +48,8 @@
|
|
|
48
48
|
"docs",
|
|
49
49
|
"index.cjs",
|
|
50
50
|
"index.js",
|
|
51
|
-
"README.md"
|
|
51
|
+
"README.md",
|
|
52
|
+
"templates"
|
|
52
53
|
],
|
|
53
54
|
"scripts": {
|
|
54
55
|
"build": "yarn build:host && yarn build:remote",
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: embed-ui-v1-components-ui
|
|
3
|
+
description: Use when building, editing, or reviewing RetailCRM JS module UI with @retailcrm/embed-ui-v1-components. Covers page pattern selection, component profile lookup, styling constraints, table pagination fidelity, forms, widgets, and visual-review triage.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Embed UI v1 Components UI
|
|
7
|
+
|
|
8
|
+
Use this skill before changing frontend UI built with `@retailcrm/embed-ui-v1-components`.
|
|
9
|
+
|
|
10
|
+
## Reading order
|
|
11
|
+
|
|
12
|
+
1. Read project `AGENTS.md` if it exists.
|
|
13
|
+
2. Read `__PACKAGE_DOCS_PATH__/AGENTS.md`.
|
|
14
|
+
3. Read `__PACKAGE_DOCS_PATH__/docs/AI.md`.
|
|
15
|
+
4. Read `__PACKAGE_DOCS_PATH__/docs/PROFILES.md`.
|
|
16
|
+
5. Read `__PACKAGE_DOCS_PATH__/docs/STYLING.md` for typography, spacing, CSS variables, and selector boundaries.
|
|
17
|
+
6. Open the relevant component profiles from `__PACKAGE_DOCS_PATH__/docs/profiles/components/*.yml`.
|
|
18
|
+
7. For full screens, overlays, filters, tables, or settings layouts, open the relevant page profile from `__PACKAGE_DOCS_PATH__/docs/profiles/pages/*.yml`.
|
|
19
|
+
|
|
20
|
+
## Workflow
|
|
21
|
+
|
|
22
|
+
1. Choose the page pattern before writing code: `EntityListPage`, `CardSettingsPage`, `CollapseBlockPage`, `MultiColumnPage`, `ModalSidebar`, or `ModalWindow`.
|
|
23
|
+
2. List the component profiles you are using.
|
|
24
|
+
3. Use `@retailcrm/embed-ui-v1-components/remote` for extension UI components and `@retailcrm/embed-ui-v1-components/assets/...` for icons.
|
|
25
|
+
4. Do not import from `@retailcrm/embed-ui-v1-components/dist/*`, `@retailcrm/embed-ui-v1-components/host`, source files, or repository-only paths in extension frontend code.
|
|
26
|
+
5. Do not hand-roll buttons, inputs, selects, tables, or links when a documented Embed UI component exists.
|
|
27
|
+
6. Do not set arbitrary `font-family`; inherit the host/component stack unless a project-specific design requirement says otherwise.
|
|
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.
|
|
31
|
+
|
|
32
|
+
## High-signal checks
|
|
33
|
+
|
|
34
|
+
- For entity lists, use `UiTable`, `UiTableColumn`, and table footer slots.
|
|
35
|
+
- For table pagination, follow the reference example in `UiTable.yml`: button sizes, active state, dividers, arrow assets, and scoped selector pattern.
|
|
36
|
+
- For form fields, use `UiField` with the matching control and forward slot props such as `id` when the control accepts them.
|
|
37
|
+
- Put validation errors inside the relevant field composition, not as unrelated sibling blocks.
|
|
38
|
+
- For `UiSwitch`, use the documented switch + visible label + hint row instead of wrapping it in `UiField`.
|
|
39
|
+
- For simple `UiCheckbox` rows, use checkbox + visible label + optional hint instead of wrapping it in `UiField`.
|
|
40
|
+
- Use `UiSwitch` for compact boolean settings and `UiCheckbox` for checkbox groups, table row selection, select-all, and acknowledgements.
|
|
41
|
+
- Use `UiNumberStepper` for bounded numeric settings and `UiTextbox` for numeric-looking text such as phones, codes, and identifiers.
|
|
42
|
+
- Use `Secondary` buttons for real neighboring commands and `Tertiary` buttons for lower-emphasis edit/open/configure/cancel/close actions.
|
|
43
|
+
- For widgets mounted into CRM targets, keep inline UI compact; move complex UI into `UiModalSidebar` or `UiModalWindow`.
|
|
44
|
+
- For sidebar footers, group related actions, use 12px or 16px gaps inside groups, and confirm destructive icon-only actions with `UiPopconfirm okVariant="danger"`.
|
|
45
|
+
|
|
46
|
+
## Design feedback triage
|
|
47
|
+
|
|
48
|
+
When design feedback does not match the package docs:
|
|
49
|
+
|
|
50
|
+
1. Check the component profile, page profile, `STYLING.md`, Figma, and CRM computed styles.
|
|
51
|
+
2. If Embed UI docs do not contain the requested rule, treat it as a project-specific design requirement.
|
|
52
|
+
3. State which source wins before editing.
|
|
53
|
+
4. Keep the selected values documented in the task or project `AGENTS.md`.
|