@milaboratories/uikit 2.2.96 → 2.2.97

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 (33) hide show
  1. package/.turbo/turbo-build.log +16 -16
  2. package/.turbo/turbo-type-check.log +1 -1
  3. package/CHANGELOG.md +8 -0
  4. package/dist/components/PlClipboard/PlClipboard.vue.d.ts +1 -1
  5. package/dist/components/PlElementList/PlElementList.vue.d.ts +19 -27
  6. package/dist/components/PlElementList/PlElementList.vue.d.ts.map +1 -1
  7. package/dist/components/PlElementList/PlElementList.vue2.js +155 -174
  8. package/dist/components/PlElementList/PlElementList.vue2.js.map +1 -1
  9. package/dist/components/PlElementList/PlElementListItem.vue.d.ts.map +1 -1
  10. package/dist/components/PlElementList/PlElementListItem.vue2.js +1 -1
  11. package/dist/components/PlElementList/PlElementListItem.vue2.js.map +1 -1
  12. package/dist/components/PlElementList/PlElementListItem.vue3.js +24 -24
  13. package/dist/components/PlElementList/utils.d.ts +0 -1
  14. package/dist/components/PlElementList/utils.d.ts.map +1 -1
  15. package/dist/components/PlElementList/utils.js +5 -11
  16. package/dist/components/PlElementList/utils.js.map +1 -1
  17. package/dist/components/PlErrorBoundary/PlErrorBoundary.vue.js +8 -8
  18. package/dist/components/PlFileInput/PlFileInput.vue.js +8 -8
  19. package/dist/components/PlIcon16/PlIcon16.vue.d.ts +1 -1
  20. package/dist/components/PlIcon24/PlIcon24.vue.d.ts +1 -1
  21. package/dist/components/PlSvg/PlSvg.vue.d.ts +1 -1
  22. package/dist/components/PlSvg/PlSvg.vue.d.ts.map +1 -1
  23. package/dist/components/PlSvg/PlSvg.vue2.js +24 -22
  24. package/dist/components/PlSvg/PlSvg.vue2.js.map +1 -1
  25. package/dist/composition/useWatchFetch.js +10 -10
  26. package/dist/lib/util/helpers/dist/index.js +108 -104
  27. package/dist/lib/util/helpers/dist/index.js.map +1 -1
  28. package/package.json +4 -4
  29. package/src/components/PlElementList/PlElementList.vue +116 -135
  30. package/src/components/PlElementList/PlElementListItem.vue +9 -1
  31. package/src/components/PlElementList/README.md +325 -72
  32. package/src/components/PlElementList/utils.ts +0 -9
  33. package/src/components/PlSvg/PlSvg.vue +2 -2
@@ -1,92 +1,345 @@
1
- PlElementList.vue
2
- =================
1
+ # PlElementList
3
2
 
4
- ## 1. GENERICS
3
+ A universal component for displaying lists of elements with support for drag & drop, pinning, toggling, content expansion, and element removal.
5
4
 
6
- **`T`** – Runtime type of a single list element
7
- • **`K`** – Type returned by `getItemKey` (typically `string` or `number`)
5
+ ## Key Features
8
6
 
7
+ - **Drag & Drop**: Drag elements to reorder them
8
+ - **Pinning**: Pin important elements to the top of the list
9
+ - **Toggle**: Toggle element states (enabled/disabled)
10
+ - **Expand/Collapse**: Expand additional element content
11
+ - **Remove**: Remove elements from the list
12
+ - **Active State**: Highlight active elements
9
13
 
10
- ## 2. TWO-WAY BINDINGS (`v-model`)
11
14
 
12
- | Model name | Type | Purpose / Behaviour |
13
- |-------------------|----------|--------------------------------------------------------|
14
- | `items`* | `T[]` | Source array; the list you render & mutate |
15
- | `draggableItems` | `Set<T>` | Restricts which items may be dragged |
16
- | `removableItems` | `Set<T>` | Restricts which items may be removed |
17
- | `expandableItems` | `Set<T>` | Restricts which items may be expanded |
18
- | `expandedItems` | `Set<T>` | Tracks the expand/collapse state per item |
19
- | `pinnableItems` | `Set<T>` | Restricts which items may be pinned |
20
- | `pinnedItems` | `Set<T>` | Tracks the *current* pinned elements |
21
- | `toggableItems` | `Set<T>` | Restricts which items may be toggled (show/hide) |
22
- | `toggledItems` | `Set<T>` | Tracks the visibility toggle state per item |
15
+ ## Props
23
16
 
