@ticatec/uniface-element 0.2.4 → 0.3.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 (176) hide show
  1. package/dist/accordion/README.md +169 -0
  2. package/dist/accordion/README_CN.md +176 -0
  3. package/dist/action-bar/README.md +275 -0
  4. package/dist/action-bar/README_CN.md +285 -0
  5. package/dist/app-layout/README.md +374 -0
  6. package/dist/app-layout/README_CN.md +381 -0
  7. package/dist/app-top-bar/README.md +357 -0
  8. package/dist/app-top-bar/README_CN.md +365 -0
  9. package/dist/attachment-files/AttachmentFilesField.svelte +67 -1
  10. package/dist/attachment-files/AttachmentFilesField.svelte.d.ts +8 -1
  11. package/dist/base-calendar/CalendarBase.svelte +0 -3
  12. package/dist/box/README.md +385 -0
  13. package/dist/box/README_CN.md +393 -0
  14. package/dist/breadcrumbs/README.md +442 -0
  15. package/dist/breadcrumbs/README_CN.md +442 -0
  16. package/dist/button/Button.svelte +27 -7
  17. package/dist/button/Button.svelte.d.ts +3 -1
  18. package/dist/button/IconButton.svelte +26 -6
  19. package/dist/button/IconButton.svelte.d.ts +3 -1
  20. package/dist/button/IconButton_README.md +513 -0
  21. package/dist/button/IconButton_README_CN.md +513 -0
  22. package/dist/button/README.md +428 -0
  23. package/dist/button/README_CN.md +428 -0
  24. package/dist/button/TextButton.svelte +26 -6
  25. package/dist/button/TextButton.svelte.d.ts +3 -1
  26. package/dist/button/TextButton_README.md +571 -0
  27. package/dist/button/TextButton_README_CN.md +571 -0
  28. package/dist/card/README.md +666 -0
  29. package/dist/card/README_CN.md +666 -0
  30. package/dist/cascade-options-select/CascadeOptionSelect.svelte +43 -7
  31. package/dist/cascade-options-select/CascadeOptionSelect.svelte.d.ts +3 -4
  32. package/dist/cascade-options-select/README.md +663 -2
  33. package/dist/cascade-options-select/README_CN.md +664 -0
  34. package/dist/checkbox/CheckBox.svelte +20 -6
  35. package/dist/checkbox/CheckBox.svelte.d.ts +4 -2
  36. package/dist/checkbox/README.md +562 -0
  37. package/dist/checkbox/README_CN.md +727 -0
  38. package/dist/common-editor/NumberInput.svelte +39 -26
  39. package/dist/common-editor/NumberInput.svelte.d.ts +5 -1
  40. package/dist/concise-data-table/ConciseListTable.svelte +133 -36
  41. package/dist/concise-data-table/ConciseListTable.svelte.d.ts +8 -1
  42. package/dist/concise-data-table/DataCell.svelte +5 -6
  43. package/dist/concise-data-table/DataRow.svelte +22 -2
  44. package/dist/concise-data-table/DataRow.svelte.d.ts +1 -0
  45. package/dist/concise-data-table/README.md +606 -0
  46. package/dist/concise-data-table/README_CN.md +611 -0
  47. package/dist/context-menu/ContextMenu.svelte +138 -21
  48. package/dist/context-menu/ContextMenu.svelte.d.ts +5 -0
  49. package/dist/context-menu/ContextMenuItem.d.ts +20 -4
  50. package/dist/context-menu/README.md +551 -0
  51. package/dist/context-menu/README_CN.md +551 -0
  52. package/dist/criteria-field/CriteriaField.svelte +3 -5
  53. package/dist/criteria-field/README.md +532 -0
  54. package/dist/criteria-field/README_CN.md +532 -0
  55. package/dist/data-table/README.md +712 -0
  56. package/dist/data-table/README_CN.md +712 -0
  57. package/dist/date-picker/DatePicker.svelte +44 -7
  58. package/dist/date-picker/DatePicker.svelte.d.ts +3 -4
  59. package/dist/date-picker/DateTimePicker.svelte +32 -8
  60. package/dist/date-picker/DateTimePicker.svelte.d.ts +3 -4
  61. package/dist/date-picker/README.md +326 -0
  62. package/dist/date-picker/README_CN.md +316 -0
  63. package/dist/date-picker/ScrollBar.svelte +18 -8
  64. package/dist/date-picker/ScrollBar.svelte.d.ts +1 -1
  65. package/dist/date-picker/TimePanel.svelte +5 -5
  66. package/dist/date-picker/TimePanel.svelte.d.ts +1 -1
  67. package/dist/date-range/README.md +374 -0
  68. package/dist/date-range/README_CN.md +374 -0
  69. package/dist/dialog/README.md +596 -0
  70. package/dist/dialog/README_CN.md +596 -0
  71. package/dist/drawer/README.md +679 -1
  72. package/dist/drawer/README_CN.md +679 -0
  73. package/dist/group-box/group-check-box/GroupCheckBox.svelte +62 -5
  74. package/dist/group-box/group-check-box/GroupCheckBox.svelte.d.ts +7 -2
  75. package/dist/group-box/group-check-box/README.md +749 -0
  76. package/dist/group-box/group-check-box/README_CN.md +750 -0
  77. package/dist/group-box/group-radio-box/GroupRadioBox.svelte +69 -4
  78. package/dist/group-box/group-radio-box/GroupRadioBox.svelte.d.ts +8 -2
  79. package/dist/group-box/group-radio-box/README.md +920 -0
  80. package/dist/group-box/group-radio-box/README_CN.md +920 -0
  81. package/dist/image-files/README.md +1346 -0
  82. package/dist/image-files/README_CN.md +1346 -0
  83. package/dist/input-options-select/InputOptionsSelect.svelte +161 -65
  84. package/dist/input-options-select/InputOptionsSelect.svelte.d.ts +4 -1
  85. package/dist/lib/forwardAllEvents.d.ts +3 -0
  86. package/dist/lib/forwardAllEvents.js +24 -0
  87. package/dist/list-box/README.md +579 -0
  88. package/dist/list-box/README_CN.md +682 -0
  89. package/dist/lookup-editor/LookupEditor.svelte +15 -3
  90. package/dist/lookup-editor/LookupEditor.svelte.d.ts +3 -4
  91. package/dist/lookup-editor/README.md +684 -0
  92. package/dist/lookup-editor/README_CN.md +684 -0
  93. package/dist/memo-editor/MemoEditor.svelte +77 -27
  94. package/dist/memo-editor/MemoEditor.svelte.d.ts +6 -3
  95. package/dist/memo-editor/README.md +819 -0
  96. package/dist/memo-editor/README_CN.md +819 -0
  97. package/dist/message-box/README.md +398 -0
  98. package/dist/message-box/README_CN.md +398 -0
  99. package/dist/number-editor/NumberEditor.svelte +2 -2
  100. package/dist/number-editor/NumberEditor.svelte.d.ts +1 -1
  101. package/dist/number-editor/README.md +353 -22
  102. package/dist/number-editor/README_CN.md +357 -0
  103. package/dist/number-range/README.md +443 -0
  104. package/dist/number-range/README_CN.md +443 -0
  105. package/dist/options-multi-select/OptionsMultiSelect.svelte +87 -12
  106. package/dist/options-multi-select/OptionsMultiSelect.svelte.d.ts +3 -1
  107. package/dist/options-multi-select/README.md +480 -0
  108. package/dist/options-multi-select/README_CN.md +480 -0
  109. package/dist/options-select/OptionsSelect.svelte +36 -5
  110. package/dist/options-select/OptionsSelect.svelte.d.ts +3 -4
  111. package/dist/options-select/README.md +632 -0
  112. package/dist/options-select/README_CN.md +632 -0
  113. package/dist/page/README.md +605 -0
  114. package/dist/page/README_CN.md +605 -0
  115. package/dist/pagination/README.md +946 -0
  116. package/dist/pagination/README_CN.md +946 -0
  117. package/dist/pagination-panel/README.md +1075 -0
  118. package/dist/pagination-panel/README_CN.md +1075 -0
  119. package/dist/popup-hint/README.md +841 -0
  120. package/dist/popup-hint/README_CN.md +840 -0
  121. package/dist/progress-bar/README.md +242 -0
  122. package/dist/progress-bar/README_CN.md +242 -0
  123. package/dist/progress-step-bar/README.md +316 -0
  124. package/dist/progress-step-bar/README_CN.md +316 -0
  125. package/dist/prompts-text-editor/PromptsTextEditor.svelte +6 -3
  126. package/dist/prompts-text-editor/PromptsTextEditor.svelte.d.ts +3 -1
  127. package/dist/prompts-text-editor/README.md +329 -0
  128. package/dist/prompts-text-editor/README_CN.md +329 -0
  129. package/dist/property-editor/README.md +969 -0
  130. package/dist/property-editor/README_CN.md +969 -0
  131. package/dist/radio-button/README.md +372 -0
  132. package/dist/radio-button/README_CN.md +372 -0
  133. package/dist/radio-button/RadioButton.svelte +32 -3
  134. package/dist/radio-button/RadioButton.svelte.d.ts +12 -1
  135. package/dist/search-box/README.md +425 -0
  136. package/dist/search-box/README_CN.md +424 -0
  137. package/dist/search-box/SearchBox.svelte +22 -6
  138. package/dist/search-box/SearchBox.svelte.d.ts +4 -1
  139. package/dist/tabs/README.md +919 -0
  140. package/dist/tabs/README_CN.md +920 -0
  141. package/dist/tag/README.md +922 -0
  142. package/dist/tag/README_CN.md +922 -0
  143. package/dist/text-editor/PasswordEditor.svelte +17 -2
  144. package/dist/text-editor/PasswordEditor.svelte.d.ts +3 -3
  145. package/dist/text-editor/PasswordEditor_README.md +551 -0
  146. package/dist/text-editor/PasswordEditor_README_CN.md +551 -0
  147. package/dist/text-editor/README.md +466 -0
  148. package/dist/text-editor/README_CN.md +466 -0
  149. package/dist/text-editor/TextEditor.svelte +51 -19
  150. package/dist/text-editor/TextEditor.svelte.d.ts +0 -1
  151. package/dist/time-editor/README.md +498 -24
  152. package/dist/time-editor/README_CN.md +498 -0
  153. package/dist/time-editor/TimeEditor.svelte +7 -3
  154. package/dist/time-editor/TimeEditor.svelte.d.ts +3 -1
  155. package/dist/toast/README.md +667 -0
  156. package/dist/toast/README_CN.md +667 -0
  157. package/dist/transfer/README.md +795 -0
  158. package/dist/transfer/Transfer.svelte +6 -4
  159. package/dist/transfer/Transfer.svelte.d.ts +3 -1
  160. package/dist/tree-view/README.md +761 -0
  161. package/dist/tree-view/README_CN.md +761 -0
  162. package/dist/tree-view/TreeView.svelte +5 -3
  163. package/dist/tree-view/TreeView.svelte.d.ts +3 -1
  164. package/dist/unit-number-editor/README.md +546 -20
  165. package/dist/unit-number-editor/README_CN.md +525 -0
  166. package/dist/unit-number-editor/UnitNumberEditor.svelte +62 -8
  167. package/dist/unit-number-editor/UnitNumberEditor.svelte.d.ts +5 -1
  168. package/docs/form-field/cn/README_CN.md +364 -0
  169. package/docs/form-field/en/README.md +364 -0
  170. package/docs/form-panel/flex-form/cn/README_CN.md +586 -0
  171. package/docs/form-panel/flex-form/en/README.md +586 -0
  172. package/docs/form-panel/flex-row-form/cn/README_CN.md +787 -0
  173. package/docs/form-panel/flex-row-form/en/README.md +787 -0
  174. package/docs/form-panel/grid-form/en/README.md +744 -0
  175. package/package.json +1 -1
  176. package/dist/drawer/README-CN.md +0 -1
