intelliwaketssveltekitv25 1.0.95 → 1.0.98

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.
@@ -0,0 +1,557 @@
1
+ # MasterDetailLayout Component
2
+
3
+ **Purpose:** Responsive master-detail pattern with automatic routing integration and breakpoint-based mobile/desktop views
4
+
5
+ **When to Use:**
6
+ - List-detail interfaces (users list → user detail, products → product detail)
7
+ - Navigation sidebars with dynamic content areas
8
+ - Inbox/message viewers, file browsers, or any hierarchical data view
9
+ - When you need automatic mobile/tablet responsive behavior
10
+ - Master list needs to hide on mobile when detail is shown
11
+
12
+ ## Key Features
13
+
14
+ - **Automatic Mobile Behavior:** Master list hides when detail opens on smaller screens
15
+ - **PathAnalyzer Integration:** Automatically syncs with SvelteKit routing
16
+ - **Breakpoint Control:** Choose when layout switches from mobile to desktop view
17
+ - **ListGroupItems Integration:** Built-in list navigation with data attributes for testing
18
+ - **Hierarchical Support:** Parent-child relationships in list items
19
+ - **Snippet-Based:** Flexible content slots for header, list, footer, and detail areas
20
+
21
+ ## Key Props
22
+
23
+ ### Required Props
24
+
25
+ - `pageRoute: string` - Base route for navigation (e.g., 'Users', 'Products'). Used with PathAnalyzer for routing.
26
+
27
+ ### Optional Props
28
+
29
+ - `breakAt?: TBreakAt` (default: 'md') - Responsive breakpoint: 'sm' | 'md' | 'lg' | 'xl' | '2xl'
30
+ - `backName?: string | null` - Text for back navigation button on mobile
31
+ - `listItems?: TMasterDetailListGroupItem[] | null` - Array of items for the list
32
+ - `caret?: boolean | 'left' | 'right'` - Show selection indicator arrow
33
+ - `emptyListMessage?: string | null` - Message when list is empty
34
+ - `active?: TFindIsActive` - Filter items: `true` (non-hidden), `false` (hidden), `null` (all)
35
+ - `useValueInHref?: boolean` (default: false) - Append `:value` to generated hrefs
36
+
37
+ ### Styling Props
38
+
39
+ - `mdClass?: string` - CSS classes for root container
40
+ - `masterClass?: string` - CSS classes for master (list) section
41
+ - `detailClass?: string` - CSS classes for detail section
42
+ - `wrapText?: boolean` (default: false) - Allow list item text to wrap
43
+ - `ellipses?: boolean` (default: false) - Truncate long text with ellipses
44
+ - `borders?: boolean` (default: false) - Show borders between list items
45
+ - `rounded?: boolean` (default: false) - Use rounded card style
46
+ - `roundedDetailFormatted?: boolean` (default: true) - Apply rounded style to detail when `rounded` is true
47
+ - `roundedDetailFormattedHome?: boolean` - Override rounded detail style for home/no-selection view
48
+
49
+ ### Detail Header Props
50
+
51
+ - `detailShowHeader?: boolean | string` (default: false) - Show header in detail area (true = item title, string = custom text)
52
+ - `detailShowHeaderFAProps?: IFAProps | null` - FontAwesome icon props for detail header
53
+ - `noLinkReplace?: boolean` (default: false) - Disable automatic link conversion in DisplayHTML
54
+
55
+ ## Snippets
56
+
57
+ - `empty?: Snippet` - Content when list is empty
58
+ - `header?: Snippet` - Master section header (typically title or search)
59
+ - `subheader?: Snippet` - Additional header content below main header
60
+ - `list?: Snippet` - Custom list implementation (replaces default ListGroupItems)
61
+ - `footer?: Snippet` - Master section footer
62
+ - `detail?: Snippet` - Detail/content area (right side on desktop, full screen on mobile)
63
+
64
+ ## TMasterDetailListGroupItem Type
65
+
66
+ Extends `TListGroupItem` with additional properties:
67
+
68
+ ```typescript
69
+ type TMasterDetailListGroupItem = Omit<TListGroupItem, 'subs'> & {
70
+ subs?: TMasterDetailListGroupItem[] // Child items
71
+ roundedDetailFormatted?: boolean // Override rounded style for this item's detail
72
+ detailShowHeader?: boolean | string // Override header visibility for this item
73
+ }
74
+ ```
75
+
76
+ ### Common TListGroupItem Properties
77
+
78
+ - `key?: string` - Custom key for testing (recommended)
79
+ - `value?: string` - Item identifier (used for routing and hierarchy)
80
+ - `title?: string | Snippet` - Primary text
81
+ - `sub_title?: string | Snippet` - Secondary text
82
+ - `section?: string` - Section header grouping
83
+ - `href?: string` - Custom navigation path
84
+ - `parent_value?: string` - Parent item value (creates hierarchy)
85
+ - `selected?: boolean` - Item is selected
86
+ - `disabled?: boolean` - Item is disabled
87
+ - `hidden?: boolean` - Item is hidden (respects `active` prop)
88
+ - `faProps?: IFAProps` - FontAwesome icon
89
+ - `icon?: IconDefinition` - FontAwesome icon (alternative)
90
+ - `imageHref?: string` - Image URL
91
+ - `badgeValue?: string | number` - Badge content
92
+ - `badgeColor?: string` - Badge color theme
93
+ - `rightText?: string` - Right-aligned text
94
+ - `linkClick?: () => void` - Custom click handler (overrides href)
95
+ - `hover_title?: string` - Tooltip text
96
+
97
+ ## Usage Examples
98
+
99
+ ### Basic Master-Detail
100
+
101
+ ```svelte
102
+ <script>
103
+ import { MasterDetailLayout } from 'intelliwaketssveltekitv25';
104
+
105
+ const users = [
106
+ { value: '1', title: 'Alice Johnson', href: 'User/1' },
107
+ { value: '2', title: 'Bob Smith', href: 'User/2' },
108
+ { value: '3', title: 'Carol White', href: 'User/3' }
109
+ ];
110
+ </script>
111
+
112
+ <MasterDetailLayout
113
+ pageRoute="Users"
114
+ listItems={users}
115
+ backName="Users"
116
+ >
117
+ {#snippet header()}
118
+ <h1 class="text-center p-2">Users</h1>
119
+ {/snippet}
120
+
121
+ {#snippet detail()}
122
+ <slot /> <!-- Child route renders here -->
123
+ {/snippet}
124
+ </MasterDetailLayout>
125
+ ```
126
+
127
+ ### With Search and Hierarchical Items
128
+
129
+ ```svelte
130
+ <script>
131
+ import { MasterDetailLayout, Search } from 'intelliwaketssveltekitv25';
132
+ import { SearchRows } from '@solidbasisventures/intelliwaketsfoundation';
133
+
134
+ let search = $state('');
135
+
136
+ const allItems = [
137
+ { value: '1', title: 'Parent Item', href: 'Item/1' },
138
+ { value: '2', title: 'Child Item A', parent_value: '1', href: 'Item/2' },
139
+ { value: '3', title: 'Child Item B', parent_value: '1', href: 'Item/3' }
140
+ ];
141
+
142
+ let filteredItems = $derived(SearchRows(allItems, search));
143
+ </script>
144
+
145
+ <MasterDetailLayout
146
+ pageRoute="Items"
147
+ listItems={filteredItems}
148
+ breakAt="lg"
149
+ rounded
150
+ caret="right"
151
+ >
152
+ {#snippet header()}
153
+ <div class="p-2">
154
+ <h1 class="text-center">Items</h1>
155
+ <Search bind:value={search} placeholder="Search items..." />
156
+ </div>
157
+ {/snippet}
158
+
159
+ {#snippet detail()}
160
+ <slot />
161
+ {/snippet}
162
+ </MasterDetailLayout>
163
+ ```
164
+
165
+ ### With Sections and Custom Keys
166
+
167
+ ```svelte
168
+ <script>
169
+ import { MasterDetailLayout } from 'intelliwaketssveltekitv25';
170
+
171
+ const products = [
172
+ {
173
+ key: 'electronics-laptop',
174
+ value: 'laptop',
175
+ title: 'Laptop',
176
+ section: 'Electronics',
177
+ badgeValue: 5,
178
+ href: 'Product/laptop'
179
+ },
180
+ {
181
+ key: 'electronics-phone',
182
+ value: 'phone',
183
+ title: 'Phone',
184
+ section: 'Electronics',
185
+ badgeValue: 12,
186
+ href: 'Product/phone'
187
+ },
188
+ {
189
+ key: 'furniture-desk',
190
+ value: 'desk',
191
+ title: 'Desk',
192
+ section: 'Furniture',
193
+ rightText: 'In Stock',
194
+ href: 'Product/desk'
195
+ }
196
+ ];
197
+ </script>
198
+
199
+ <MasterDetailLayout
200
+ pageRoute="Products"
201
+ listItems={products}
202
+ detailShowHeader={true}
203
+ borders
204
+ >
205
+ {#snippet header()}
206
+ <h1 class="text-center p-2">Products</h1>
207
+ {/snippet}
208
+
209
+ {#snippet footer()}
210
+ <div class="p-2 text-sm text-gray-500">
211
+ {products.length} products
212
+ </div>
213
+ {/snippet}
214
+
215
+ {#snippet detail()}
216
+ <slot />
217
+ {/snippet}
218
+ </MasterDetailLayout>
219
+ ```
220
+
221
+ ---
222
+
223
+ ## PathAnalyzer Integration
224
+
225
+ MasterDetailLayout automatically creates a `PathAnalyzer` instance to manage navigation state. The PathAnalyzer:
226
+
227
+ - Parses the current SvelteKit route
228
+ - Determines which list item is "open" based on the URL
229
+ - Generates navigation paths for list items
230
+ - Provides `isOpen()` method to check if a path is active
231
+
232
+ ### How Item Paths Are Generated
233
+
234
+ If an item doesn't have an explicit `href`, the path is generated:
235
+
236
+ ```typescript
237
+ // Without useValueInHref
238
+ ToPascalCase(item.title) // "User Settings" → "UserSettings"
239
+
240
+ // With useValueInHref={true}
241
+ ToPascalCase(item.title) + ':' + item.value // "User Settings" → "UserSettings:123"
242
+ ```
243
+
244
+ **Example Routes:**
245
+ - `pageRoute="Users"` + item `{ title: "Alice", value: "1" }` → `/Users/Alice` (or `/Users/Alice:1` with useValueInHref)
246
+ - `pageRoute="Products"` + item `{ href: "Item/special-123" }` → `/Products/Item/special-123`
247
+
248
+ ---
249
+
250
+ ## Responsive Behavior
251
+
252
+ The `breakAt` prop controls when the layout switches from mobile to desktop:
253
+
254
+ | breakAt | Mobile (stacked) | Desktop (side-by-side) |
255
+ |---------|------------------|------------------------|
256
+ | `sm` | < 640px | ≥ 640px |
257
+ | `md` | < 768px | ≥ 768px (default) |
258
+ | `lg` | < 1024px | ≥ 1024px |
259
+ | `xl` | < 1280px | ≥ 1280px |
260
+ | `2xl` | < 1536px | ≥ 1536px |
261
+
262
+ **Mobile Behavior:**
263
+ - Master list visible when no item selected (URL at base route)
264
+ - Detail area visible when item selected (URL includes item path)
265
+ - Master list hidden when detail shown
266
+ - "Back" button appears (using `backName` prop) via BreakAtManager
267
+
268
+ **Desktop Behavior:**
269
+ - Master and detail visible side-by-side
270
+ - Master list always visible
271
+ - Detail updates without hiding master
272
+
273
+ ---
274
+
275
+ ## Testing with Playwright
276
+
277
+ Since MasterDetailLayout uses `ListGroupItems` internally, you can use the same testing patterns documented in [ListGroupItems Testing Guide](ListGroupItems).
278
+
279
+ ### Locating List Items
280
+
281
+ MasterDetailLayout automatically adds `data-listgroupitem-key` attributes to all list items:
282
+
283
+ ```typescript
284
+ import { test, expect } from '@playwright/test';
285
+ import { ListGroupItemKeyCalc } from 'intelliwaketssveltekitv25';
286
+
287
+ test('navigate to user detail', async ({ page }) => {
288
+ await page.goto('/Users');
289
+
290
+ // Using custom key
291
+ await page.locator('[data-listgroupitem-key="user-alice"]').click();
292
+
293
+ // Or using ListGroupItemKeyCalc for auto-generated keys
294
+ const userItem = { value: '1', title: 'Alice Johnson' };
295
+ const key = ListGroupItemKeyCalc(userItem);
296
+ await page.locator(`[data-listgroupitem-key="${key}"]`).click();
297
+
298
+ // Verify navigation
299
+ await expect(page).toHaveURL(/.*Alice/);
300
+ });
301
+ ```
302
+
303
+ ### Testing Master-Detail Navigation
304
+
305
+ ```typescript
306
+ test('master-detail responsive behavior', async ({ page }) => {
307
+ await page.goto('/Products');
308
+
309
+ // Desktop: Both master and detail visible
310
+ await page.setViewportSize({ width: 1024, height: 768 });
311
+
312
+ await page.locator('[data-listgroupitem-key="electronics-laptop"]').click();
313
+
314
+ await expect(page.locator('.masterDetailMaster')).toBeVisible();
315
+ await expect(page.locator('.masterDetailDetail')).toBeVisible();
316
+
317
+ // Mobile: Only detail visible when item open
318
+ await page.setViewportSize({ width: 375, height: 667 });
319
+
320
+ await expect(page.locator('.masterDetailMaster')).toBeHidden();
321
+ await expect(page.locator('.masterDetailDetail')).toBeVisible();
322
+ });
323
+ ```
324
+
325
+ ### Testing List Item Selection
326
+
327
+ ```typescript
328
+ test('verify selected item styling', async ({ page }) => {
329
+ await page.goto('/Users/Alice');
330
+
331
+ // Find selected item
332
+ const selectedItem = page.locator('.listGroupItem.selected');
333
+ await expect(selectedItem).toBeVisible();
334
+
335
+ // Verify correct item is selected
336
+ await expect(selectedItem).toHaveAttribute('data-listgroupitem-key', 'user-alice');
337
+
338
+ // Selection indicator should be visible
339
+ await expect(page.locator('.list_group_indicator')).toBeVisible();
340
+ });
341
+ ```
342
+
343
+ ### Testing Hierarchical Items
344
+
345
+ ```typescript
346
+ test('expand and navigate to child item', async ({ page }) => {
347
+ await page.goto('/Items');
348
+
349
+ // Parent item with children
350
+ const parentItem = page.locator('[data-listgroupitem-key="parent-item"]');
351
+
352
+ // Click expand toggle (first button in the item)
353
+ await parentItem.locator('div[role="button"]').first().click();
354
+
355
+ // Child item becomes visible
356
+ const childItem = page.locator('[data-listgroupitem-key="child-item-a"]');
357
+ await expect(childItem).toBeVisible();
358
+
359
+ // Navigate to child
360
+ await childItem.locator('a').click();
361
+ await expect(page).toHaveURL(/.*ChildItemA/);
362
+ });
363
+ ```
364
+
365
+ ### Testing with Custom Keys (Recommended)
366
+
367
+ For more reliable testing, always provide custom `key` properties:
368
+
369
+ ```typescript
370
+ // In your component
371
+ const listItems = [
372
+ { key: 'user-alice', value: '1', title: 'Alice', href: 'User/1' },
373
+ { key: 'user-bob', value: '2', title: 'Bob', href: 'User/2' }
374
+ ];
375
+
376
+ // In your test
377
+ await page.locator('[data-listgroupitem-key="user-alice"]').click();
378
+ ```
379
+
380
+ ### Testing Empty State
381
+
382
+ ```typescript
383
+ test('display empty message', async ({ page }) => {
384
+ await page.goto('/Products');
385
+
386
+ // When no items
387
+ const emptyMessage = page.locator('.listEmpty');
388
+ await expect(emptyMessage).toContainText('No products available');
389
+ });
390
+ ```
391
+
392
+ ---
393
+
394
+ ## Advanced Patterns
395
+
396
+ ### Filter by Hidden State
397
+
398
+ Control which items are visible using the `active` prop:
399
+
400
+ ```typescript
401
+ const items = [
402
+ { value: '1', title: 'Active User', hidden: false },
403
+ { value: '2', title: 'Inactive User', hidden: true },
404
+ { value: '3', title: 'Archived User', hidden: true }
405
+ ];
406
+
407
+ // Show only active items (hidden: false)
408
+ <MasterDetailLayout listItems={items} active={true} />
409
+
410
+ // Show only hidden items (hidden: true)
411
+ <MasterDetailLayout listItems={items} active={false} />
412
+
413
+ // Show all items regardless of hidden property
414
+ <MasterDetailLayout listItems={items} active={null} />
415
+ ```
416
+
417
+ ### Dynamic Detail Headers
418
+
419
+ Show item-specific headers in the detail area:
420
+
421
+ ```typescript
422
+ const items = [
423
+ {
424
+ value: '1',
425
+ title: 'Dashboard',
426
+ detailShowHeader: 'Overview Dashboard',
427
+ faProps: { icon: 'chart-line' }
428
+ },
429
+ {
430
+ value: '2',
431
+ title: 'Settings',
432
+ detailShowHeader: true, // Uses item title
433
+ faProps: { icon: 'cog' }
434
+ },
435
+ {
436
+ value: '3',
437
+ title: 'Profile',
438
+ detailShowHeader: false // No header
439
+ }
440
+ ];
441
+
442
+ <MasterDetailLayout
443
+ listItems={items}
444
+ detailShowHeader={false} // Default for all items
445
+ detailShowHeaderFAProps={{ icon: 'info-circle' }} // Default icon
446
+ />
447
+ ```
448
+
449
+ ### Custom List Implementation
450
+
451
+ Replace the default ListGroupItems with your own:
452
+
453
+ ```svelte
454
+ <MasterDetailLayout pageRoute="Custom">
455
+ {#snippet list()}
456
+ <ul class="custom-list">
457
+ {#each items as item}
458
+ <li><a href={item.href}>{item.title}</a></li>
459
+ {/each}
460
+ </ul>
461
+ {/snippet}
462
+
463
+ {#snippet detail()}
464
+ <slot />
465
+ {/snippet}
466
+ </MasterDetailLayout>
467
+ ```
468
+
469
+ ---
470
+
471
+ ## Common Issues & Solutions
472
+
473
+ ### Master List Not Showing on Desktop
474
+
475
+ **Problem:** Master list is hidden even on large screens.
476
+
477
+ **Solution:** Check that an item is not selected (URL is at base route). The master hides when `pathAnalyzer.activePageSlug` exists.
478
+
479
+ ### Item Not Getting Selected
480
+
481
+ **Problem:** Clicking an item doesn't show selection styling.
482
+
483
+ **Solution:** Ensure the `href` matches the actual route structure. Use PathAnalyzer's `isOpen()` logic by matching your route params.
484
+
485
+ ### Links Not Clickable
486
+
487
+ **Problem:** List items don't navigate when clicked.
488
+
489
+ **Solution:**
490
+ 1. Ensure `href` is set on items, or title can be converted to a path
491
+ 2. Check that `linkClick` isn't set (it overrides href navigation)
492
+ 3. Verify items aren't `disabled`
493
+
494
+ ### Children Not Expanding
495
+
496
+ **Problem:** Clicking expand icon doesn't show child items.
497
+
498
+ **Solution:**
499
+ 1. Ensure child items have `parent_value` matching parent's `value`
500
+ 2. Check that `subsExist` is true or items have `subs` array
501
+ 3. Verify children aren't filtered out by `active` prop
502
+
503
+ ---
504
+
505
+ ## Styling Customization
506
+
507
+ ### CSS Classes Used
508
+
509
+ | Class | Element | Purpose |
510
+ |-------|---------|---------|
511
+ | `.masterDetail` | Root container | Grid layout container |
512
+ | `.masterDetailMaster` | Master section | Left sidebar with list |
513
+ | `.masterDetailDetail` | Detail section | Right content area |
514
+ | `.masterDetailDetailWithHeader` | Detail section | Applied when header is shown |
515
+ | `.listGroupItem` | List items | Individual list entries |
516
+ | `.selected` | Active list item | Currently selected item |
517
+ | `.list_group_indicator` | Selection indicator | Animated selection highlight |
518
+
519
+ ### Custom Styling Examples
520
+
521
+ ```svelte
522
+ <!-- Wider master section -->
523
+ <MasterDetailLayout
524
+ mdClass="md:grid-cols-[400px_1fr]"
525
+ pageRoute="Users"
526
+ {listItems}
527
+ />
528
+
529
+ <!-- Custom colors -->
530
+ <MasterDetailLayout
531
+ masterClass="bg-blue-50 dark:bg-blue-900"
532
+ detailClass="bg-white dark:bg-slate-800"
533
+ pageRoute="Products"
534
+ {listItems}
535
+ />
536
+
537
+ <!-- Compact rounded style -->
538
+ <MasterDetailLayout
539
+ rounded
540
+ mdClass="p-1 gap-1"
541
+ pageRoute="Items"
542
+ {listItems}
543
+ />
544
+ ```
545
+
546
+ ---
547
+
548
+ ## Related Components
549
+
550
+ - **[ListGroupItems](ListGroupItems)** - Testing guide for list item data attributes
551
+ - **[PathAnalyzer](Functions#pathanalyzer)** - URL parsing and navigation helper
552
+ - **[Search](Search)** - Integrate search with filtered list items
553
+ - **[TabHref](TabHref)** - Alternative navigation pattern
554
+
555
+ ---
556
+
557
+ For more details on testing list items with Playwright, see the complete [ListGroupItems Testing Guide](ListGroupItems).
package/docs/_Sidebar.md CHANGED
@@ -17,6 +17,7 @@
17
17
 
18
18
  ### Layout & Overlays
19
19
  - [Modal](Modal)
20
+ - [MasterDetailLayout](MasterDetailLayout)
20
21
  - [TabHeader](TabHeader)
21
22
  - [TabHref](TabHref)
22
23
  - [SlideDown](SlideDown)
@@ -35,4 +36,7 @@
35
36
  - [ImporterLoad](ImporterLoad)
36
37
  - [ImporterAnalysis](ImporterAnalysis)
37
38
 
39
+ ### Testing
40
+ - [ListGroupItems](ListGroupItems)
41
+
38
42
  ---
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "intelliwaketssveltekitv25",
3
- "version": "1.0.95",
3
+ "version": "1.0.98",
4
4
  "private": false,
5
5
  "license": "MIT",
6
6
  "exports": {