@zeyos/zx 4.3.0 → 4.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (136) hide show
  1. package/CHANGELOG.md +266 -1
  2. package/README.md +7 -3
  3. package/dist/types/components/activity-item/activity-item.d.ts +351 -0
  4. package/dist/types/components/activity-list/activity-list.d.ts +195 -0
  5. package/dist/types/components/app-sidebar/app-sidebar.d.ts +28 -0
  6. package/dist/types/components/button/button.d.ts +8 -0
  7. package/dist/types/components/calendar/calendar-model.d.ts +22 -0
  8. package/dist/types/components/calendar/calendar.d.ts +19 -0
  9. package/dist/types/components/card/card.d.ts +7 -0
  10. package/dist/types/components/card-view/card-view.d.ts +12 -4
  11. package/dist/types/components/card-view/record-card.d.ts +53 -5
  12. package/dist/types/components/code-editor/code-editor.d.ts +160 -0
  13. package/dist/types/components/code-editor/field-adapter.d.ts +5 -0
  14. package/dist/types/components/context-menu/context-menu.d.ts +18 -2
  15. package/dist/types/components/date-picker/date-picker.d.ts +8 -0
  16. package/dist/types/components/date-picker/time-picker.d.ts +7 -0
  17. package/dist/types/components/dialog/dialog.d.ts +29 -0
  18. package/dist/types/components/entity-ref/entity-ref.d.ts +331 -0
  19. package/dist/types/components/file-item/file-item.d.ts +234 -0
  20. package/dist/types/components/file-list/file-list.d.ts +171 -0
  21. package/dist/types/components/filter/filter.d.ts +91 -36
  22. package/dist/types/components/filter-panel/filter-panel.d.ts +449 -0
  23. package/dist/types/components/kanban-view/kanban-card.d.ts +138 -0
  24. package/dist/types/components/kanban-view/kanban-policy.d.ts +268 -0
  25. package/dist/types/components/kanban-view/kanban-view.d.ts +837 -26
  26. package/dist/types/components/launcher/launcher.d.ts +19 -12
  27. package/dist/types/components/list-toolbar/list-toolbar.d.ts +599 -0
  28. package/dist/types/components/menu-button/menu-button.d.ts +4 -37
  29. package/dist/types/components/message/message.d.ts +7 -0
  30. package/dist/types/components/modal/modal.d.ts +48 -0
  31. package/dist/types/components/navigation-bar/navigation-bar.d.ts +105 -21
  32. package/dist/types/components/panel/panel.d.ts +7 -0
  33. package/dist/types/components/search/search.d.ts +7 -0
  34. package/dist/types/components/select/select.d.ts +4 -0
  35. package/dist/types/components/sort-control/sort-control.d.ts +105 -0
  36. package/dist/types/components/stat-tile/stat-tile.d.ts +277 -0
  37. package/dist/types/components/tabbox/tabbox.d.ts +7 -0
  38. package/dist/types/components/table/table.d.ts +144 -0
  39. package/dist/types/components/table-view/table-view.d.ts +11 -5
  40. package/dist/types/components/timebox/timebox.d.ts +7 -0
  41. package/dist/types/components/value-list/value-list.d.ts +8 -0
  42. package/dist/types/components/view/record-view.d.ts +96 -5
  43. package/dist/types/core/component.d.ts +6 -1
  44. package/dist/types/core/icons.d.ts +17 -0
  45. package/dist/types/index.d.ts +13 -1
  46. package/dist/types/internal/app-rail.d.ts +10 -0
  47. package/dist/types/internal/menu-items.d.ts +134 -0
  48. package/dist/zx-zeyos.esm.js +651 -88
  49. package/dist/zx-zeyos.esm.min.js +8 -1
  50. package/dist/zx.css +6266 -3900
  51. package/dist/zx.esm.js +8487 -1805
  52. package/dist/zx.esm.min.js +10 -3
  53. package/dist/zx.global.js +8487 -1805
  54. package/dist/zx.global.min.js +10 -3
  55. package/dist/zx.min.css +1 -1
  56. package/docs/api.json +1811 -247
  57. package/docs/llms.md +345 -40
  58. package/docs/llms.txt +11 -7
  59. package/package.json +1 -2
  60. package/src/components/activity-item/activity-item.css +137 -0
  61. package/src/components/activity-item/activity-item.js +599 -0
  62. package/src/components/activity-list/activity-list.css +64 -0
  63. package/src/components/activity-list/activity-list.js +375 -0
  64. package/src/components/app-rail/app-rail.css +227 -19
  65. package/src/components/app-sidebar/app-sidebar.css +414 -24
  66. package/src/components/app-sidebar/app-sidebar.js +65 -5
  67. package/src/components/button/button.css +8 -0
  68. package/src/components/button/button.js +6 -1
  69. package/src/components/calendar/calendar-model.js +45 -0
  70. package/src/components/calendar/calendar.css +30 -10
  71. package/src/components/calendar/calendar.js +41 -13
  72. package/src/components/card/card.js +12 -1
  73. package/src/components/card-view/card-view.css +212 -9
  74. package/src/components/card-view/card-view.js +32 -16
  75. package/src/components/card-view/record-card.js +98 -21
  76. package/src/components/code-editor/code-editor.css +67 -0
  77. package/src/components/code-editor/code-editor.js +288 -0
  78. package/src/components/code-editor/field-adapter.js +34 -0
  79. package/src/components/context-menu/context-menu.css +80 -3
  80. package/src/components/context-menu/context-menu.js +22 -30
  81. package/src/components/date-picker/date-picker.js +22 -8
  82. package/src/components/date-picker/month-picker.js +3 -3
  83. package/src/components/date-picker/time-picker.js +20 -8
  84. package/src/components/dialog/dialog.js +88 -66
  85. package/src/components/dropdown/dropdown.js +2 -2
  86. package/src/components/entity-ref/entity-ref.css +182 -0
  87. package/src/components/entity-ref/entity-ref.js +496 -0
  88. package/src/components/file-item/file-item.css +88 -0
  89. package/src/components/file-item/file-item.js +371 -0
  90. package/src/components/file-list/file-list.css +51 -0
  91. package/src/components/file-list/file-list.js +320 -0
  92. package/src/components/filter/filter.css +27 -1
  93. package/src/components/filter/filter.js +199 -45
  94. package/src/components/filter-panel/filter-panel.css +104 -0
  95. package/src/components/filter-panel/filter-panel.js +964 -0
  96. package/src/components/kanban-view/kanban-card.js +239 -0
  97. package/src/components/kanban-view/kanban-policy.js +412 -0
  98. package/src/components/kanban-view/kanban-view.css +674 -36
  99. package/src/components/kanban-view/kanban-view.js +1762 -240
  100. package/src/components/launcher/launcher.js +59 -27
  101. package/src/components/list-toolbar/list-toolbar.css +189 -0
  102. package/src/components/list-toolbar/list-toolbar.js +851 -0
  103. package/src/components/menu-button/menu-button.css +80 -3
  104. package/src/components/menu-button/menu-button.js +21 -37
  105. package/src/components/message/message.css +14 -12
  106. package/src/components/message/message.js +13 -2
  107. package/src/components/modal/modal.css +15 -0
  108. package/src/components/modal/modal.js +141 -0
  109. package/src/components/navigation-bar/navigation-bar.css +77 -2
  110. package/src/components/navigation-bar/navigation-bar.js +171 -4
  111. package/src/components/panel/panel.js +13 -2
  112. package/src/components/search/search.js +17 -5
  113. package/src/components/select/field-adapter.js +4 -1
  114. package/src/components/select/select.js +3 -0
  115. package/src/components/sort-control/sort-control.css +17 -0
  116. package/src/components/sort-control/sort-control.js +140 -0
  117. package/src/components/stat-tile/stat-tile.css +213 -0
  118. package/src/components/stat-tile/stat-tile.js +590 -0
  119. package/src/components/tabbox/tabbox.js +12 -1
  120. package/src/components/table/table.css +51 -2
  121. package/src/components/table/table.js +298 -19
  122. package/src/components/table-view/table-view.css +8 -2
  123. package/src/components/table-view/table-view.js +21 -6
  124. package/src/components/tag-picker/tag-picker.css +3 -1
  125. package/src/components/timebox/timebox.js +18 -5
  126. package/src/components/value-list/value-list.js +22 -6
  127. package/src/components/view/record-view.css +5 -0
  128. package/src/components/view/record-view.js +100 -7
  129. package/src/core/component.js +8 -3
  130. package/src/core/icons.js +74 -5
  131. package/src/index.js +33 -3
  132. package/src/internal/app-rail.js +19 -1
  133. package/src/internal/menu-items.js +142 -0
  134. package/styles/print.css +192 -0
  135. package/styles/zx.css +11 -0
  136. package/DESIGN-SYSTEM.md +0 -182