@@ -0,0 +1,795 @@
1
+ # Transfer
2
+
3
+ A dual-panel selection component that allows users to move items between two lists. Features include multiple selection, bulk transfer operations, custom item rendering, and search filtering. Perfect for role assignments, permission management, and data organization tasks.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @ticatec/uniface-element
9
+ ```
10
+
11
+ ## Import
12
+
13
+ ```typescript
14
+ import Transfer, { type TransferData } from "@ticatec/uniface-element/Transfer";
15
+ ```
16
+
17
+ ## Basic Usage
18
+
19
+ ```svelte
20
+ <script>
21
+ import Transfer from "@ticatec/uniface-element/Transfer";
22
+
23
+ let availableUsers = [
24
+ { id: 1, name: "John Doe", email: "john@example.com" },
25
+ { id: 2, name: "Jane Smith", email: "jane@example.com" },
26
+ { id: 3, name: "Bob Wilson", email: "bob@example.com" },
27
+ { id: 4, name: "Alice Brown", email: "alice@example.com" }
28
+ ];
29
+
30
+ let selectedUsers = [
31
+ { id: 5, name: "Charlie Davis", email: "charlie@example.com" }
32
+ ];
33
+
34
+ async function handleTransfer(items, direction) {
35
+ console.log(`Moving ${items.length} items ${direction}`);
36
+ // Add your transfer logic here
37
+ return true; // Return true to allow the transfer
38
+ }
39
+ </script>
40
+
41
+ <Transfer
42
+ sourceList={availableUsers}
43
+ sourceTitle="Available Users"
44
+ targetList={selectedUsers}
45
+ targetTitle="Selected Users"
46
+ onchange={handleTransfer}
47
+ />
48
+ ```
49
+
50
+ ## Props
51
+
52
+ | Prop | Type | Default | Description |
53
+ |------|------|---------|-------------|
54
+ | `sourceList` | `Array<any>` | Required | Items available for selection |
55
+ | `sourceTitle` | `string` | Required | Title for the source panel |
56
+ | `targetList` | `Array<any>` | Required | Currently selected items |
57
+ | `targetTitle` | `string` | Required | Title for the target panel |
58
+ | `onchange` | `TransferData` | `null` | Callback when items are transferred |
59
+ | `onfocus` | `((event: FocusEvent) => void) \| null` | `null` | Callback when component gains focus |
60
+ | `onblur` | `((event: FocusEvent) => void) \| null` | `null` | Callback when component loses focus |
61
+ | `itemRender` | `ComponentType \| null` | `null` | Custom component for rendering items |
62
+ | `filter` | `FunFilter \| null` | `null` | Search filter function |
63
+ | `style` | `string` | `""` | Additional CSS styles |
64
+ | `footer$style` | `string` | Default footer styling | Custom footer styles |
65
+
66
+ ## Type Definitions
67
+
68
+ ### TransferData
69
+ ```typescript
70
+ type TransferData = (list: Array<any>, direction: "left" | "right") => Promise<boolean>;
71
+ ```
72
+
73
+ The transfer callback function receives:
74
+ - `list`: Array of items being transferred
75
+ - `direction`: "left" (to source) or "right" (to target)
76
+ - Returns: Promise<boolean> - true to allow transfer, false to prevent
77
+
78
+ ## Examples
79
+
80
+ ### User Role Management
81
+
82
+ ```svelte
83
+ <script>
84
+ import Transfer from "@ticatec/uniface-element/Transfer";
85
+
86
+ let availableUsers = [
87
+ { id: 1, name: "John Doe", role: "Developer", department: "Engineering" },
88
+ { id: 2, name: "Jane Smith", role: "Designer", department: "Product" },
89
+ { id: 3, name: "Bob Wilson", role: "Manager", department: "Sales" },
90
+ { id: 4, name: "Alice Brown", role: "Analyst", department: "Marketing" }
91
+ ];
92
+
93
+ let adminUsers = [
94
+ { id: 5, name: "Charlie Davis", role: "Admin", department: "IT" }
95
+ ];
96
+
97
+ async function handleRoleAssignment(users, direction) {
98
+ console.log(`${direction === 'right' ? 'Granting' : 'Revoking'} admin access for:`, users);
99
+
100
+ // Simulate API call
101
+ try {
102
+ await new Promise(resolve => setTimeout(resolve, 1000));
103
+
104
+ if (direction === 'right') {
105
+ // Grant admin access
106
+ users.forEach(user => {
107
+ console.log(`Granted admin access to ${user.name}`);
108
+ });
109
+ } else {
110
+ // Revoke admin access
111
+ users.forEach(user => {
112
+ console.log(`Revoked admin access from ${user.name}`);
113
+ });
114
+ }
115
+
116
+ return true;
117
+ } catch (error) {
118
+ console.error('Failed to update user roles:', error);
119
+ return false;
120
+ }
121
+ }
122
+ </script>
123
+
124
+ <div class="role-management">
125
+ <h3>Admin Role Management</h3>
126
+ <Transfer
127
+ sourceList={availableUsers}
128
+ sourceTitle="Available Users"
129
+ targetList={adminUsers}
130
+ targetTitle="Admin Users"
131
+ onchange={handleRoleAssignment}
132
+ />
133
+ </div>
134
+
135
+ <style>
136
+ .role-management {
137
+ max-width: 800px;
138
+ margin: 20px auto;
139
+ padding: 20px;
140
+ }
141
+
142
+ h3 {
143
+ margin-bottom: 20px;
144
+ text-align: center;
145
+ }
146
+ </style>
147
+ ```
148
+
149
+ ### With Custom Item Rendering
150
+
151
+ ```svelte
152
+ <script>
153
+ import Transfer from "@ticatec/uniface-element/Transfer";
154
+ import UserCard from "./UserCard.svelte"; // Custom component
155
+
156
+ let candidates = [
157
+ {
158
+ id: 1,
159
+ name: "John Doe",
160
+ email: "john@example.com",
161
+ skills: ["JavaScript", "React", "Node.js"],
162
+ experience: 5
163
+ },
164
+ {
165
+ id: 2,
166
+ name: "Jane Smith",
167
+ email: "jane@example.com",
168
+ skills: ["Python", "Django", "PostgreSQL"],
169
+ experience: 3
170
+ },
171
+ {
172
+ id: 3,
173
+ name: "Bob Wilson",
174
+ email: "bob@example.com",
175
+ skills: ["Java", "Spring", "MySQL"],
176
+ experience: 7
177
+ }
178
+ ];
179
+
180
+ let shortlisted = [];
181
+
182
+ async function handleShortlist(candidates, direction) {
183
+ // Add validation logic
184
+ if (direction === 'right' && shortlisted.length + candidates.length > 5) {
185
+ alert('Cannot shortlist more than 5 candidates');
186
+ return false;
187
+ }
188
+
189
+ return true;
190
+ }
191
+ </script>
192
+
193
+ <!-- UserCard.svelte -->
194
+ <script>
195
+ export let item;
196
+ </script>
197
+
198
+ <div class="user-card">
199
+ <div class="user-header">
200
+ <h4>{item.name}</h4>
201
+ <span class="experience">{item.experience} years</span>
202
+ </div>
203
+ <p class="email">{item.email}</p>
204
+ <div class="skills">
205
+ {#each item.skills as skill}
206
+ <span class="skill-tag">{skill}</span>
207
+ {/each}
208
+ </div>
209
+ </div>
210
+
211
+ <style>
212
+ .user-card {
213
+ padding: 12px;
214
+ border: 1px solid #eee;
215
+ border-radius: 4px;
216
+ margin: 4px 0;
217
+ }
218
+
219
+ .user-header {
220
+ display: flex;
221
+ justify-content: space-between;
222
+ align-items: center;
223
+ margin-bottom: 8px;
224
+ }
225
+
226
+ .user-header h4 {
227
+ margin: 0;
228
+ font-size: 16px;
229
+ }
230
+
231
+ .experience {
232
+ background: #007bff;
233
+ color: white;
234
+ padding: 2px 8px;
235
+ border-radius: 12px;
236
+ font-size: 12px;
237
+ }
238
+
239
+ .email {
240
+ margin: 0 0 8px 0;
241
+ color: #666;
242
+ font-size: 14px;
243
+ }
244
+
245
+ .skills {
246
+ display: flex;
247
+ flex-wrap: wrap;
248
+ gap: 4px;
249
+ }
250
+
251
+ .skill-tag {
252
+ background: #f8f9fa;
253
+ border: 1px solid #dee2e6;
254
+ padding: 2px 6px;
255
+ border-radius: 4px;
256
+ font-size: 12px;
257
+ }
258
+ </style>
259
+
260
+ <!-- Main component -->
261
+ <div class="candidate-selection">
262
+ <h3>Candidate Shortlisting</h3>
263
+ <Transfer
264
+ sourceList={candidates}
265
+ sourceTitle="All Candidates"
266
+ targetList={shortlisted}
267
+ targetTitle="Shortlisted"
268
+ itemRender={UserCard}
269
+ onchange={handleShortlist}
270
+ />
271
+ </div>
272
+
273
+ <style>
274
+ .candidate-selection {
275
+ max-width: 900px;
276
+ margin: 20px auto;
277
+ padding: 20px;
278
+ }
279
+ </style>
280
+ ```
281
+
282
+ ### With Search Filter
283
+
284
+ ```svelte
285
+ <script>
286
+ import Transfer from "@ticatec/uniface-element/Transfer";
287
+
288
+ let allProducts = [
289
+ { id: 1, name: "Laptop Pro", category: "Electronics", price: 1299 },
290
+ { id: 2, name: "Wireless Mouse", category: "Electronics", price: 25 },
291
+ { id: 3, name: "Coffee Maker", category: "Kitchen", price: 89 },
292
+ { id: 4, name: "Desk Chair", category: "Furniture", price: 199 },
293
+ { id: 5, name: "Monitor Stand", category: "Electronics", price: 45 },
294
+ { id: 6, name: "Blender", category: "Kitchen", price: 120 }
295
+ ];
296
+
297
+ let cartItems = [];
298
+
299
+ // Custom filter function
300
+ function productFilter(item, searchTerm) {
301
+ if (!searchTerm) return true;
302
+
303
+ const term = searchTerm.toLowerCase();
304
+ return item.name.toLowerCase().includes(term) ||
305
+ item.category.toLowerCase().includes(term) ||
306
+ item.price.toString().includes(term);
307
+ }
308
+
309
+ async function handleAddToCart(products, direction) {
310
+ if (direction === 'right') {
311
+ console.log('Adding to cart:', products);
312
+ } else {
313
+ console.log('Removing from cart:', products);
314
+ }
315
+ return true;
316
+ }
317
+ </script>
318
+
319
+ <div class="product-transfer">
320
+ <h3>Product Selection</h3>
321
+ <Transfer
322
+ sourceList={allProducts}
323
+ sourceTitle="Available Products"
324
+ targetList={cartItems}
325
+ targetTitle="Shopping Cart"
326
+ filter={productFilter}
327
+ onchange={handleAddToCart}
328
+ />
329
+
330
+ {#if cartItems.length > 0}
331
+ <div class="cart-summary">
332
+ <h4>Cart Summary</h4>
333
+ <p>Items: {cartItems.length}</p>
334
+ <p>Total: ${cartItems.reduce((sum, item) => sum + item.price, 0)}</p>
335
+ </div>
336
+ {/if}
337
+ </div>
338
+
339
+ <style>
340
+ .product-transfer {
341
+ max-width: 800px;
342
+ margin: 20px auto;
343
+ padding: 20px;
344
+ }
345
+
346
+ .cart-summary {
347
+ margin-top: 20px;
348
+ padding: 16px;
349
+ background: #f8f9fa;
350
+ border-radius: 8px;
351
+ text-align: center;
352
+ }
353
+
354
+ .cart-summary h4 {
355
+ margin: 0 0 12px 0;
356
+ }
357
+
358
+ .cart-summary p {
359
+ margin: 4px 0;
360
+ font-size: 16px;
361
+ }
362
+ </style>
363
+ ```
364
+
365
+ ### Permission Management
366
+
367
+ ```svelte
368
+ <script>
369
+ import Transfer from "@ticatec/uniface-element/Transfer";
370
+
371
+ let availablePermissions = [
372
+ { id: 1, name: "Read Users", module: "User Management", level: "Read" },
373
+ { id: 2, name: "Create Users", module: "User Management", level: "Write" },
374
+ { id: 3, name: "Delete Users", module: "User Management", level: "Admin" },
375
+ { id: 4, name: "View Reports", module: "Analytics", level: "Read" },
376
+ { id: 5, name: "Export Data", module: "Analytics", level: "Write" },
377
+ { id: 6, name: "System Settings", module: "Administration", level: "Admin" }
378
+ ];
379
+
380
+ let rolePermissions = [];
381
+ let selectedRole = "Editor";
382
+
383
+ async function handlePermissionChange(permissions, direction) {
384
+ console.log(`${direction === 'right' ? 'Granting' : 'Revoking'} permissions:`, permissions);
385
+
386
+ // Validation: Admin permissions require approval
387
+ const adminPermissions = permissions.filter(p => p.level === 'Admin');
388
+ if (direction === 'right' && adminPermissions.length > 0) {
389
+ const confirmed = confirm(
390
+ `You are about to grant admin-level permissions. Continue?`
391
+ );
392
+ if (!confirmed) return false;
393
+ }
394
+
395
+ return true;
396
+ }
397
+
398
+ function resetPermissions() {
399
+ rolePermissions = [];
400
+ availablePermissions = [...availablePermissions, ...rolePermissions];
401
+ }
402
+ </script>
403
+
404
+ <div class="permission-management">
405
+ <div class="role-header">
406
+ <h3>Role: {selectedRole}</h3>
407
+ <select bind:value={selectedRole}>
408
+ <option value="Viewer">Viewer</option>
409
+ <option value="Editor">Editor</option>
410
+ <option value="Admin">Admin</option>
411
+ </select>
412
+ <button on:click={resetPermissions}>Reset</button>
413
+ </div>
414
+
415
+ <Transfer
416
+ sourceList={availablePermissions}
417
+ sourceTitle="Available Permissions"
418
+ targetList={rolePermissions}
419
+ targetTitle="Role Permissions"
420
+ onchange={handlePermissionChange}
421
+ />
422
+
423
+ <div class="permission-summary">
424
+ <h4>Permission Summary</h4>
425
+ {#each ['Read', 'Write', 'Admin'] as level}
426
+ {@const count = rolePermissions.filter(p => p.level === level).length}
427
+ <div class="permission-level">
428
+ <span class="level-name">{level}:</span>
429
+ <span class="level-count">{count}</span>
430
+ </div>
431
+ {/each}
432
+ </div>
433
+ </div>
434
+
435
+ <style>
436
+ .permission-management {
437
+ max-width: 900px;
438
+ margin: 20px auto;
439
+ padding: 20px;
440
+ }
441
+
442
+ .role-header {
443
+ display: flex;
444
+ align-items: center;
445
+ gap: 16px;
446
+ margin-bottom: 20px;
447
+ }
448
+
449
+ .role-header h3 {
450
+ margin: 0;
451
+ flex: 1;
452
+ }
453
+
454
+ .role-header select,
455
+ .role-header button {
456
+ padding: 8px 12px;
457
+ border: 1px solid #ccc;
458
+ border-radius: 4px;
459
+ }
460
+
461
+ .permission-summary {
462
+ margin-top: 20px;
463
+ padding: 16px;
464
+ background: #f8f9fa;
465
+ border-radius: 8px;
466
+ }
467
+
468
+ .permission-summary h4 {
469
+ margin: 0 0 12px 0;
470
+ }
471
+
472
+ .permission-level {
473
+ display: flex;
474
+ justify-content: space-between;
475
+ margin: 8px 0;
476
+ }
477
+
478
+ .level-name {
479
+ font-weight: 500;
480
+ }
481
+
482
+ .level-count {
483
+ background: #007bff;
484
+ color: white;
485
+ padding: 2px 8px;
486
+ border-radius: 12px;
487
+ font-size: 14px;
488
+ }
489
+ </style>
490
+ ```
491
+
492
+ ### With Event Handlers
493
+
494
+ ```svelte
495
+ <script>
496
+ import Transfer from "@ticatec/uniface-element/Transfer";
497
+
498
+ let sourceItems = ["Item 1", "Item 2", "Item 3", "Item 4"];
499
+ let targetItems = ["Item 5"];
500
+ let isFocused = false;
501
+ let transferLog = [];
502
+
503
+ async function handleTransfer(items, direction) {
504
+ const action = direction === 'right' ? 'moved to target' : 'moved to source';
505
+ const logEntry = `${new Date().toLocaleTimeString()}: ${items.length} item(s) ${action}`;
506
+ transferLog = [...transferLog, logEntry];
507
+
508
+ return true;
509
+ }
510
+
511
+ function handleFocus(event) {
512
+ console.log('Transfer component focused');
513
+ isFocused = true;
514
+ }
515
+
516
+ function handleBlur(event) {
517
+ console.log('Transfer component blurred');
518
+ isFocused = false;
519
+ }
520
+ </script>
521
+
522
+ <div class="event-demo">
523
+ <h3>Transfer with Event Handling</h3>
524
+ <p>Component is {isFocused ? 'focused' : 'not focused'}</p>
525
+
526
+ <Transfer
527
+ sourceList={sourceItems}
528
+ sourceTitle="Source"
529
+ targetList={targetItems}
530
+ targetTitle="Target"
531
+ onchange={handleTransfer}
532
+ onfocus={handleFocus}
533
+ onblur={handleBlur}
534
+ />
535
+
536
+ <div class="transfer-log">
537
+ <h4>Transfer Log:</h4>
538
+ {#each transferLog.slice(-5) as entry}
539
+ <p>{entry}</p>
540
+ {/each}
541
+ </div>
542
+ </div>
543
+
544
+ <style>
545
+ .event-demo {
546
+ max-width: 800px;
547
+ margin: 20px auto;
548
+ padding: 20px;
549
+ }
550
+
551
+ .transfer-log {
552
+ margin-top: 20px;
553
+ padding: 16px;
554
+ background: #f8f9fa;
555
+ border-radius: 8px;
556
+ max-height: 200px;
557
+ overflow-y: auto;
558
+ }
559
+
560
+ .transfer-log h4 {
561
+ margin: 0 0 12px 0;
562
+ }
563
+
564
+ .transfer-log p {
565
+ margin: 4px 0;
566
+ font-family: monospace;
567
+ font-size: 14px;
568
+ }
569
+ </style>
570
+ ```
571
+
572
+ ### Bulk Operations
573
+
574
+ ```svelte
575
+ <script>
576
+ import Transfer from "@ticatec/uniface-element/Transfer";
577
+
578
+ let employees = [
579
+ { id: 1, name: "John Doe", department: "Engineering", salary: 75000 },
580
+ { id: 2, name: "Jane Smith", department: "Marketing", salary: 65000 },
581
+ { id: 3, name: "Bob Wilson", department: "Sales", salary: 70000 },
582
+ { id: 4, name: "Alice Brown", department: "HR", salary: 60000 },
583
+ { id: 5, name: "Charlie Davis", department: "Engineering", salary: 80000 }
584
+ ];
585
+
586
+ let projectTeam = [];
587
+ let maxTeamSize = 3;
588
+
589
+ async function handleTeamAssignment(members, direction) {
590
+ if (direction === 'right') {
591
+ // Check team size limit
592
+ if (projectTeam.length + members.length > maxTeamSize) {
593
+ alert(`Team size cannot exceed ${maxTeamSize} members`);
594
+ return false;
595
+ }
596
+
597
+ // Check budget constraint
598
+ const newBudget = [...projectTeam, ...members]
599
+ .reduce((sum, member) => sum + member.salary, 0);
600
+
601
+ if (newBudget > 200000) {
602
+ const confirmed = confirm(
603
+ `Adding these members will exceed budget ($${newBudget.toLocaleString()}). Continue?`
604
+ );
605
+ if (!confirmed) return false;
606
+ }
607
+ }
608
+
609
+ return true;
610
+ }
611
+
612
+ function moveAllToTarget() {
613
+ if (employees.length + projectTeam.length <= maxTeamSize) {
614
+ projectTeam = [...projectTeam, ...employees];
615
+ employees = [];
616
+ } else {
617
+ alert(`Cannot add all employees. Team size limit: ${maxTeamSize}`);
618
+ }
619
+ }
620
+
621
+ function moveAllToSource() {
622
+ employees = [...employees, ...projectTeam];
623
+ projectTeam = [];
624
+ }
625
+
626
+ $: currentBudget = projectTeam.reduce((sum, member) => sum + member.salary, 0);
627
+ </script>
628
+
629
+ <div class="team-builder">
630
+ <div class="controls">
631
+ <h3>Project Team Builder</h3>
632
+ <div class="bulk-actions">
633
+ <button on:click={moveAllToTarget}>Add All</button>
634
+ <button on:click={moveAllToSource}>Remove All</button>
635
+ </div>
636
+ </div>
637
+
638
+ <Transfer
639
+ sourceList={employees}
640
+ sourceTitle="Available Employees"
641
+ targetList={projectTeam}
642
+ targetTitle="Project Team"
643
+ onchange={handleTeamAssignment}
644
+ />
645
+
646
+ <div class="team-stats">
647
+ <div class="stat">
648
+ <span>Team Size:</span>
649
+ <span>{projectTeam.length}/{maxTeamSize}</span>
650
+ </div>
651
+ <div class="stat">
652
+ <span>Budget:</span>
653
+ <span>${currentBudget.toLocaleString()}</span>
654
+ </div>
655
+ <div class="stat">
656
+ <span>Avg Salary:</span>
657
+ <span>${projectTeam.length > 0 ? Math.round(currentBudget / projectTeam.length).toLocaleString() : 0}</span>
658
+ </div>
659
+ </div>
660
+ </div>
661
+
662
+ <style>
663
+ .team-builder {
664
+ max-width: 900px;
665
+ margin: 20px auto;
666
+ padding: 20px;
667
+ }
668
+
669
+ .controls {
670
+ display: flex;
671
+ justify-content: space-between;
672
+ align-items: center;
673
+ margin-bottom: 20px;
674
+ }
675
+
676
+ .controls h3 {
677
+ margin: 0;
678
+ }
679
+
680
+ .bulk-actions {
681
+ display: flex;
682
+ gap: 8px;
683
+ }
684
+
685
+ .bulk-actions button {
686
+ padding: 8px 16px;
687
+ border: 1px solid #007bff;
688
+ background: white;
689
+ color: #007bff;
690
+ border-radius: 4px;
691
+ cursor: pointer;
692
+ }
693
+
694
+ .bulk-actions button:hover {
695
+ background: #007bff;
696
+ color: white;
697
+ }
698
+
699
+ .team-stats {
700
+ margin-top: 20px;
701
+ display: grid;
702
+ grid-template-columns: repeat(3, 1fr);
703
+ gap: 16px;
704
+ }
705
+
706
+ .stat {
707
+ padding: 16px;
708
+ background: #f8f9fa;
709
+ border-radius: 8px;
710
+ text-align: center;
711
+ }
712
+
713
+ .stat span:first-child {
714
+ display: block;
715
+ font-weight: 500;
716
+ margin-bottom: 8px;
717
+ }
718
+
719
+ .stat span:last-child {
720
+ font-size: 18px;
721
+ color: #007bff;
722
+ font-weight: bold;
723
+ }
724
+ </style>
725
+ ```
726
+
727
+ ## Features
728
+
729
+ - **Dual Panel Layout**: Clean source and target lists with transfer controls
730
+ - **Multiple Selection**: Select multiple items for bulk transfer operations
731
+ - **Custom Rendering**: Use custom components to display items
732
+ - **Search & Filter**: Built-in search functionality with custom filter functions
733
+ - **Transfer Validation**: Async callback system for validating transfers
734
+ - **Event Handling**: Focus, blur, and change event support
735
+ - **Keyboard Accessibility**: Full keyboard navigation support
736
+ - **Responsive Design**: Adapts to different screen sizes
737
+ - **Internationalization**: Built-in i18n support for labels
738
+
739
+ ## Styling
740
+
741
+ The Transfer component uses CSS Grid layout and can be customized with CSS:
742
+
743
+ ```css
744
+ /* Custom transfer styling */
745
+ .uniface-transfer-container {
746
+ /* Your custom styles */
747
+ display: grid;
748
+ grid-template-columns: 1fr auto 1fr;
749
+ gap: 16px;
750
+ min-height: 400px;
751
+ }
752
+
753
+ .list-container {
754
+ /* Custom list box styles */
755
+ }
756
+
757
+ .icon-button-container {
758
+ /* Custom button container styles */
759
+ display: flex;
760
+ flex-direction: column;
761
+ justify-content: center;
762
+ gap: 8px;
763
+ }
764
+ ```
765
+
766
+ ## Accessibility
767
+
768
+ - Keyboard navigation with Tab and Enter keys
769
+ - ARIA attributes for screen readers
770
+ - Focus management and visual indicators
771
+ - Semantic HTML structure
772
+ - High contrast support
773
+
774
+ ## Best Practices
775
+
776
+ 1. **Validation**: Always implement transfer validation in the `onchange` callback
777
+ 2. **Custom Rendering**: Use `itemRender` for complex item display needs
778
+ 3. **Search**: Implement appropriate filter functions for large datasets
779
+ 4. **Error Handling**: Provide clear feedback when transfers fail
780
+ 5. **Performance**: Consider pagination for very large lists
781
+ 6. **Accessibility**: Ensure proper keyboard navigation and screen reader support
782
+
783
+ ## Browser Support
784
+
785
+ - Modern browsers with full event support
786
+ - Compatible with Svelte 5+
787
+ - CSS Grid layout support
788
+ - Touch-friendly interface
789
+ - Full TypeScript support
790
+
791
+ ## Related Components
792
+
793
+ - `ListBox` - Single list selection component
794
+ - `OptionsSelect` - Dropdown selection component
795
+ - `CheckboxGroup` - Multiple checkbox selection