@poodle64/ui 2026.8.13 → 2026.8.14
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/components/ui/document-detail/document-detail.svelte +21 -8
- package/dist/components/ui/library-browse/document-table.svelte +27 -8
- package/dist/components/ui/library-browse/library-browse.svelte +24 -1
- package/package.json +1 -1
- package/registry/component-map.json +1 -1
- package/registry/component-map.md +1 -1
|
@@ -69,17 +69,28 @@
|
|
|
69
69
|
<EmptyState title={notFoundTitle} description={notFoundDescription} />
|
|
70
70
|
{:else}
|
|
71
71
|
<DetailPanel eyebrow="Document" title={document.title} titleFace="display" footer={actions}>
|
|
72
|
-
|
|
72
|
+
<!-- min-w-0 on the grid AND on every cell and value below: a grid or
|
|
73
|
+
flex item defaults to min-width:auto, so it grows to its widest
|
|
74
|
+
child and `truncate` can never shrink it. A 64-character content
|
|
75
|
+
hash and a filesystem path therefore took this card to 1,124px
|
|
76
|
+
inside a 390px screen and pushed every VALUE off the side, leaving
|
|
77
|
+
the labels alone on it. -->
|
|
78
|
+
<div class="grid min-w-0 gap-4 sm:grid-cols-2">
|
|
73
79
|
{#if document.fields?.length}
|
|
74
|
-
<div>
|
|
80
|
+
<div class="min-w-0">
|
|
75
81
|
<h3 class="text-muted-foreground text-2xs mb-2 font-semibold tracking-wide uppercase">
|
|
76
82
|
Details
|
|
77
83
|
</h3>
|
|
78
84
|
<dl class="space-y-1 text-sm">
|
|
79
85
|
{#each document.fields as field (field.label)}
|
|
80
86
|
<div class="flex justify-between gap-4">
|
|
81
|
-
<dt class="text-muted-foreground">{field.label}</dt>
|
|
82
|
-
<dd
|
|
87
|
+
<dt class="text-muted-foreground shrink-0">{field.label}</dt>
|
|
88
|
+
<dd
|
|
89
|
+
class={field.mono
|
|
90
|
+
? 'min-w-0 truncate font-mono text-xs'
|
|
91
|
+
: 'min-w-0 truncate text-right'}
|
|
92
|
+
title={field.value}
|
|
93
|
+
>
|
|
83
94
|
{field.value}
|
|
84
95
|
</dd>
|
|
85
96
|
</div>
|
|
@@ -89,14 +100,16 @@
|
|
|
89
100
|
{/if}
|
|
90
101
|
|
|
91
102
|
{#if document.locations?.length}
|
|
92
|
-
<div>
|
|
103
|
+
<div class="min-w-0">
|
|
93
104
|
<h3 class="text-muted-foreground text-2xs mb-2 font-semibold tracking-wide uppercase">
|
|
94
105
|
Locations
|
|
95
106
|
</h3>
|
|
96
107
|
<ul class="space-y-1 text-sm">
|
|
97
108
|
{#each document.locations as location (location.path)}
|
|
98
109
|
<li class="flex items-center justify-between gap-2">
|
|
99
|
-
<span class="truncate font-mono text-xs"
|
|
110
|
+
<span class="min-w-0 truncate font-mono text-xs" title={location.path}
|
|
111
|
+
>{location.path}</span
|
|
112
|
+
>
|
|
100
113
|
<div class="flex shrink-0 items-center gap-1">
|
|
101
114
|
{#if location.primary}
|
|
102
115
|
<Badge variant="secondary">primary</Badge>
|
|
@@ -112,7 +125,7 @@
|
|
|
112
125
|
{/if}
|
|
113
126
|
|
|
114
127
|
{#if document.tags?.length}
|
|
115
|
-
<div>
|
|
128
|
+
<div class="min-w-0">
|
|
116
129
|
<h3 class="text-muted-foreground text-2xs mb-2 font-semibold tracking-wide uppercase">
|
|
117
130
|
Tags
|
|
118
131
|
</h3>
|
|
@@ -125,7 +138,7 @@
|
|
|
125
138
|
{/if}
|
|
126
139
|
|
|
127
140
|
{#if document.memberships?.length}
|
|
128
|
-
<div>
|
|
141
|
+
<div class="min-w-0">
|
|
129
142
|
<h3 class="text-muted-foreground text-2xs mb-2 font-semibold tracking-wide uppercase">
|
|
130
143
|
Collections
|
|
131
144
|
</h3>
|
|
@@ -4,6 +4,13 @@
|
|
|
4
4
|
// a consumer that passes no `collections` never sees an empty column —
|
|
5
5
|
// which is what lets one table serve both the whole-catalogue view and a
|
|
6
6
|
// single collection's slice.
|
|
7
|
+
//
|
|
8
|
+
// Columns also come and go with the VIEWPORT. The title is the only column
|
|
9
|
+
// a phone has room for, so the secondary ones drop by breakpoint rather
|
|
10
|
+
// than pushing the table into its own horizontal scroller — measured on a
|
|
11
|
+
// real catalogue at 390px, where four columns of full-length titles, tag
|
|
12
|
+
// lists and collection lists took the table to 1,612px and every row
|
|
13
|
+
// scrolled sideways.
|
|
7
14
|
import {
|
|
8
15
|
Table as TableRoot,
|
|
9
16
|
TableBody,
|
|
@@ -37,10 +44,10 @@
|
|
|
37
44
|
<TableRow>
|
|
38
45
|
<TableHead>Title</TableHead>
|
|
39
46
|
{#if hasTags}
|
|
40
|
-
<TableHead class="w-40">Tags</TableHead>
|
|
47
|
+
<TableHead class="hidden w-40 xl:table-cell">Tags</TableHead>
|
|
41
48
|
{/if}
|
|
42
49
|
{#if hasCollections}
|
|
43
|
-
<TableHead class="w-40">Collections</TableHead>
|
|
50
|
+
<TableHead class="hidden w-40 lg:table-cell">Collections</TableHead>
|
|
44
51
|
{/if}
|
|
45
52
|
{#if hasBadges}
|
|
46
53
|
<TableHead class="w-40 text-center">Status</TableHead>
|
|
@@ -50,11 +57,16 @@
|
|
|
50
57
|
<TableBody>
|
|
51
58
|
{#each documents as doc (doc.id)}
|
|
52
59
|
<TableRow>
|
|
53
|
-
|
|
60
|
+
<!-- max-w-0 + truncate, together: TableCell sets whitespace-nowrap, so
|
|
61
|
+
without a width bound this column stretches the table to its widest
|
|
62
|
+
title, and with a bound but no truncate the text overflows UNDER the
|
|
63
|
+
next cell — which then swallows the link's clicks. -->
|
|
64
|
+
<TableCell class="max-w-0">
|
|
54
65
|
{#if documentHref}
|
|
55
66
|
<a
|
|
56
67
|
href={documentHref(doc)}
|
|
57
|
-
|
|
68
|
+
title={doc.title}
|
|
69
|
+
class="block truncate font-medium hover:underline"
|
|
58
70
|
onclick={() => onOpen?.(doc)}
|
|
59
71
|
>
|
|
60
72
|
{doc.title}
|
|
@@ -62,22 +74,29 @@
|
|
|
62
74
|
{:else if onOpen}
|
|
63
75
|
<button
|
|
64
76
|
type="button"
|
|
65
|
-
|
|
77
|
+
title={doc.title}
|
|
78
|
+
class="block w-full truncate text-left font-medium hover:underline"
|
|
66
79
|
onclick={() => onOpen(doc)}
|
|
67
80
|
>
|
|
68
81
|
{doc.title}
|
|
69
82
|
</button>
|
|
70
83
|
{:else}
|
|
71
|
-
<span class="font-medium">{doc.title}</span>
|
|
84
|
+
<span class="block truncate font-medium" title={doc.title}>{doc.title}</span>
|
|
72
85
|
{/if}
|
|
73
86
|
</TableCell>
|
|
74
87
|
{#if hasTags}
|
|
75
|
-
<TableCell
|
|
88
|
+
<TableCell
|
|
89
|
+
class="text-muted-foreground hidden max-w-40 truncate text-xs xl:table-cell"
|
|
90
|
+
title={doc.tags?.join(', ')}
|
|
91
|
+
>
|
|
76
92
|
{doc.tags?.join(', ') || '—'}
|
|
77
93
|
</TableCell>
|
|
78
94
|
{/if}
|
|
79
95
|
{#if hasCollections}
|
|
80
|
-
<TableCell
|
|
96
|
+
<TableCell
|
|
97
|
+
class="text-muted-foreground hidden max-w-40 truncate text-xs lg:table-cell"
|
|
98
|
+
title={doc.collections?.join(', ')}
|
|
99
|
+
>
|
|
81
100
|
{doc.collections?.join(', ') || '—'}
|
|
82
101
|
</TableCell>
|
|
83
102
|
{/if}
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
import { cn, type WithElementRef } from '../../../utils.js';
|
|
16
16
|
import Search from '@lucide/svelte/icons/search';
|
|
17
17
|
import LibraryIcon from '@lucide/svelte/icons/library';
|
|
18
|
+
import SlidersHorizontal from '@lucide/svelte/icons/sliders-horizontal';
|
|
18
19
|
import X from '@lucide/svelte/icons/x';
|
|
19
20
|
import { Input } from '../input/index.js';
|
|
20
21
|
import Button from '../button/button.svelte';
|
|
@@ -110,6 +111,15 @@
|
|
|
110
111
|
}
|
|
111
112
|
}
|
|
112
113
|
|
|
114
|
+
/**
|
|
115
|
+
* Whether the facet rail is revealed below `md`. The only state this
|
|
116
|
+
* component owns, and deliberately so: it is a disclosure, not a filter.
|
|
117
|
+
* The rail used to be `hidden md:block` with nothing to reveal it, so on a
|
|
118
|
+
* phone the whole of filtering was simply absent — no drawer, no
|
|
119
|
+
* disclosure, no way to reach it at all.
|
|
120
|
+
*/
|
|
121
|
+
let facetsOpen = $state(false);
|
|
122
|
+
|
|
113
123
|
const shownTotal = $derived(total ?? documents.length);
|
|
114
124
|
// Floored at 1 so a consumer's limit: 0 cannot put Infinity in the pager.
|
|
115
125
|
const pageSize = $derived(Math.max(1, limit));
|
|
@@ -161,11 +171,24 @@
|
|
|
161
171
|
</div>
|
|
162
172
|
{/if}
|
|
163
173
|
|
|
174
|
+
{#if facets.length > 0}
|
|
175
|
+
<Button
|
|
176
|
+
variant="outline"
|
|
177
|
+
size="sm"
|
|
178
|
+
class="w-fit md:hidden"
|
|
179
|
+
aria-expanded={facetsOpen}
|
|
180
|
+
onclick={() => (facetsOpen = !facetsOpen)}
|
|
181
|
+
>
|
|
182
|
+
<SlidersHorizontal class="size-4" />
|
|
183
|
+
{facetsOpen ? 'Hide filters' : 'Filters'}
|
|
184
|
+
</Button>
|
|
185
|
+
{/if}
|
|
186
|
+
|
|
164
187
|
<div
|
|
165
188
|
class={facets.length > 0 ? 'grid gap-6 md:grid-cols-[220px_minmax(0,1fr)]' : 'grid gap-6'}
|
|
166
189
|
>
|
|
167
190
|
{#if facets.length > 0}
|
|
168
|
-
<aside class=
|
|
191
|
+
<aside class={facetsOpen ? 'block' : 'hidden md:block'} aria-label="Filters">
|
|
169
192
|
<FacetRail {facets} onChange={onFacetChange} />
|
|
170
193
|
</aside>
|
|
171
194
|
{/if}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@poodle64/ui",
|
|
3
|
-
"version": "2026.8.
|
|
3
|
+
"version": "2026.8.14",
|
|
4
4
|
"description": "Household shared component layer: shadcn-svelte primitives (bits-ui) plus the composed page chrome every app builds its routes from, restyled by each app's @poodle64/design-tokens alias layer. One fix reaches every app.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# @poodle64/ui — situation → component map
|
|
2
2
|
|
|
3
3
|
<!-- GENERATED by scripts/generate-registry.mjs from scripts/situations.json + package source. DO NOT EDIT. -->
|
|
4
|
-
Generated from `@poodle64/ui@2026.8.
|
|
4
|
+
Generated from `@poodle64/ui@2026.8.14`. 54 components, 14 situations.
|
|
5
5
|
|
|
6
6
|
**Read this before writing a `<div>`.** Find the SITUATION you are in below, then compose the component named for it — do not hand-build it from raw `Card` or utility classes. Import is `import { Name } from '<import path>'`. Props marked `?` are optional. This map is the retrieval step the [`frontend-design` skill] makes mandatory; the [CHI 2026 study] measured composing-from-a-registry at 95% design-system compliance against 71% for writing the CSS from a prose style guide.
|
|
7
7
|
|