@@ -0,0 +1,320 @@
1
+ // @ts-check
2
+ import { Component } from '../../core/component.js';
3
+ import { h, restoreTarget, snapshotTarget } from '../../core/dom.js';
4
+ import { skeleton, skeletonText } from '../skeleton/skeleton.js';
5
+ import { FileItem, normalizeFileItem } from '../file-item/file-item.js';
6
+
7
+ /** @typedef {import('../file-item/file-item.js').FileItemOptions} FileItemOptions */
8
+ /** @typedef {import('../file-item/file-item.js').FileItemState} FileItemState */
9
+
10
+ /**
11
+ * @typedef {Object} FileListOptions
12
+ * @property {FileItemOptions[]} [items=[]] Initial files in host-supplied order.
13
+ * @property {string} [label='Files'] Accessible collection name.
14
+ * @property {boolean} [loading=false] Whether initial or incremental files are loading.
15
+ * @property {number} [loadingCount=3] Placeholder rows shown for an empty loading list.
16
+ * @property {string} [loadingText='Loading files'] Loading status text.
17
+ * @property {string|number|Node|Component|null} [emptyText='No files'] Empty-state content.
18
+ * @property {(event: CustomEvent<{id: unknown, index: number, file: Readonly<FileItemState>, href: string, event: MouseEvent}>) => void} [onactivate]
19
+ * Preventable native-file-link listener.
20
+ * @property {(event: CustomEvent<{id: string, fileId: unknown, index: number, action: object, file: Readonly<FileItemState>, event: MouseEvent}>) => void} [onaction]
21
+ * Preventable file-action listener.
22
+ * @property {(event: CustomEvent<{items: Readonly<FileItemState>[]}>) => void} [ondatachange]
23
+ * Collection mutation listener.
24
+ */
25
+
26
+ /**
27
+ * Accessible collection for durable and transient files. Ordering and mutation are local
28
+ * presentation concerns only; transport, authorization, and persistence remain host-owned.
29
+ * @fires FileList#activate
30
+ * @fires FileList#action
31
+ * @fires FileList#datachange
32
+ * @extends {Component<FileListOptions>}
33
+ */
34
+ export class FileList extends Component {
35
+ static cssName = 'file-list';
36
+
37
+ /** @type {Readonly<FileListOptions>} */
38
+ static defaults = {
39
+ items: [],
40
+ label: 'Files',
41
+ loading: false,
42
+ loadingCount: 3,
43
+ loadingText: 'Loading files',
44
+ emptyText: 'No files'
45
+ };
46
+
47
+ /**
48
+ * Creates or enhances a file collection.
49
+ * @param {Element|string|null} [target=null] Existing host, selector, or null.
50
+ * @param {FileListOptions} [options={}] Collection presentation.
51
+ */
52
+ constructor(target = null, options = {}) {
53
+ super(target, options);
54
+ }
55
+
56
+ /** @returns {HTMLElement} */
57
+ render() {
58
+ this._createdRoot = this.el === null;
59
+ const root = /** @type {HTMLElement} */ (this.el ?? h('section'));
60
+ this.el = root;
61
+ this._snapshot = this._createdRoot ? null : snapshotTarget(root);
62
+ this._destroyed = false;
63
+ this._items = normalizeFileListItems(this.options.items);
64
+ this._loading = Boolean(this.options.loading);
65
+ this._itemComponents = [];
66
+
67
+ root.setAttribute('aria-label', String(this.options.label || 'Files'));
68
+ this.listen(root, 'zx-activate', (event) => this._forwardActivate(/** @type {CustomEvent} */ (event)));
69
+ this.listen(root, 'zx-action', (event) => this._forwardAction(/** @type {CustomEvent} */ (event)));
70
+ try {
71
+ this._renderList();
72
+ return root;
73
+ } catch (error) {
74
+ this._destroyItems();
75
+ if (!this._createdRoot) restoreTarget(root, this._snapshot);
76
+ throw error;
77
+ }
78
+ }
79
+
80
+ /**
81
+ * Replaces every file.
82
+ * @param {FileItemOptions[]} items Ordered file descriptors.
83
+ * @param {{silent?:boolean}} [options={}] Event behavior.
84
+ * @returns {this}
85
+ */
86
+ setItems(items, options = {}) {
87
+ this._items = normalizeFileListItems(items);
88
+ this._renderList();
89
+ if (!options.silent) this._emitDataChange();
90
+ return this;
91
+ }
92
+
93
+ /** Returns defensive snapshots in display order. @returns {Readonly<FileItemState>[]} */
94
+ getItems() {
95
+ return this._itemComponents.map((item) => item.getFile());
96
+ }
97
+
98
+ /** Returns a defensive snapshot by stable ID. @param {unknown} id File id. @returns {Readonly<FileItemState>|null} */
99
+ getItem(id) {
100
+ const component = this._itemComponents.find((item) => Object.is(item.getFile().id, id));
101
+ return component?.getFile() ?? null;
102
+ }
103
+
104
+ /**
105
+ * Adds one file at a bounded index.
106
+ * @param {FileItemOptions} item File descriptor.
107
+ * @param {number} [index] Insertion position; omitted means append.
108
+ * @param {{silent?:boolean}} [options={}] Event behavior.
109
+ * @returns {this}
110
+ */
111
+ addItem(item, index = this._items.length, options = {}) {
112
+ const position = Math.max(0, Math.min(this._items.length, Math.trunc(Number(index)) || 0));
113
+ const next = this._items.slice();
114
+ next.splice(position, 0, normalizeFileItem(item));
115
+ return this.setItems(next, options);
116
+ }
117
+
118
+ /**
119
+ * Patches the first file whose ID is `Object.is`-equal to `id`.
120
+ * @param {unknown} id File id.
121
+ * @param {Partial<FileItemOptions>} values Replacement fields.
122
+ * @param {{silent?:boolean}} [options={}] Event behavior.
123
+ * @returns {this}
124
+ */
125
+ updateItem(id, values, options = {}) {
126
+ if (!values || typeof values !== 'object' || Array.isArray(values)) {
127
+ throw new TypeError('FileList update values must be an object');
128
+ }
129
+ const index = this._items.findIndex((item) => Object.is(item.id, id));
130
+ if (index < 0) return this;
131
+ const next = this._items.slice();
132
+ next[index] = normalizeFileItem({ ...next[index], ...values });
133
+ return this.setItems(next, options);
134
+ }
135
+
136
+ /**
137
+ * Removes the first file whose ID is `Object.is`-equal to `id`.
138
+ * @param {unknown} id File id.
139
+ * @param {{silent?:boolean}} [options={}] Event behavior.
140
+ * @returns {this}
141
+ */
142
+ removeItem(id, options = {}) {
143
+ const index = this._items.findIndex((item) => Object.is(item.id, id));
144
+ if (index < 0) return this;
145
+ const next = this._items.slice();
146
+ next.splice(index, 1);
147
+ return this.setItems(next, options);
148
+ }
149
+
150
+ /** Sets initial/incremental loading presentation. @param {boolean} loading Loading state. @returns {this} */
151
+ setLoading(loading) {
152
+ const next = Boolean(loading);
153
+ if (next === this._loading) return this;
154
+ this._loading = next;
155
+ this._renderList();
156
+ return this;
157
+ }
158
+
159
+ /** Restores an enhanced target exactly, or removes an owned collection. @returns {void} */
160
+ destroy() {
161
+ if (this._destroyed) return;
162
+ this._destroyed = true;
163
+ const root = this.el;
164
+ this._destroyItems();
165
+ super.destroy();
166
+ if (!this._createdRoot && root) restoreTarget(root, this._snapshot);
167
+ }
168
+
169
+ /** @returns {void} */
170
+ _renderList() {
171
+ this._destroyItems();
172
+ const root = /** @type {HTMLElement} */ (this.el);
173
+ root.setAttribute('aria-busy', String(this._loading));
174
+ /** @type {HTMLElement[]} */
175
+ const content = [];
176
+ try {
177
+ if (this._items.length) {
178
+ const list = h('ol', { class: 'zx-file-list__items' });
179
+ for (const descriptor of this._items) {
180
+ const component = new FileItem(null, descriptor);
181
+ this._itemComponents.push(component);
182
+ list.append(h('li', { class: 'zx-file-list__item' }, component));
183
+ }
184
+ content.push(list);
185
+ } else if (!this._loading) {
186
+ content.push(h('div', {
187
+ class: 'zx-file-list__empty', role: 'status'
188
+ }, listContent(this.options.emptyText, 'FileList emptyText')));
189
+ }
190
+ if (this._loading) content.push(this._createLoading(this._items.length === 0));
191
+ root.replaceChildren(...content);
192
+ } catch (error) {
193
+ this._destroyItems();
194
+ throw error;
195
+ }
196
+ }
197
+
198
+ /** @param {boolean} placeholders @returns {HTMLElement} */
199
+ _createLoading(placeholders) {
200
+ const region = h('div', {
201
+ class: 'zx-file-list__loading', role: 'status', ariaLive: 'polite'
202
+ }, h('span', { class: 'zx-file-list__loading-label' }, String(this.options.loadingText)));
203
+ if (!placeholders) return region;
204
+ const rows = h('div', { class: 'zx-file-list__skeletons', ariaHidden: 'true' });
205
+ for (let index = 0; index < normalizeLoadingCount(this.options.loadingCount); index += 1) {
206
+ rows.append(h('div', { class: 'zx-file-list__skeleton' },
207
+ skeleton({ width: '2rem', height: '2rem', radius: 'md' }),
208
+ skeletonText({ lines: 2, lastLineWidth: index % 2 ? '48%' : '64%' })));
209
+ }
210
+ region.append(rows);
211
+ return region;
212
+ }
213
+
214
+ /** @returns {void} */
215
+ _destroyItems() {
216
+ for (const item of this._itemComponents) item.destroy();
217
+ this._itemComponents.length = 0;
218
+ }
219
+
220
+ /** @param {CustomEvent} event @returns {void} */
221
+ _forwardActivate(event) {
222
+ const component = this._componentForEvent(event);
223
+ if (!component) return;
224
+ const index = this._itemComponents.indexOf(component);
225
+ const detail = /** @type {{href?:string,event?:MouseEvent}} */ (event.detail ?? {});
226
+ const file = component.getFile();
227
+ const forwarded = this.emit('activate', {
228
+ id: file.id,
229
+ index,
230
+ file,
231
+ href: String(detail.href ?? ''),
232
+ event: detail.event
233
+ }, { honorDomCancellation: true });
234
+ if (forwarded.defaultPrevented) event.preventDefault();
235
+ }
236
+
237
+ /** @param {CustomEvent} event @returns {void} */
238
+ _forwardAction(event) {
239
+ const component = this._componentForEvent(event);
240
+ if (!component) return;
241
+ const index = this._itemComponents.indexOf(component);
242
+ const detail = /** @type {{id?:string,action?:object,event?:MouseEvent}} */ (event.detail ?? {});
243
+ const file = component.getFile();
244
+ const forwarded = this.emit('action', {
245
+ id: String(detail.id ?? ''),
246
+ fileId: file.id,
247
+ index,
248
+ action: detail.action ?? {},
249
+ file,
250
+ event: detail.event
251
+ }, { honorDomCancellation: true });
252
+ if (forwarded.defaultPrevented) event.preventDefault();
253
+ }
254
+
255
+ /** @param {CustomEvent} event @returns {FileItem|null} */
256
+ _componentForEvent(event) {
257
+ if (event.target === this.el) return null;
258
+ return this._itemComponents.find((item) => event.target === item.el) ?? null;
259
+ }
260
+
261
+ /** @returns {void} */
262
+ _emitDataChange() {
263
+ this.emit('datachange', { items: this.getItems() });
264
+ }
265
+ }
266
+
267
+ /**
268
+ * Validates, normalizes, and clones file descriptors without mutating the input.
269
+ * Explicit duplicate IDs are rejected; id-less rows remain valid.
270
+ * @param {unknown} value Candidate descriptors.
271
+ * @returns {FileItemState[]}
272
+ */
273
+ export function normalizeFileListItems(value) {
274
+ if (!Array.isArray(value)) throw new TypeError('FileList items must be an array');
275
+ const ids = new Set();
276
+ return value.map((item) => {
277
+ if (!item || typeof item !== 'object' || Array.isArray(item)) {
278
+ throw new TypeError('FileList items must be descriptor objects');
279
+ }
280
+ const normalized = normalizeFileItem(/** @type {FileItemOptions} */ (item));
281
+ if (normalized.id !== null) {
282
+ if (ids.has(normalized.id)) throw new RangeError(`Duplicate FileList id: ${String(normalized.id)}`);
283
+ ids.add(normalized.id);
284
+ }
285
+ return cloneFileDescriptor(normalized);
286
+ });
287
+ }
288
+
289
+ /** @param {FileItemState} item @returns {FileItemState} */
290
+ function cloneFileDescriptor(item) {
291
+ return {
292
+ ...item,
293
+ metadata: item.metadata.map((entry) => ({ ...entry })),
294
+ link: item.link ? { ...item.link } : null,
295
+ actions: [...item.actions],
296
+ locale: item.locale
297
+ };
298
+ }
299
+
300
+ /** @param {unknown} value @param {string} label @returns {Node|string} */
301
+ function listContent(value, label) {
302
+ if (value == null) return '';
303
+ if (typeof value === 'string' || typeof value === 'number') return String(value);
304
+ if (value instanceof Component) return value.toElement();
305
+ if (typeof value === 'object'
306
+ && typeof /** @type {{nodeType?:unknown}} */ (value).nodeType === 'number') {
307
+ return /** @type {Node} */ (value);
308
+ }
309
+ throw new TypeError(`${label} must be text, a Node, a Component, or null`);
310
+ }
311
+
312
+ /** @param {unknown} value @returns {number} */
313
+ function normalizeLoadingCount(value) {
314
+ const count = Math.trunc(Number(value));
315
+ return Number.isFinite(count) ? Math.max(1, Math.min(12, count)) : 3;
316
+ }
317
+
318
+ /** File descriptors changed through a list mutation method. @event FileList#datachange @type {CustomEvent<{items:Readonly<FileItemState>[]}>} */
319
+ /** A file link activated through the list. @event FileList#activate @type {CustomEvent<{id:unknown,index:number,file:Readonly<FileItemState>,href:string,event:MouseEvent}>} */
320
+ /** A file action activated through the list. @event FileList#action @type {CustomEvent<{id:string,fileId:unknown,index:number,action:object,file:Readonly<FileItemState>,event:MouseEvent}>} */
@@ -27,7 +27,9 @@
27
27
  border-inline-start: 3px solid var(--zx-color-border-strong);
