@ticatec/uniface-element 0.2.4 → 0.3.1

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 (177) 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/ticatec-uniface-web.css +2 -0
  152. package/dist/time-editor/README.md +498 -24
  153. package/dist/time-editor/README_CN.md +498 -0
  154. package/dist/time-editor/TimeEditor.svelte +7 -3
  155. package/dist/time-editor/TimeEditor.svelte.d.ts +3 -1
  156. package/dist/toast/README.md +667 -0
  157. package/dist/toast/README_CN.md +667 -0
  158. package/dist/transfer/README.md +795 -0
  159. package/dist/transfer/Transfer.svelte +6 -4
  160. package/dist/transfer/Transfer.svelte.d.ts +3 -1
  161. package/dist/tree-view/README.md +761 -0
  162. package/dist/tree-view/README_CN.md +761 -0
  163. package/dist/tree-view/TreeView.svelte +5 -3
  164. package/dist/tree-view/TreeView.svelte.d.ts +3 -1
  165. package/dist/unit-number-editor/README.md +546 -20
  166. package/dist/unit-number-editor/README_CN.md +525 -0
  167. package/dist/unit-number-editor/UnitNumberEditor.svelte +62 -8
  168. package/dist/unit-number-editor/UnitNumberEditor.svelte.d.ts +5 -1
  169. package/docs/form-field/cn/README_CN.md +364 -0
  170. package/docs/form-field/en/README.md +364 -0
  171. package/docs/form-panel/flex-form/cn/README_CN.md +586 -0
  172. package/docs/form-panel/flex-form/en/README.md +586 -0
  173. package/docs/form-panel/flex-row-form/cn/README_CN.md +787 -0
  174. package/docs/form-panel/flex-row-form/en/README.md +787 -0
  175. package/docs/form-panel/grid-form/en/README.md +744 -0
  176. package/package.json +1 -1
  177. package/dist/drawer/README-CN.md +0 -1
