@design.estate/dees-catalog 3.28.1 β 3.29.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.
- package/dist_bundle/bundle.js +2 -1
- package/dist_bundle/bundle.js.map +2 -2
- package/dist_ts_web/00_commitinfo_data.js +1 -1
- package/dist_ts_web/elements/00group-appui/dees-appui/dees-appui.js +2 -1
- package/package.json +1 -1
- package/readme.md +155 -4
- package/ts_web/00_commitinfo_data.ts +1 -1
- package/ts_web/elements/00group-appui/dees-appui/dees-appui.ts +1 -0
- package/ts_web/elements/00group-appui/dees-appui/readme.md +293 -30
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# DeesAppui
|
|
2
2
|
|
|
3
|
-
A comprehensive application shell component providing a complete UI framework with navigation, menus, activity logging, and view management.
|
|
3
|
+
A comprehensive application shell component providing a complete UI framework with navigation, menus, activity logging, and view management. π
|
|
4
4
|
|
|
5
5
|
## Quick Start
|
|
6
6
|
|
|
@@ -35,6 +35,34 @@ class MyApp extends DeesElement {
|
|
|
35
35
|
}
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
+
## Architecture Overview
|
|
39
|
+
|
|
40
|
+
The DeesAppui shell consists of several interconnected components:
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
44
|
+
β AppBar (dees-appui-appbar) β
|
|
45
|
+
β βββ Menus (File, Edit, View...) β
|
|
46
|
+
β βββ Breadcrumbs β
|
|
47
|
+
β βββ User Profile + Dropdown β
|
|
48
|
+
β βββ Activity Log Toggle β
|
|
49
|
+
βββββββββββββββ¬ββββββββββββββββββββββββββββββββββββ¬ββββββββββββββββββββ€
|
|
50
|
+
β Main Menu β Content Area β Activity Log β
|
|
51
|
+
β (collapsed/ β βββ Content Tabs β (slide panel) β
|
|
52
|
+
β expanded) β β (closable, from tables/lists)β β
|
|
53
|
+
β β βββ View Container β β
|
|
54
|
+
β βββββββββββ β βββ Active View β β
|
|
55
|
+
β β π Home β βββββββββββββββββββββββββββββββββββ β β
|
|
56
|
+
β β π Filesβ β Secondary Menu β β β
|
|
57
|
+
β β β Settings βββ Collapsible Groups β β β
|
|
58
|
+
β β β β βββ Item 1 β β β
|
|
59
|
+
β βββββββββββ β βββ Item 2 (with badge) β β β
|
|
60
|
+
β β βββ Item 3 β β β
|
|
61
|
+
βββββββββββββββ΄ββββββββββββββββββββββββββββββββββ΄ββββββββββββββββββββββββ
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
38
66
|
## Configuration API
|
|
39
67
|
|
|
40
68
|
### `configure(config: IAppConfig)`
|
|
@@ -155,74 +183,289 @@ appui.removeMainMenuItem('Main', 'tasks');
|
|
|
155
183
|
|
|
156
184
|
// Selection
|
|
157
185
|
appui.setMainMenuSelection('dashboard');
|
|
158
|
-
|
|
186
|
+
|
|
187
|
+
// Visibility control
|
|
188
|
+
appui.setMainMenuCollapsed(true); // Collapse to icon-only sidebar
|
|
189
|
+
appui.setMainMenuVisible(false); // Hide completely
|
|
159
190
|
|
|
160
191
|
// Badges
|
|
161
192
|
appui.setMainMenuBadge('inbox', 12);
|
|
162
193
|
appui.clearMainMenuBadge('inbox');
|
|
163
194
|
```
|
|
164
195
|
|
|
165
|
-
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## Secondary Menu API π
|
|
199
|
+
|
|
200
|
+
The secondary menu is a contextual sidebar that appears next to the main content area. It supports **collapsible groups** with icons and badges, making it perfect for:
|
|
166
201
|
|
|
167
|
-
|
|
202
|
+
- **Settings pages** (grouped settings categories)
|
|
203
|
+
- **File browsers** (folder trees)
|
|
204
|
+
- **Project navigation** (grouped by category)
|
|
205
|
+
- **Documentation** (chapters/sections)
|
|
206
|
+
|
|
207
|
+
### Collapsible Groups
|
|
208
|
+
|
|
209
|
+
Groups can be collapsed/expanded by clicking the group header. The state is visually indicated with an icon rotation.
|
|
168
210
|
|
|
169
211
|
```typescript
|
|
170
|
-
// Set menu
|
|
212
|
+
// Set secondary menu with collapsible groups
|
|
171
213
|
appui.setSecondaryMenu({
|
|
172
214
|
heading: 'Settings',
|
|
173
215
|
groups: [
|
|
174
216
|
{
|
|
175
217
|
name: 'Account',
|
|
218
|
+
iconName: 'lucide:user', // Group icon
|
|
219
|
+
collapsed: false, // Initial state (default: false)
|
|
176
220
|
items: [
|
|
177
|
-
{ key: 'profile', iconName: 'lucide:user', action: () =>
|
|
178
|
-
{ key: 'security', iconName: 'lucide:shield', action: () =>
|
|
221
|
+
{ key: 'profile', iconName: 'lucide:user', action: () => showProfile() },
|
|
222
|
+
{ key: 'security', iconName: 'lucide:shield', badge: '!', badgeVariant: 'warning', action: () => showSecurity() },
|
|
223
|
+
{ key: 'billing', iconName: 'lucide:credit-card', action: () => showBilling() }
|
|
224
|
+
]
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
name: 'Preferences',
|
|
228
|
+
iconName: 'lucide:settings',
|
|
229
|
+
collapsed: true, // Start collapsed
|
|
230
|
+
items: [
|
|
231
|
+
{ key: 'notifications', iconName: 'lucide:bell', action: () => {} },
|
|
232
|
+
{ key: 'appearance', iconName: 'lucide:palette', action: () => {} },
|
|
233
|
+
{ key: 'language', iconName: 'lucide:globe', action: () => {} }
|
|
179
234
|
]
|
|
180
235
|
}
|
|
181
236
|
]
|
|
182
237
|
});
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### Secondary Menu Item Properties
|
|
241
|
+
|
|
242
|
+
```typescript
|
|
243
|
+
interface ISecondaryMenuItem {
|
|
244
|
+
key: string; // Unique identifier
|
|
245
|
+
iconName?: string; // Icon (e.g., 'lucide:user')
|
|
246
|
+
action: () => void; // Click handler
|
|
247
|
+
badge?: string | number; // Badge text/count
|
|
248
|
+
badgeVariant?: 'default' | 'success' | 'warning' | 'error';
|
|
249
|
+
}
|
|
183
250
|
|
|
184
|
-
|
|
185
|
-
|
|
251
|
+
interface ISecondaryMenuGroup {
|
|
252
|
+
name: string; // Group name (shown in header)
|
|
253
|
+
iconName?: string; // Group icon
|
|
254
|
+
collapsed?: boolean; // Initial collapsed state
|
|
255
|
+
items: ISecondaryMenuItem[]; // Items in this group
|
|
256
|
+
}
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### Updating Secondary Menu
|
|
260
|
+
|
|
261
|
+
```typescript
|
|
262
|
+
// Update a specific group
|
|
263
|
+
appui.updateSecondaryMenuGroup('Account', {
|
|
264
|
+
items: [...newItems]
|
|
265
|
+
});
|
|
186
266
|
|
|
187
|
-
// Add item
|
|
267
|
+
// Add item to a group
|
|
188
268
|
appui.addSecondaryMenuItem('Account', {
|
|
189
|
-
key: '
|
|
190
|
-
iconName: 'lucide:
|
|
191
|
-
action: () =>
|
|
269
|
+
key: 'api-keys',
|
|
270
|
+
iconName: 'lucide:key',
|
|
271
|
+
action: () => showApiKeys()
|
|
192
272
|
});
|
|
193
273
|
|
|
194
|
-
// Selection
|
|
274
|
+
// Selection (highlights the item)
|
|
195
275
|
appui.setSecondaryMenuSelection('profile');
|
|
196
276
|
|
|
277
|
+
// Visibility control
|
|
278
|
+
appui.setSecondaryMenuCollapsed(true); // Collapse panel
|
|
279
|
+
appui.setSecondaryMenuVisible(false); // Hide completely
|
|
280
|
+
|
|
197
281
|
// Clear
|
|
198
282
|
appui.clearSecondaryMenu();
|
|
199
283
|
```
|
|
200
284
|
|
|
201
|
-
###
|
|
285
|
+
### View-Specific Secondary Menus
|
|
286
|
+
|
|
287
|
+
Each view can define its own secondary menu that appears when the view is activated:
|
|
288
|
+
|
|
289
|
+
```typescript
|
|
290
|
+
// In view definition
|
|
291
|
+
{
|
|
292
|
+
id: 'settings',
|
|
293
|
+
name: 'Settings',
|
|
294
|
+
content: 'my-settings-view',
|
|
295
|
+
secondaryMenu: [
|
|
296
|
+
{
|
|
297
|
+
name: 'General',
|
|
298
|
+
items: [
|
|
299
|
+
{ key: 'account', iconName: 'lucide:user', action: () => {} },
|
|
300
|
+
{ key: 'security', iconName: 'lucide:shield', action: () => {} }
|
|
301
|
+
]
|
|
302
|
+
}
|
|
303
|
+
]
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// Or set dynamically in view's onActivate hook
|
|
307
|
+
onActivate(context: IViewActivationContext) {
|
|
308
|
+
context.appui.setSecondaryMenu({
|
|
309
|
+
heading: 'Project Files',
|
|
310
|
+
groups: [...]
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
---
|
|
316
|
+
|
|
317
|
+
## Content Tabs API π
|
|
318
|
+
|
|
319
|
+
Content tabs appear above the main view content. They're designed for **opening multiple items** from tables, lists, or other data sourcesβsimilar to browser tabs or IDE editor tabs.
|
|
202
320
|
|
|
203
|
-
|
|
321
|
+
### Common Use Cases
|
|
322
|
+
|
|
323
|
+
- **Table row details** - Click a row to open it as a tab
|
|
324
|
+
- **Document editing** - Open multiple documents
|
|
325
|
+
- **Entity inspection** - View customer, order, product details
|
|
326
|
+
- **Multi-file editing** - Edit multiple configuration files
|
|
327
|
+
|
|
328
|
+
### Closable Tabs
|
|
329
|
+
|
|
330
|
+
Tabs can be closable, allowing users to open items, work with them, and close when done:
|
|
204
331
|
|
|
205
332
|
```typescript
|
|
206
|
-
// Set tabs
|
|
333
|
+
// Set initial tabs
|
|
207
334
|
appui.setContentTabs([
|
|
208
|
-
{ key: '
|
|
209
|
-
{ key: '
|
|
335
|
+
{ key: 'overview', iconName: 'lucide:home', action: () => showOverview() },
|
|
336
|
+
{ key: 'activity', iconName: 'lucide:activity', action: () => showActivity() }
|
|
210
337
|
]);
|
|
211
338
|
|
|
212
|
-
// Add
|
|
213
|
-
|
|
339
|
+
// Add a closable tab when user clicks a table row
|
|
340
|
+
table.addEventListener('row-click', (e) => {
|
|
341
|
+
const item = e.detail.item;
|
|
342
|
+
|
|
343
|
+
appui.addContentTab({
|
|
344
|
+
key: `item-${item.id}`,
|
|
345
|
+
label: item.name, // Display label
|
|
346
|
+
iconName: 'lucide:file',
|
|
347
|
+
closable: true, // Allow closing
|
|
348
|
+
action: () => showItemDetails(item)
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
// Select the new tab
|
|
352
|
+
appui.selectContentTab(`item-${item.id}`);
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
// Handle tab close
|
|
356
|
+
appui.addEventListener('tab-close', (e) => {
|
|
357
|
+
const tabKey = e.detail.key;
|
|
358
|
+
// Cleanup resources if needed
|
|
359
|
+
console.log(`Tab ${tabKey} closed`);
|
|
360
|
+
});
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
### Tab Management
|
|
364
|
+
|
|
365
|
+
```typescript
|
|
366
|
+
// Add/remove tabs
|
|
367
|
+
appui.addContentTab({
|
|
368
|
+
key: 'debug',
|
|
369
|
+
iconName: 'lucide:bug',
|
|
370
|
+
closable: true,
|
|
371
|
+
action: () => {}
|
|
372
|
+
});
|
|
214
373
|
appui.removeContentTab('debug');
|
|
215
374
|
|
|
216
|
-
// Select
|
|
375
|
+
// Select tab
|
|
217
376
|
appui.selectContentTab('preview');
|
|
218
377
|
|
|
219
|
-
// Get current
|
|
378
|
+
// Get current tab
|
|
220
379
|
const current = appui.getSelectedContentTab();
|
|
380
|
+
|
|
381
|
+
// Visibility control
|
|
382
|
+
appui.setContentTabsVisible(false); // Hide tab bar
|
|
383
|
+
|
|
384
|
+
// Auto-hide when only one tab
|
|
385
|
+
appui.setContentTabsAutoHide(true, 1); // Hide when β€ 1 tab
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
### Opening Items from Tables/Lists
|
|
389
|
+
|
|
390
|
+
A common pattern is opening table rows as closable tabs:
|
|
391
|
+
|
|
392
|
+
```typescript
|
|
393
|
+
@customElement('my-customers-view')
|
|
394
|
+
class MyCustomersView extends DeesElement {
|
|
395
|
+
private appui: DeesAppui;
|
|
396
|
+
|
|
397
|
+
onActivate(context: IViewActivationContext) {
|
|
398
|
+
this.appui = context.appui;
|
|
399
|
+
|
|
400
|
+
// Set base tabs
|
|
401
|
+
this.appui.setContentTabs([
|
|
402
|
+
{ key: 'list', label: 'All Customers', iconName: 'lucide:users', action: () => this.showList() }
|
|
403
|
+
]);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
render() {
|
|
407
|
+
return html`
|
|
408
|
+
<dees-table
|
|
409
|
+
.data=${this.customers}
|
|
410
|
+
@row-dblclick=${this.openCustomerTab}
|
|
411
|
+
></dees-table>
|
|
412
|
+
`;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
openCustomerTab(e: CustomEvent) {
|
|
416
|
+
const customer = e.detail.item;
|
|
417
|
+
const tabKey = `customer-${customer.id}`;
|
|
418
|
+
|
|
419
|
+
// Check if tab already exists
|
|
420
|
+
const existingTab = this.appui.getSelectedContentTab();
|
|
421
|
+
if (existingTab?.key === tabKey) {
|
|
422
|
+
return; // Already viewing this customer
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
// Add new closable tab
|
|
426
|
+
this.appui.addContentTab({
|
|
427
|
+
key: tabKey,
|
|
428
|
+
label: customer.name,
|
|
429
|
+
iconName: 'lucide:user',
|
|
430
|
+
closable: true,
|
|
431
|
+
action: () => this.showCustomerDetails(customer)
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
this.appui.selectContentTab(tabKey);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
showCustomerDetails(customer: Customer) {
|
|
438
|
+
// Render customer details
|
|
439
|
+
this.currentView = html`<customer-details .customer=${customer}></customer-details>`;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
showList() {
|
|
443
|
+
this.currentView = html`<dees-table ...></dees-table>`;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
221
446
|
```
|
|
222
447
|
|
|
223
|
-
|
|
448
|
+
---
|
|
449
|
+
|
|
450
|
+
## Activity Log API π
|
|
451
|
+
|
|
452
|
+
The activity log is a slide-out panel on the right side showing user actions and system events.
|
|
224
453
|
|
|
225
|
-
|
|
454
|
+
### Activity Log Toggle
|
|
455
|
+
|
|
456
|
+
The appbar includes a toggle button with a badge showing the entry count:
|
|
457
|
+
|
|
458
|
+
```typescript
|
|
459
|
+
// Control visibility
|
|
460
|
+
appui.setActivityLogVisible(true); // Show panel
|
|
461
|
+
appui.toggleActivityLog(); // Toggle state
|
|
462
|
+
const isVisible = appui.getActivityLogVisible();
|
|
463
|
+
|
|
464
|
+
// The toggle button automatically shows entry count
|
|
465
|
+
// Add entries and the badge updates automatically
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
### Adding Entries
|
|
226
469
|
|
|
227
470
|
```typescript
|
|
228
471
|
// Add single entry
|
|
@@ -234,19 +477,35 @@ appui.activityLog.add({
|
|
|
234
477
|
data: { invoiceId: '123' } // Optional metadata
|
|
235
478
|
});
|
|
236
479
|
|
|
237
|
-
// Add multiple
|
|
480
|
+
// Add multiple entries (e.g., from backend)
|
|
238
481
|
appui.activityLog.addMany([...entries]);
|
|
239
482
|
|
|
240
|
-
// Clear
|
|
483
|
+
// Clear all entries
|
|
241
484
|
appui.activityLog.clear();
|
|
242
485
|
|
|
243
|
-
// Query
|
|
486
|
+
// Query entries
|
|
244
487
|
const entries = appui.activityLog.getEntries();
|
|
245
488
|
const filtered = appui.activityLog.filter({ user: 'John', type: 'create' });
|
|
246
489
|
const searched = appui.activityLog.search('invoice');
|
|
247
490
|
```
|
|
248
491
|
|
|
249
|
-
###
|
|
492
|
+
### Activity Entry Types
|
|
493
|
+
|
|
494
|
+
Each type has a default icon that can be overridden:
|
|
495
|
+
|
|
496
|
+
| Type | Default Icon | Use Case |
|
|
497
|
+
|------|--------------|----------|
|
|
498
|
+
| `login` | `lucide:log-in` | User sign-in |
|
|
499
|
+
| `logout` | `lucide:log-out` | User sign-out |
|
|
500
|
+
| `view` | `lucide:eye` | Page/item viewed |
|
|
501
|
+
| `create` | `lucide:plus` | New item created |
|
|
502
|
+
| `update` | `lucide:pencil` | Item modified |
|
|
503
|
+
| `delete` | `lucide:trash` | Item deleted |
|
|
504
|
+
| `custom` | `lucide:activity` | Custom events |
|
|
505
|
+
|
|
506
|
+
---
|
|
507
|
+
|
|
508
|
+
## Navigation API
|
|
250
509
|
|
|
251
510
|
Navigate between views programmatically.
|
|
252
511
|
|
|
@@ -512,6 +771,7 @@ class CrmSettings extends DeesElement {
|
|
|
512
771
|
groups: [
|
|
513
772
|
{
|
|
514
773
|
name: 'Account',
|
|
774
|
+
iconName: 'lucide:user',
|
|
515
775
|
items: [
|
|
516
776
|
{ key: 'profile', iconName: 'lucide:user', action: () => this.showSection('profile') },
|
|
517
777
|
{ key: 'security', iconName: 'lucide:shield', action: () => this.showSection('security') }
|
|
@@ -519,6 +779,8 @@ class CrmSettings extends DeesElement {
|
|
|
519
779
|
},
|
|
520
780
|
{
|
|
521
781
|
name: 'Preferences',
|
|
782
|
+
iconName: 'lucide:settings',
|
|
783
|
+
collapsed: true,
|
|
522
784
|
items: [
|
|
523
785
|
{ key: 'notifications', iconName: 'lucide:bell', action: () => this.showSection('notifications') }
|
|
524
786
|
]
|
|
@@ -557,4 +819,5 @@ All interfaces are exported from `@design.estate/dees-catalog`:
|
|
|
557
819
|
- `IAppBarMenuItem` - App bar menu item
|
|
558
820
|
- `IMainMenuConfig` - Main menu configuration
|
|
559
821
|
- `ISecondaryMenuGroup` - Secondary menu group
|
|
560
|
-
- `
|
|
822
|
+
- `ISecondaryMenuItem` - Secondary menu item
|
|
823
|
+
- `IMenuItem` - Tab/menu item definition
|