24
- (*required)
17
+ ### Data Model
25
18
 
26
- ## 3. PROPS
19
+ | Prop | Type | Default | Description |
20
+ |------|------|---------|-------------|
21
+ | `items` | `T[]` | *(required)* | Array of list items. Used as v-model |
27
22
 
28
- | Prop | Type | Default | Notes |
29
- |------------------|-----------------------------------------------------------|-------------|-------------------------------------------------|
30
- | `getItemKey` | `(item:T) ⇒ K` | — | Stable key for `v-for` & SortableJS |
31
- | `itemClass` | `string \| string[] \| ((item:T, index:number) ⇒ string \| string[])` | — | CSS classes for individual items |
32
- | `activeItems` | `Set<T>` | — | Set of currently active items |
33
- | **Sorting** | | | |
34
- | `enableDragging` | `boolean` | `undefined` | Master switch for drag-and-drop |
35
- | `onDragEnd` | `(oldIdx:number, newIdx:number) ⇒ void\|bool` | — | Fired by SortableJS; return **false** to ignore |
36
- | `onSort` | `(oldIdx:number, newIdx:number) ⇒ void\|bool` | — | Return **false** to cancel applying new order |
37
- | **Removing** | | | |
38
- | `enableRemoving` | `boolean` | `undefined` | `true` ⇒ always show remove, `false` ⇒ never |
39
- | `onRemove` | `(item:T, index:number) ⇒ void\|bool` | — | Return **false** to veto |
40
- | **Expanding** | | | |
41
- | `enableExpanding`| `boolean` | `undefined` | Master switch for expand/collapse |
42
- | `onExpand` | `(item:T, index:number) ⇒ void\|bool` | — | Return **false** to veto |
43
- | **Toggling** | | | |
44
- | `enableToggling` | `boolean` | `undefined` | Master switch for visibility toggle |
45
- | `onToggle` | `(item:T, index:number) ⇒ void\|bool` | — | Return **false** to veto |
46
- | **Pinning** | | | |
47
- | `enablePinning` | `boolean` | `undefined` | Master switch for pinning |
48
- | `onPin` | `(item:T, index:number) ⇒ void\|bool` | — | Return **false** to veto |
23
+ ### Item Keys
49
24
 
25
+ | Prop | Type | Default | Description |
26
+ |------|------|---------|-------------|
27
+ | `getItemKey` | `(item: T, index: number) => K` | `JSON.stringify(item)` | Function to get unique key for an item |
50
28
 
51
- ## 4. SLOTS
29
+ ### Styling
52
30
 
53
- - **`item-title`** **required**. Receives `{ item, index }`.
54
- - **`item-content`** – optional. Same slot props; rendered only when the item
55
- is in the “opened” state (`toggleContent` event).
31
+ | Prop | Type | Default | Description |
32
+ |------|------|---------|-------------|
33
+ | `itemClass` | `string \| string[] \| ((item: T, index: number) => string \| string[])` | `undefined` | CSS classes for list items |
34
+ | `isActive` | `(item: T, index: number) => boolean` | `undefined` | Function to determine active item |
56
35
 
36
+ ### Drag & Drop
57
37
 
58
- ## 5. EVENTS
38
+ | Prop | Type | Default | Description |
39
+ |------|------|---------|-------------|
40
+ | `disableDragging` | `boolean` | `false` | Completely disable dragging |
41
+ | `isDraggable` | `(item: T, index: number) => boolean` | `true` | Function to check if item is draggable |
42
+ | `onDragEnd` | `(oldIndex: number, newIndex: number) => void \| boolean` | `undefined` | Callback when dragging ends |
43
+ | `onSort` | `(oldIndex: number, newIndex: number) => void \| boolean` | `undefined` | Callback when item order changes |
59
44
 
60
- | Event | Payload | Description |
61
- |------------|---------------------|------------------------------------------------|
62
- | `itemClick`| `(item: T)` | Emitted when an item is clicked |
45
+ ### Removal
63
46
 
64
- Additionally, these events bubble up from `<PlElementListItem>`:
65
- - `remove` – `(item: T, index: number)`
66
- - `toggle` `(item: T, index: number)`
67
- - `pin` `(item: T, index: number)`
68
- - `expand` `(item: T, index: number)`
47
+ | Prop | Type | Default | Description |
48
+ |------|------|---------|-------------|
49
+ | `disableRemoving` | `boolean` | `false` | Completely disable removal |
50
+ | `isRemovable` | `(item: T, index: number) => boolean` | `undefined` | Function to check if item is removable |
51
+ | `onRemove` | `(item: T, index: number) => void \| boolean` | `undefined` | Callback when item is removed |
69
52
 
