@retailcrm/embed-ui-v1-components 0.9.26 → 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 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
- summary ? vue.h("div", { class: "ui-v1-table__footer-meta" }, [summary]) : null,
2700
- pageSize || exportControl || pagination ? vue.h("div", { class: "ui-v1-table__footer-controls" }, [
2701
- pageSize || exportControl ? vue.h("div", { class: "ui-v1-table__footer-main" }, [
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
- pagination ? vue.h("div", { class: "ui-v1-table__footer-side" }, [pagination]) : null
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
- summary ? h("div", { class: "ui-v1-table__footer-meta" }, [summary]) : null,
2698
- pageSize || exportControl || pagination ? h("div", { class: "ui-v1-table__footer-controls" }, [
2699
- pageSize || exportControl ? h("div", { class: "ui-v1-table__footer-main" }, [
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
- pagination ? h("div", { class: "ui-v1-table__footer-side" }, [pagination]) : null
2717
+ hasPagination ? h("div", { class: "ui-v1-table__footer-side" }, [pagination]) : null
2704
2718
  ]) : null
2705
2719
  ]))));
2706
2720
  }
package/docs/AI.md CHANGED
@@ -126,6 +126,10 @@ screen where users scan and refine datasets:
126
126
  - reset `page` to `1` when filters or sorting change;
127
127
  - debounce free-text search before writing query or fetching data;
128
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;
129
133
  - use `UiTable` footer slots for summary, page-size controls, export, and pagination;
130
134
  - compose table footer controls with `UiTableFooterSection` and `UiTableFooterButton`, not regular `UiButton`;
131
135
  - use chevron icon assets for table footer previous/next controls instead of text glyphs;
@@ -32,24 +32,18 @@ examples:
32
32
  :rows="rows"
33
33
  row-key="id"
34
34
  >
35
- <UiTableColumn label="Title">
36
- <template #cell="{ row }">
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
- <template #cell="{ row }">
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
- <template #cell="{ row }">
49
- <UiTag :background="statusBackgroundByName[row.status]" size="md" saturated :ticker="false">
50
- {{ row.status }}
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
- <template #cell="{ row }">
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
- <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>
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
- <template #cell="{ expanded, toggle }">
144
- <button class="table-expand" type="button" @click="toggle">
145
- {{ expanded ? '-' : '+' }}
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
- <template #cell="{ row }">
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
- <template #cell="{ row }">
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
- <template #cell="{ row }">
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
- <template #cell="{ row }">
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
- <template #cell="{ row }">
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
- <template #cell="{ row }">
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:
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.26",
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.3",
86
- "@vitest/browser-playwright": "4.1.3",
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": "^4.1.3",
103
+ "vitest": "4.1.8",
104
104
  "vue": "^3.5.32",
105
105
  "vue-i18n": "10.0.8"
106
106
  }
@@ -32,6 +32,7 @@ Use this skill before changing frontend UI built with `@retailcrm/embed-ui-v1-co
32
32
  ## High-signal checks
33
33
 
34
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.
35
36
  - For table pagination, follow the reference example in `UiTable.yml`: button sizes, active state, dividers, arrow assets, and scoped selector pattern.
36
37
  - For form fields, use `UiField` with the matching control and forward slot props such as `id` when the control accepts them.
37
38
  - Put validation errors inside the relevant field composition, not as unrelated sibling blocks.