@@ -0,0 +1,551 @@
1
+ # ContextMenu Component
2
+
3
+ A powerful and accessible context menu component that provides right-click contextual actions with full keyboard navigation support. Perfect for creating rich interactive experiences with contextual commands and options.
4
+
5
+ ## Features
6
+
7
+ - **Contextual Actions**: Right-click to show relevant menu options
8
+ - **Keyboard Navigation**: Full arrow key navigation and keyboard shortcuts
9
+ - **Accessibility**: ARIA attributes and screen reader support
10
+ - **Auto Positioning**: Smart positioning to stay within viewport bounds
11
+ - **Icon Support**: Optional icons for menu items
12
+ - **Disabled States**: Support for disabled menu items
13
+ - **Separators**: Visual separation between menu groups
14
+ - **Event Handling**: Focus, blur, and hide event callbacks
15
+ - **Portal Rendering**: Renders outside component hierarchy for proper layering
16
+
17
+ ## Basic Usage
18
+
19
+ ```svelte
20
+ <script>
21
+ import ContextMenu from '@ticatec/uniface-element/ContextMenu';
22
+
23
+ let contextMenu;
24
+
25
+ const menuItems = [
26
+ { text: 'Copy', action: () => console.log('Copy') },
27
+ { text: 'Paste', action: () => console.log('Paste') },
28
+ { separator: true },
29
+ { text: 'Delete', action: () => console.log('Delete') }
30
+ ];
31
+
32
+ const handleRightClick = (event) => {
33
+ contextMenu.show(event, menuItems);
34
+ };
35
+ </script>
36
+
37
+ <div on:contextmenu={handleRightClick}>
38
+ Right-click me to show context menu
39
+ </div>
40
+
41
+ <ContextMenu bind:this={contextMenu} />
42
+ ```
43
+
44
+ ## Props
45
+
46
+ | Prop | Type | Default | Description |
47
+ |------|------|---------|-------------|
48
+ | `onfocus` | `((event: FocusEvent) => void) \| null` | `null` | Focus event handler |
49
+ | `onblur` | `((event: FocusEvent) => void) \| null` | `null` | Blur event handler |
50
+ | `onhide` | `(() => void) \| null` | `null` | Hide event handler |
51
+
52
+ ## Methods
53
+
54
+ | Method | Signature | Description |
55
+ |--------|-----------|-------------|
56
+ | `show(event, items, width?, height?)` | Shows the context menu at the event position |
57
+ | `hide()` | Hides the context menu |
58
+
59
+ ### show() Parameters
60
+
61
+ - `event`: MouseEvent - The triggering mouse event (for positioning)
62
+ - `items`: ContextMenuItem[] - Array of menu items to display
63
+ - `width`: number (optional, default: 150) - Menu width in pixels
64
+ - `height`: number (optional, default: 150) - Menu height in pixels
65
+
66
+ ## ContextMenuItem Interface
67
+
68
+ ```typescript
69
+ interface ContextMenuItem {
70
+ text: string; // Display text for the menu item
71
+ action?: () => void; // Action to execute when clicked
72
+ disabled?: boolean; // Whether the item is disabled
73
+ separator?: boolean; // Whether this is a separator item
74
+ icon?: string; // Icon class or name
75
+ style?: string; // Custom CSS styles
76
+ }
77
+ ```
78
+
79
+ ## Basic Examples
80
+
81
+ ### Simple Context Menu
82
+
83
+ ```svelte
84
+ <script>
85
+ let contextMenu;
86
+
87
+ const items = [
88
+ { text: 'New File', action: () => createFile() },
89
+ { text: 'New Folder', action: () => createFolder() },
90
+ { separator: true },
91
+ { text: 'Refresh', action: () => refresh() }
92
+ ];
93
+
94
+ const showMenu = (event) => {
95
+ contextMenu.show(event, items);
96
+ };
97
+ </script>
98
+
99
+ <div class="file-explorer" on:contextmenu={showMenu}>
100
+ <p>Right-click for options</p>
101
+ </div>
102
+
103
+ <ContextMenu bind:this={contextMenu} />
104
+ ```
105
+
106
+ ### With Icons and Disabled Items
107
+
108
+ ```svelte
109
+ <script>
110
+ let contextMenu;
111
+ let canPaste = false;
112
+
113
+ const items = [
114
+ {
115
+ text: 'Cut',
116
+ icon: 'icon-cut',
117
+ action: () => cut()
118
+ },
119
+ {
120
+ text: 'Copy',
121
+ icon: 'icon-copy',
122
+ action: () => copy()
123
+ },
124
+ {
125
+ text: 'Paste',
126
+ icon: 'icon-paste',
127
+ action: () => paste(),
128
+ disabled: !canPaste
129
+ },
130
+ { separator: true },
131
+ {
132
+ text: 'Delete',
133
+ icon: 'icon-delete',
134
+ action: () => deleteItem()
135
+ }
136
+ ];
137
+ </script>
138
+
139
+ <div on:contextmenu={(e) => contextMenu.show(e, items)}>
140
+ Content with edit operations
141
+ </div>
142
+
143
+ <ContextMenu bind:this={contextMenu} />
144
+ ```
145
+
146
+ ### Dynamic Menu Items
147
+
148
+ ```svelte
149
+ <script>
150
+ let contextMenu;
151
+ let selectedItems = [];
152
+
153
+ const getMenuItems = (target) => {
154
+ const baseItems = [
155
+ { text: 'Properties', action: () => showProperties(target) }
156
+ ];
157
+
158
+ if (selectedItems.length > 0) {
159
+ baseItems.unshift(
160
+ { text: `Delete ${selectedItems.length} items`, action: () => deleteSelected() },
161
+ { separator: true }
162
+ );
163
+ }
164
+
165
+ if (target.type === 'file') {
166
+ baseItems.unshift(
167
+ { text: 'Open', action: () => openFile(target) },
168
+ { text: 'Open with...', action: () => openWith(target) },
169
+ { separator: true }
170
+ );
171
+ }
172
+
173
+ return baseItems;
174
+ };
175
+
176
+ const handleContextMenu = (event, target) => {
177
+ const items = getMenuItems(target);
178
+ contextMenu.show(event, items);
179
+ };
180
+ </script>
181
+
182
+ <div class="item-list">
183
+ {#each items as item}
184
+ <div class="item" on:contextmenu={(e) => handleContextMenu(e, item)}>
185
+ {item.name}
186
+ </div>
187
+ {/each}
188
+ </div>
189
+
190
+ <ContextMenu bind:this={contextMenu} />
191
+ ```
192
+
193
+ ## Event Handling
194
+
195
+ ```svelte
196
+ <script>
197
+ let contextMenu;
198
+ let menuVisible = false;
199
+
200
+ const handleFocus = (event) => {
201
+ console.log('Context menu focused');
202
+ menuVisible = true;
203
+ };
204
+
205
+ const handleBlur = (event) => {
206
+ console.log('Context menu blurred');
207
+ menuVisible = false;
208
+ };
209
+
210
+ const handleHide = () => {
211
+ console.log('Context menu hidden');
212
+ menuVisible = false;
213
+ };
214
+
215
+ const items = [
216
+ { text: 'Action 1', action: () => console.log('Action 1') },
217
+ { text: 'Action 2', action: () => console.log('Action 2') }
218
+ ];
219
+ </script>
220
+
221
+ <div on:contextmenu={(e) => contextMenu.show(e, items)}>
222
+ Right-click me
223
+ </div>
224
+
225
+ <ContextMenu
226
+ bind:this={contextMenu}
227
+ onfocus={handleFocus}
228
+ onblur={handleBlur}
229
+ onhide={handleHide}
230
+ />
231
+
232
+ <p>Menu visible: {menuVisible}</p>
233
+ ```
234
+
235
+ ## Advanced Examples
236
+
237
+ ### File Manager Context Menu
238
+
239
+ ```svelte
240
+ <script>
241
+ import ContextMenu from '@ticatec/uniface-element/ContextMenu';
242
+
243
+ let contextMenu;
244
+ let files = [
245
+ { name: 'document.pdf', type: 'file', selected: false },
246
+ { name: 'images', type: 'folder', selected: false },
247
+ { name: 'script.js', type: 'file', selected: true }
248
+ ];
249
+
250
+ let clipboard = null;
251
+
252
+ const getFileMenuItems = (file) => {
253
+ const items = [];
254
+
255
+ if (file.type === 'file') {
256
+ items.push(
257
+ { text: 'Open', icon: 'icon-open', action: () => openFile(file) },
258
+ { text: 'Open with...', action: () => openWith(file) },
259
+ { separator: true }
260
+ );
261
+ }
262
+
263
+ items.push(
264
+ { text: 'Cut', icon: 'icon-cut', action: () => cutFile(file) },
265
+ { text: 'Copy', icon: 'icon-copy', action: () => copyFile(file) },
266
+ {
267
+ text: 'Paste',
268
+ icon: 'icon-paste',
269
+ action: () => pasteFile(),
270
+ disabled: !clipboard
271
+ },
272
+ { separator: true },
273
+ { text: 'Rename', action: () => renameFile(file) },
274
+ { text: 'Delete', icon: 'icon-delete', action: () => deleteFile(file) },
275
+ { separator: true },
276
+ { text: 'Properties', action: () => showProperties(file) }
277
+ );
278
+
279
+ return items;
280
+ };
281
+
282
+ const getEmptySpaceMenuItems = () => [
283
+ { text: 'New Folder', icon: 'icon-folder-plus', action: () => createFolder() },
284
+ { text: 'New File', icon: 'icon-file-plus', action: () => createFile() },
285
+ { separator: true },
286
+ {
287
+ text: 'Paste',
288
+ icon: 'icon-paste',
289
+ action: () => pasteFile(),
290
+ disabled: !clipboard
291
+ },
292
+ { separator: true },
293
+ { text: 'Refresh', icon: 'icon-refresh', action: () => refresh() }
294
+ ];
295
+
296
+ const handleFileContextMenu = (event, file) => {
297
+ const items = getFileMenuItems(file);
298
+ contextMenu.show(event, items, 200, 300);
299
+ };
300
+
301
+ const handleEmptySpaceContextMenu = (event) => {
302
+ const items = getEmptySpaceMenuItems();
303
+ contextMenu.show(event, items);
304
+ };
305
+
306
+ const openFile = (file) => console.log('Opening', file.name);
307
+ const openWith = (file) => console.log('Open with...', file.name);
308
+ const cutFile = (file) => { clipboard = { type: 'cut', file }; };
309
+ const copyFile = (file) => { clipboard = { type: 'copy', file }; };
310
+ const pasteFile = () => console.log('Pasting', clipboard?.file?.name);
311
+ const renameFile = (file) => console.log('Renaming', file.name);
312
+ const deleteFile = (file) => console.log('Deleting', file.name);
313
+ const createFolder = () => console.log('Creating folder');
314
+ const createFile = () => console.log('Creating file');
315
+ const refresh = () => console.log('Refreshing');
316
+ const showProperties = (file) => console.log('Properties for', file.name);
317
+ </script>
318
+
319
+ <div class="file-manager" on:contextmenu={handleEmptySpaceContextMenu}>
320
+ <h3>File Manager</h3>
321
+
322
+ <div class="file-list">
323
+ {#each files as file}
324
+ <div
325
+ class="file-item"
326
+ class:selected={file.selected}
327
+ on:contextmenu|stopPropagation={(e) => handleFileContextMenu(e, file)}
328
+ >
329
+ <span class="file-icon {file.type === 'folder' ? 'icon-folder' : 'icon-file'}"></span>
330
+ <span class="file-name">{file.name}</span>
331
+ </div>
332
+ {/each}
333
+ </div>
334
+ </div>
335
+
336
+ <ContextMenu bind:this={contextMenu} />
337
+
338
+ <style>
339
+ .file-manager {
340
+ padding: 20px;
341
+ border: 1px solid #ddd;
342
+ border-radius: 8px;
343
+ min-height: 300px;
344
+ background: #f9f9f9;
345
+ }
346
+
347
+ .file-list {
348
+ margin-top: 16px;
349
+ }
350
+
351
+ .file-item {
352
+ display: flex;
353
+ align-items: center;
354
+ padding: 8px;
355
+ margin: 4px 0;
356
+ border-radius: 4px;
357
+ cursor: pointer;
358
+ transition: background 0.2s;
359
+ }
360
+
361
+ .file-item:hover {
362
+ background: #e9e9e9;
363
+ }
364
+
365
+ .file-item.selected {
366
+ background: #007acc;
367
+ color: white;
368
+ }
369
+
370
+ .file-icon {
371
+ margin-right: 8px;
372
+ width: 16px;
373
+ height: 16px;
374
+ }
375
+ </style>
376
+ ```
377
+
378
+ ### Table Row Context Menu
379
+
380
+ ```svelte
381
+ <script>
382
+ import ContextMenu from '@ticatec/uniface-element/ContextMenu';
383
+
384
+ let contextMenu;
385
+ let users = [
386
+ { id: 1, name: 'John Doe', email: 'john@example.com', active: true },
387
+ { id: 2, name: 'Jane Smith', email: 'jane@example.com', active: false },
388
+ { id: 3, name: 'Bob Johnson', email: 'bob@example.com', active: true }
389
+ ];
390
+
391
+ const getUserMenuItems = (user) => [
392
+ { text: 'View Profile', icon: 'icon-user', action: () => viewProfile(user) },
393
+ { text: 'Edit User', icon: 'icon-edit', action: () => editUser(user) },
394
+ { separator: true },
395
+ {
396
+ text: user.active ? 'Deactivate' : 'Activate',
397
+ icon: user.active ? 'icon-pause' : 'icon-play',
398
+ action: () => toggleUserStatus(user)
399
+ },
400
+ { text: 'Reset Password', action: () => resetPassword(user) },
401
+ { separator: true },
402
+ { text: 'Delete User', icon: 'icon-trash', action: () => deleteUser(user) }
403
+ ];
404
+
405
+ const handleRowContextMenu = (event, user) => {
406
+ const items = getUserMenuItems(user);
407
+ contextMenu.show(event, items, 180, 200);
408
+ };
409
+
410
+ const viewProfile = (user) => console.log('Viewing profile for', user.name);
411
+ const editUser = (user) => console.log('Editing', user.name);
412
+ const toggleUserStatus = (user) => {
413
+ user.active = !user.active;
414
+ users = [...users];
415
+ console.log(`${user.active ? 'Activated' : 'Deactivated'} ${user.name}`);
416
+ };
417
+ const resetPassword = (user) => console.log('Resetting password for', user.name);
418
+ const deleteUser = (user) => {
419
+ users = users.filter(u => u.id !== user.id);
420
+ console.log('Deleted', user.name);
421
+ };
422
+ </script>
423
+
424
+ <div class="user-table">
425
+ <h3>User Management</h3>
426
+
427
+ <table>
428
+ <thead>
429
+ <tr>
430
+ <th>Name</th>
431
+ <th>Email</th>
432
+ <th>Status</th>
433
+ </tr>
434
+ </thead>
435
+ <tbody>
436
+ {#each users as user}
437
+ <tr on:contextmenu={(e) => handleRowContextMenu(e, user)}>
438
+ <td>{user.name}</td>
439
+ <td>{user.email}</td>
440
+ <td>
441
+ <span class="status" class:active={user.active}>
442
+ {user.active ? 'Active' : 'Inactive'}
443
+ </span>
444
+ </td>
445
+ </tr>
446
+ {/each}
447
+ </tbody>
448
+ </table>
449
+ </div>
450
+
451
+ <ContextMenu bind:this={contextMenu} />
452
+
453
+ <style>
454
+ .user-table {
455
+ padding: 20px;
456
+ }
457
+
458
+ table {
459
+ width: 100%;
460
+ border-collapse: collapse;
461
+ margin-top: 16px;
462
+ }
463
+
464
+ th, td {
465
+ text-align: left;
466
+ padding: 12px;
467
+ border-bottom: 1px solid #ddd;
468
+ }
469
+
470
+ th {
471
+ background: #f5f5f5;
472
+ font-weight: 600;
473
+ }
474
+
475
+ tr:hover {
476
+ background: #f9f9f9;
477
+ cursor: context-menu;
478
+ }
479
+
480
+ .status {
481
+ padding: 4px 8px;
482
+ border-radius: 12px;
483
+ font-size: 12px;
484
+ font-weight: 500;
485
+ background: #dc3545;
486
+ color: white;
487
+ }
488
+
489
+ .status.active {
490
+ background: #28a745;
491
+ }
492
+ </style>
493
+ ```
494
+
495
+ ## Keyboard Navigation
496
+
497
+ The ContextMenu supports full keyboard navigation:
498
+
499
+ - **Arrow Up/Down**: Navigate between menu items
500
+ - **Enter/Space**: Execute the focused menu item
501
+ - **Escape**: Close the menu
502
+ - **Tab**: Close the menu (focus moves away)
503
+
504
+ ## Accessibility
505
+
506
+ The ContextMenu component includes comprehensive accessibility features:
507
+
508
+ - Proper ARIA roles (`menu`, `menuitem`, `separator`)
509
+ - ARIA attributes (`aria-label`, `aria-disabled`)
510
+ - Keyboard navigation support
511
+ - Focus management
512
+ - Screen reader compatibility
513
+ - Semantic HTML structure
514
+
515
+ ### Best Practices
516
+
517
+ ```svelte
518
+ <!-- Good: Clear context indication -->
519
+ <div class="editable-content" on:contextmenu={showEditMenu}>
520
+ <!-- content -->
521
+ </div>
522
+
523
+ <!-- Good: Appropriate menu items for context -->
524
+ const editMenuItems = [
525
+ { text: 'Cut', action: () => cut() },
526
+ { text: 'Copy', action: () => copy() },
527
+ { text: 'Paste', action: () => paste(), disabled: !canPaste }
528
+ ];
529
+
530
+ <!-- Good: Visual feedback for disabled items -->
531
+ .menu-item.disabled {
532
+ opacity: 0.5;
533
+ cursor: not-allowed;
534
+ }
535
+ ```
536
+
537
+ ## Browser Support
538
+
539
+ Works in all modern browsers that support:
540
+ - ES6+ JavaScript
541
+ - Modern DOM APIs
542
+ - CSS custom properties
543
+ - Portal/Teleport functionality
544
+
545
+ ## Performance Considerations
546
+
547
+ - Efficient event handling with proper cleanup
548
+ - Portal rendering for optimal layering
549
+ - Minimal DOM updates with reactive changes
550
+ - Smart positioning calculations
551
+ - Automatic cleanup on component destroy