53
+ ### Content Expansion
70
54
 
71
- ## 6. EXAMPLE USAGE
55
+ | Prop | Type | Default | Description |
56
+ |------|------|---------|-------------|
57
+ | `disableExpanding` | `boolean` | `false` | Completely disable expansion |
58
+ | `isExpandable` | `(item: T, index: number) => boolean` | `undefined` | Function to check if item is expandable |
59
+ | `isExpanded` | `(item: T, index: number) => boolean` | `undefined` | Function to check if item is expanded |
60
+ | `onExpand` | `(item: T, index: number) => unknown` | `undefined` | Callback when item is expanded/collapsed |
61
+
62
+ ### State Toggling
63
+
64
+ | Prop | Type | Default | Description |
65
+ |------|------|---------|-------------|
66
+ | `disableToggling` | `boolean` | `false` | Completely disable toggling |
67
+ | `isToggable` | `(item: T, index: number) => boolean` | `undefined` | Function to check if item is toggleable |
68
+ | `isToggled` | `(item: T, index: number) => boolean` | `undefined` | Function to check if item is toggled |
69
+ | `onToggle` | `(item: T, index: number) => unknown` | `undefined` | Callback when item is toggled |
70
+
71
+ ### Pinning
72
+
73
+ | Prop | Type | Default | Description |
74
+ |------|------|---------|-------------|
75
+ | `disablePinning` | `boolean` | `false` | Completely disable pinning |
76
+ | `isPinnable` | `(item: T, index: number) => boolean` | `undefined` | Function to check if item is pinnable |
77
+ | `isPinned` | `(item: T, index: number) => boolean` | `undefined` | Function to check if item is pinned |
78
+ | `onPin` | `(item: T, index: number) => void \| boolean` | `undefined` | Callback when item is pinned/unpinned |
79
+
80
+ ## Events
81
+
82
+ | Event | Type | Description |
83
+ |-------|------|-------------|
84
+ | `itemClick` | `(item: T) => void` | Emitted when an item is clicked |
85
+
86
+ ## Slots
87
+
88
+ | Slot | Props | Description |
89
+ |------|-------|-------------|
90
+ | `item-title` | `{ item: T, index: number }` | *(Required)* Content for item title |
91
+ | `item-content` | `{ item: T, index: number }` | *(Optional)* Expandable item content |
92
+
93
+ ## Usage Examples
94
+
95
+ ### Simple Example
96
+
97
+ Basic string list with drag and remove functionality:
72
98
 
73
99
  ```vue
74
- <PlElementList
75
- v-model:items="elements"
76
- v-model:pinnedItems="pinned"
77
- v-model:expandedItems="expanded"
78
- v-model:toggledItems="hidden"
79
- :getItemKey="el => el.id"
80
- :onSort="(oldIdx, newIdx) => api.saveOrder(oldIdx, newIdx)"
81
- :activeItems="activeSet"
82
- :itemClass="(item, index) => ({ 'special-item': item.isSpecial })"
83
- @itemClick="handleItemClick"
84
- >
85
- <template #item-title="{ item }">
86
- {{ item.name }}
87
- </template>
88
- <template #item-content="{ item }">
89
- <pre>{{ JSON.stringify(item.meta, null, 2) }}</pre>
90
- </template>
91
- </PlElementList>
100
+ <template>
101
+ <PlElementList v-model:items="items">
102
+ <template #item-title="{ item }">
103
+ {{ item }}
104
+ </template>
105
+ </PlElementList>
106
+ </template>
107
+
108
+ <script setup lang="ts">
109
+ import { ref } from 'vue';
110
+ import { PlElementList } from '@/components/PlElementList';
111
+
112
+ const items = ref([
113
+ 'First item',
114
+ 'Second item',
115
+ 'Third item'
116
+ ]);
117
+
118
+ function handleRemove(item: string, index: number): boolean {
119
+ const confirmed = confirm(`Remove "${item}"?`);
120
+ return confirmed; // return false to cancel removal
121
+ }
122
+ </script>
92
123
  ```