28
28
  border-radius: 0;
29
29
  background: transparent;
30
- padding: 0 0 0 var(--zx-space-3);
30
+ padding: 0;
31
+ /* The rule's own `border-inline-start` above says which edge this is. */
32
+ padding-inline-start: var(--zx-space-3);
31
33
  }
32
34
 
33
35
  .zx-filter__legend {
@@ -145,3 +147,27 @@
145
147
  border-color: CanvasText;
146
148
  }
147
149
  }
150
+
151
+ /* Container-based compact rows work in a drawer as well as a full-width page. */
152
+ .zx-filter[data-layout="compact"] { container-type: inline-size; inline-size: 100%; }
153
+ .zx-filter[data-layout="compact"] .zx-filter__condition {
154
+ grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr) minmax(0, 1.35fr) auto;
155
+ gap: var(--zx-space-2);
156
+ align-items: center;
157
+ padding: var(--zx-space-2);
158
+ border: 1px solid var(--zx-color-border);
159
+ background: var(--zx-color-bg-surface);
160
+ }
161
+ .zx-filter__picker { min-inline-size: 0; }
162
+ .zx-filter[data-layout="compact"] .zx-filter__value { grid-column: auto; }
163
+ .zx-filter[data-layout="compact"] [data-filter-action="remove"] {
164
+ inline-size: var(--zx-control-height);
165
+ min-block-size: var(--zx-control-height);
166
+ padding: 0;
167
+ }
168
+ .zx-filter[data-layout="compact"] [data-filter-action="remove"] > span { display: none; }
169
+ @container (max-width: 420px) {
170
+ .zx-filter[data-layout="compact"] .zx-filter__condition { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) auto; }
171
+ .zx-filter[data-layout="compact"] .zx-filter__value { grid-column: 1 / 3; grid-row: 2; }
172
+ .zx-filter[data-layout="compact"] .zx-filter__condition > [data-filter-action="remove"] { grid-column: 3; grid-row: 1 / 3; }
173
+ }