124
+
125
+ ### Advanced Example
126
+
127
+ Full-featured task list with all functionality:
128
+
129
+ ```vue
130
+ <template>
131
+ <PlElementList
132
+ v-model:items="tasks"
133
+ :getItemKey="(task) => task.id"
134
+ :itemClass="getTaskClass"
135
+ :isActive="(task) => task.id === activeTaskId"
136
+
137
+ :isRemovable="(task) => !task.isProtected"
138
+ :onRemove="handleRemoveTask"
139
+
140
+ :isExpandable="(task) => task.details.length > 0"
141
+ :isExpanded="(task) => expandedTasks.includes(task.id)"
142
+ :onExpand="handleExpandTask"
143
+
144
+ :isToggable="() => true"
145
+ :isToggled="(task) => task.completed"
146
+ :onToggle="handleToggleTask"
147
+
148
+ :isPinnable="(task) => task.priority === 'high'"
149
+ :isPinned="(task) => task.pinned"
150
+ :onPin="handlePinTask"
151
+
152
+ :onSort="handleSortTasks"
153
+
154
+ @itemClick="setActiveTask"
155
+ >
156
+ <template #item-title="{ item: task }">
157
+ <div class="task-title">
158
+ <span class="task-name" :class="{ completed: task.completed }">
159
+ {{ task.name }}
160
+ </span>
161
+ <span v-if="task.priority === 'high'" class="priority-badge">
162
+ High Priority
163
+ </span>
164
+ <span class="task-date">
165
+ {{ formatDate(task.dueDate) }}
166
+ </span>
167
+ </div>
168
+ </template>
169
+
170
+ <template #item-content="{ item: task }">
171
+ <div class="task-details">
172
+ <p class="task-description">{{ task.description }}</p>
173
+ <ul class="task-subtasks">
174
+ <li v-for="subtask in task.details" :key="subtask.id">
175
+ <input
176
+ type="checkbox"
177
+ :checked="subtask.completed"
178
+ @change="toggleSubtask(task.id, subtask.id)"
179
+ >
180
+ {{ subtask.name }}
181
+ </li>
182
+ </ul>
183
+ <div class="task-tags">
184
+ <span
185
+ v-for="tag in task.tags"
186
+ :key="tag"
187
+ class="tag"
188
+ >
189
+ {{ tag }}
190
+ </span>
191
+ </div>
192
+ </div>
193
+ </template>
194
+ </PlElementList>
195
+ </template>
196
+
197
+ <script setup lang="ts">
198
+ import { ref, computed } from 'vue';
199
+ import { PlElementList } from '@/components/PlElementList';
200
+
201
+ interface Task {
202
+ id: string;
203
+ name: string;
204
+ description: string;
205
+ completed: boolean;
206
+ priority: 'low' | 'medium' | 'high';
207
+ dueDate: Date;
208
+ pinned: boolean;
209
+ isProtected: boolean;
210
+ tags: string[];
211
+ details: Array<{
212
+ id: string;
213
+ name: string;
214
+ completed: boolean;
215
+ }>;
216
+ }
217
+
218
+ const tasks = ref<Task[]>([
219
+ {
220
+ id: '1',
221
+ name: 'Develop new feature',
222
+ description: 'Create a component for displaying task lists',
223
+ completed: false,
224
+ priority: 'high',
225
+ dueDate: new Date('2025-07-15'),
226
+ pinned: true,
227
+ isProtected: false,
228
+ tags: ['frontend', 'vue', 'typescript'],
229
+ details: [
230
+ { id: '1-1', name: 'Create component', completed: true },
231
+ { id: '1-2', name: 'Write tests', completed: false },
232
+ { id: '1-3', name: 'Add documentation', completed: false }
233
+ ]
234
+ },
235
+ {
236
+ id: '2',
237
+ name: 'Code review',
238
+ description: 'Review colleague\'s pull request',
239
+ completed: true,
240
+ priority: 'medium',
241
+ dueDate: new Date('2025-07-10'),
242
+ pinned: false,
243
+ isProtected: true,
244
+ tags: ['review'],
245
+ details: []
246
+ },
247
+ {
248
+ id: '3',
249
+ name: 'Update dependencies',
250
+ description: 'Update project packages to latest versions',
251
+ completed: false,
252
+ priority: 'low',
253
+ dueDate: new Date('2025-07-20'),
254
+ pinned: false,
255
+ isProtected: false,
256
+ tags: ['maintenance', 'dependencies'],
257
+ details: [
258
+ { id: '3-1', name: 'Check compatibility', completed: false },
259
+ { id: '3-2', name: 'Run tests', completed: false }
260
+ ]
261
+ }
262
+ ]);
263
+
264
+ const activeTaskId = ref<string | null>('1');
265
+ const expandedTasks = ref<string[]>(['1']);
266
+
267
+ function getTaskClass(task: Task, index: number): string[] {
268
+ const classes = ['task-item'];
269
+
270
+ if (task.completed) classes.push('completed');
271
+ if (task.priority === 'high') classes.push('high-priority');
272
+ if (task.isProtected) classes.push('protected');
273
+
274
+ return classes;
275
+ }
276
+
277
+ function setActiveTask(task: Task) {
278
+ activeTaskId.value = task.id;
279
+ }
280
+
281
+ function handleRemoveTask(task: Task, index: number): boolean {
282
+ if (task.isProtected) {
283
+ alert('This task cannot be deleted');
284
+ return false;
285
+ }
286
+
287
+ const confirmed = confirm(`Delete task "${task.name}"?`);
288
+ return confirmed;
289
+ }
290
+
291
+ function handleExpandTask(task: Task, index: number) {
292
+ const taskIndex = expandedTasks.value.indexOf(task.id);
293
+
294
+ if (taskIndex >= 0) {
295
+ expandedTasks.value.splice(taskIndex, 1);
296
+ } else {
297
+ expandedTasks.value.push(task.id);
298
+ }
299
+ }
300
+
301
+ function handleToggleTask(task: Task, index: number) {
302
+ task.completed = !task.completed;
303
+ }
304
+
305
+ function handlePinTask(task: Task, index: number): boolean {
306
+ if (task.priority !== 'high') {
307
+ alert('Only high priority tasks can be pinned');
308
+ return false;
309
+ }
310
+
311
+ task.pinned = !task.pinned;
312
+ return true;
313
+ }
314
+
315
+ function handleSortTasks(oldIndex: number, newIndex: number): boolean {
316
+ console.log(`Task moved from position ${oldIndex} to position ${newIndex}`);
317
+ // Return true to allow the move
318
+ return true;
319
+ }
320
+
321
+ function toggleSubtask(taskId: string, subtaskId: string) {
322
+ const task = tasks.value.find(t => t.id === taskId);
323
+ if (!task) return;
324
+
325
+ const subtask = task.details.find(st => st.id === subtaskId);
326
+ if (subtask) {
327
+ subtask.completed = !subtask.completed;
328
+ }
329
+ }
330
+
331
+ function formatDate(date: Date): string {
332
+ return date.toLocaleDateString('en-US');
333
+ }
334
+ </script>
335
+ ```
336
+
337
+ ## Notes
338
+
339
+ ### Callback Function Behavior
340
+
341
+ If callback functions (`onRemove`, `onPin`, `onSort`, `onDragEnd`) return `false`, the corresponding action will be cancelled.
342
+
343
+ ### Automatic Feature Detection
344
+
345
+ If explicit state checking functions (e.g., `isPinnable`) are not provided, the component automatically determines feature availability based on the presence of corresponding callbacks or state functions.
@@ -1,6 +1,3 @@
1
- import { isRef, isShallow } from 'vue';
2
- import { shallowClone } from '@milaboratories/helpers';
3
-
4
1
  export const moveElements = <T>(array: T[], from: number, to: number): T[] => {
5
2
  if (to >= 0 && to < array.length) {
6
3
  const element = array.splice(from, 1)[0];
@@ -9,9 +6,3 @@ export const moveElements = <T>(array: T[], from: number, to: number): T[] => {
9
6
 
10
7
  return array;
11
8
  };
12
-
13
- export function optionalUpdateRef<T>(ref: T) {
14
- if (isRef(ref) && isShallow(ref)) {
15
- ref.value = shallowClone(ref.value);
16
- }
17
- }
@@ -46,8 +46,8 @@ function getStyleColor(prop: 'fill' | 'stroke', color: undefined | string | stri
46
46
  </script>
47
47
 
48
48
  <template>
49
- <svg v-if="svgMeta" :style="[styleSize, styleColor, styleStroke]" :class="$style.svg">
50
- <use :href="`#${svgMeta.spriteId}`" />
49
+ <svg :style="[styleSize, styleColor, styleStroke]" :class="$style.svg">
50
+ <use :href="`#${svgMeta?.spriteId}`" />
51
51
  </svg>
52
52
  </template>
53
53