@bagelink/vue 1.15.141 → 1.15.145

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@bagelink/vue",
3
3
  "type": "module",
4
- "version": "1.15.141",
4
+ "version": "1.15.145",
5
5
  "description": "Bagel core sdk packages",
6
6
  "author": {
7
7
  "name": "Bagel Studio",
@@ -30,12 +30,47 @@ const props = withDefaults(
30
30
  md?: boolean
31
31
  lg?: boolean
32
32
  xl?: boolean
33
+ /** Fine-grained size overrides — take precedence over `size`. Accept any CSS
34
+ length (e.g. `'1.1rem'`, `'18px'`) or a raw number (treated as `rem` for
35
+ text, `px` for the avatar). Leave unset to inherit from `size`.
36
+ Under the hood these set public CSS vars you can also drive externally:
37
+ `--bgl-list-item-title-size`, `--bgl-list-item-subtitle-size`,
38
+ `--bgl-list-item-lead-size`, `--bgl-list-item-icon-chip-size`. */
39
+ fontSize?: string | number
40
+ titleSize?: string | number
41
+ subtitleSize?: string | number
42
+ leadSize?: string | number
43
+ /** Font weights — take precedence over `size`/defaults. Accept a number
44
+ (`600`) or a keyword (`'bold'`). Leave unset to keep the defaults
45
+ (title 600, subtitle 300, lead 600). Set the same public CSS vars:
46
+ `--bgl-list-item-title-weight`, `--bgl-list-item-subtitle-weight`,
47
+ `--bgl-list-item-lead-weight`. */
48
+ fontWeight?: string | number
49
+ titleWeight?: string | number
50
+ subtitleWeight?: string | number
51
+ leadWeight?: string | number
52
+ /** Glyph size of the leading `icon` (and, if a number, of the trailing
53
+ `iconEnd`). Any CSS length or number-as-rem. */
54
+ iconSize?: string | number
55
+ /** Box size of the leading icon-chip (the tinted square). CSS length / px. */
56
+ iconChipSize?: string | number
57
+ /** Toggle the leading icon's tinted background chip. Defaults `true` (the
58
+ tinted rounded square). Set `:icon-chip="false"` to drop the background
59
+ and render the bare glyph (still accent-colored). */
60
+ iconChip?: boolean
61
+ /** Override the leading avatar diameter (px). */
62
+ avatarSize?: number
33
63
  fullWidth?: boolean
34
64
  ellipsis?: boolean
35
65
  ripple?: boolean
36
66
  /** Theme accent for the leading icon-chip and hover/active tint.
37
67
  Defaults to the surface's primary. e.g. `color="purple"`. */
38
68
  color?: ThemeType
69
+ /** Color of the leading icon glyph ONLY — overrides the accent for the
70
+ glyph without touching the chip background or hover/active tint.
71
+ Accepts a theme name (`'purple'` → `var(--bgl-purple)`) or any raw CSS
72
+ color (`'#f00'`, `'currentColor'`). */
73
+ iconColor?: ThemeType | (string & {})
39
74
  /** Card-style row: rounded, no divider, tinted hover. Use for standalone
40
75
  selectable lists (vs. the default flush divided rows in a panel). */
41
76
  rounded?: boolean
@@ -48,6 +83,7 @@ const props = withDefaults(
48
83
  {
49
84
  ellipsis: true,
50
85
  ripple: false,
86
+ iconChip: true,
51
87
  }
52
88
  )
53
89
 
@@ -95,12 +131,62 @@ const computedSize = computed(() => {
95
131
  // behavior (thin → 30px avatar, else 38px; 1.2rem icon glyph).
96
132
  const avatarSizeMap = { xs: 20, sm: 24, md: 38, lg: 46, xl: 56 } as const
97
133
  const iconSizeMap = { xs: 0.8, sm: 0.9, md: 1.2, lg: 1.5, xl: 1.8 } as const
134
+
135
+ // Normalize a size prop to a usable CSS length: bare numbers → `rem` (text/glyph)
136
+ // or `px` (box), while any string (`'1.1rem'`, `'18px'`, `'2em'`) passes through.
137
+ const toRem = (v: string | number | undefined) =>
138
+ v === undefined ? undefined : (typeof v === 'number' ? `${v}rem` : v)
139
+ const toPx = (v: string | number | undefined) =>
140
+ v === undefined ? undefined : (typeof v === 'number' ? `${v}px` : v)
141
+
142
+ // Effective avatar diameter: explicit `avatarSize` prop wins, then `size`, then
143
+ // the historical thin/default fallback.
98
144
  const avatarSize = computed(() =>
99
- computedSize.value ? avatarSizeMap[computedSize.value] : (props.thin ? 30 : 38),
100
- )
101
- const iconSize = computed(() =>
102
- computedSize.value ? iconSizeMap[computedSize.value] : 1.2,
145
+ props.avatarSize ?? (computedSize.value ? avatarSizeMap[computedSize.value] : (props.thin ? 30 : 38)),
103
146
  )
147
+ // Effective leading-icon glyph size passed to <Icon :size>. `<Icon>` appends
148
+ // `rem` to its `size`, so we resolve to a bare rem number: a numeric prop is used
149
+ // as-is; a `'1.5rem'` string has its number extracted; a `size` step maps via
150
+ // iconSizeMap; otherwise the historical 1.2rem. (For px control of the chip box,
151
+ // use `iconChipSize`.)
152
+ const iconSize = computed(() => {
153
+ if (typeof props.iconSize === 'number') { return props.iconSize }
154
+ if (typeof props.iconSize === 'string') {
155
+ const n = Number.parseFloat(props.iconSize)
156
+ if (!Number.isNaN(n)) { return n }
157
+ }
158
+ return computedSize.value ? iconSizeMap[computedSize.value] : 1.2
159
+ })
160
+
161
+ // Inline CSS vars set from the fine-grained size props. These map onto the same
162
+ // public custom properties consumers can set externally, so a `prop` is just a
163
+ // convenient way to set the var for a single row.
164
+ // A theme name (`blue`, `purple-light`, …) maps to its `--bgl-<name>` token; any
165
+ // other value (hex, rgb(), currentColor, a custom var) is used verbatim. Theme
166
+ // names are plain lowercase identifiers with optional `-` (e.g. `blue-light`).
167
+ const resolveColor = (v: string) =>
168
+ /^[a-z]+(-[a-z]+)*$/.test(v) ? `var(--bgl-${v})` : v
169
+
170
+ const listItemVars = computed(() => {
171
+ const vars: Record<string, string> = {}
172
+ if (props.color) { vars['--bgl-list-item-accent'] = `var(--bgl-${props.color})` }
173
+ if (props.iconColor) { vars['--bgl-list-item-icon-color'] = resolveColor(props.iconColor) }
174
+ const title = toRem(props.titleSize ?? props.fontSize)
175
+ if (title) { vars['--bgl-list-item-title-size'] = title }
176
+ const subtitle = toRem(props.subtitleSize)
177
+ if (subtitle) { vars['--bgl-list-item-subtitle-size'] = subtitle }
178
+ const lead = toRem(props.leadSize)
179
+ if (lead) { vars['--bgl-list-item-lead-size'] = lead }
180
+ const chip = toPx(props.iconChipSize)
181
+ if (chip) { vars['--bgl-list-item-icon-chip-size'] = chip }
182
+ // Font weights — a number or keyword passes through as-is. `fontWeight` acts
183
+ // as the base for the title unless a more specific `titleWeight` is given.
184
+ const titleW = props.titleWeight ?? props.fontWeight
185
+ if (titleW !== undefined) { vars['--bgl-list-item-title-weight'] = String(titleW) }
186
+ if (props.subtitleWeight !== undefined) { vars['--bgl-list-item-subtitle-weight'] = String(props.subtitleWeight) }
187
+ if (props.leadWeight !== undefined) { vars['--bgl-list-item-lead-weight'] = String(props.leadWeight) }
188
+ return vars
189
+ })
104
190
 
105
191
  const hasTo = computed(() => props.to !== undefined && props.to !== '')
106
192
  const hasHref = computed(() => props.href !== undefined && props.href !== '')
@@ -143,7 +229,7 @@ const bind = computed(() => {
143
229
  { 'no-border-list': props.flat || rounded, 'list-item-flush': props.fullWidth, 'list-item-fullrow': isClickable, 'list-item-has-end': hasEnd, 'list-item-rounded': rounded, 'list-item-active': active },
144
230
  computedSize ? `list-item-${computedSize}` : '',
145
231
  ]"
146
- :style="color ? { '--bgl-list-item-accent': `var(--bgl-${color})` } : undefined"
232
+ :style="listItemVars"
147
233
  >
148
234
  <!-- Content rendered before the clickable area (e.g. a drag handle, avatar
149
235
  or leading visual). Lives outside the clickable component so interacting
@@ -162,7 +248,7 @@ const bind = computed(() => {
162
248
  (covers, thumbnails); `src`/`showAvatar` and `icon` are shortcuts. -->
163
249
  <slot name="media">
164
250
  <Avatar v-if="src || showAvatar" :name="title" :src="src" :size="avatarSize" class="flex-shrink-0" />
165
- <span v-if="icon" class="list-item-icon flex-shrink-0">
251
+ <span v-if="icon" class="list-item-icon flex-shrink-0" :class="{ 'list-item-icon-nobg': !iconChip }">
166
252
  <Icon :size="iconSize" :icon="icon" />
167
253
  </span>
168
254
  </slot>
@@ -345,15 +431,32 @@ background-color: var(--bgl-list-item-hover, color-mix(in srgb, var(--bgl-list-i
345
431
  filter: var(--bgl-hover-filter);
346
432
  }
347
433
 
348
- /* Leading icon chip — tinted with the row accent (primary by default). */
434
+ /* Leading icon chip — tinted with the row accent (primary by default).
435
+ The tinted square's box size is driven by a public CSS var so consumers (or
436
+ the `iconChipSize` prop) can retune it without a `size` step:
437
+ --bgl-list-item-icon-chip-size → the tinted square (default 34px)
438
+ The glyph *inside* it is sized via the `iconSize` prop (→ <Icon :size>). */
349
439
  .list-item-icon {
350
440
  display: grid;
351
441
  place-items: center;
352
- width: 34px;
353
- height: 34px;
442
+ width: var(--bgl-list-item-icon-chip-size, 34px);
443
+ height: var(--bgl-list-item-icon-chip-size, 34px);
354
444
  border-radius: 9px;
355
445
  background: color-mix(in srgb, var(--bgl-list-item-accent) 12%, transparent);
356
- color: var(--bgl-list-item-accent);
446
+ /* Glyph color: an explicit `iconColor` (--bgl-list-item-icon-color) wins;
447
+ otherwise fall back to the row accent so existing rows are unchanged. The
448
+ chip background above stays tied to the accent regardless. */
449
+ color: var(--bgl-list-item-icon-color, var(--bgl-list-item-accent));
450
+ }
451
+ /* `:icon-chip="false"` → drop the tinted square and its fixed box so the bare
452
+ glyph sits at its natural size (still accent-colored). The `.list-item-row`
453
+ prefix lifts specificity above the per-size `.list-item-<sz> .list-item-icon`
454
+ rules below, which also pin width/height. */
455
+ .list-item-row .list-item-icon-nobg {
456
+ width: auto;
457
+ height: auto;
458
+ background: none;
459
+ border-radius: 0;
357
460
  }
358
461
 
359
462
  /* Text block: tight, clear hierarchy. min-width:0 lets the ellipsis titles/
@@ -362,24 +465,28 @@ color: var(--bgl-list-item-accent);
362
465
  min-width: 0;
363
466
  }
364
467
  .list-item-body > p { margin: 0; padding: 0; }
468
+ /* Type sizes read a public CSS var with the historical value as fallback, so
469
+ consumers / the fontSize·titleSize·subtitleSize·leadSize props can retune the
470
+ text without stepping the whole `size` scale:
471
+ --bgl-list-item-title-size · --bgl-list-item-subtitle-size · --bgl-list-item-lead-size */
365
472
  .list-item-lead {
366
- font-size: 0.6875rem;
367
- font-weight: 600;
473
+ font-size: var(--bgl-list-item-lead-size, 0.6875rem);
474
+ font-weight: var(--bgl-list-item-lead-weight, 600);
368
475
  text-transform: uppercase;
369
476
  letter-spacing: 0.04em;
370
477
  opacity: 0.5;
371
478
  line-height: 1.3;
372
479
  }
373
480
  .list-item-title {
374
- font-size: 0.9375rem;
375
- font-weight: 600;
481
+ font-size: var(--bgl-list-item-title-size, 0.9375rem);
482
+ font-weight: var(--bgl-list-item-title-weight, 600);
376
483
  line-height: 1.35;
377
484
  }
378
485
  .list-item-subtitle {
379
- font-size: 0.8125rem;
486
+ font-size: var(--bgl-list-item-subtitle-size, 0.8125rem);
380
487
  opacity: 0.6;
381
488
  line-height: 1.35;
382
- font-weight: 300;
489
+ font-weight: var(--bgl-list-item-subtitle-weight, 300);
383
490
  margin-top: 1px;
384
491
  }
385
492
  .list-item-endtext {
@@ -395,29 +502,27 @@ min-width: 0;
395
502
  row's vertical padding + horizontal gutter. `md` matches the historical
396
503
  defaults, so unsized rows are unchanged. `size` composes with `thin` (which
397
504
  only trims vertical padding). */
398
- .list-item-xs { --list-item-padding-inline: 0.4rem; }
505
+ /* Each size sets the public type vars (not font-size directly) so a per-row
506
+ fontSize/titleSize/… prop — which sets the same var inline — still wins over
507
+ the chosen `size`. The icon-chip box size stays a direct width/height here;
508
+ its border-radius keeps its per-size tuning. */
509
+ .list-item-xs { --list-item-padding-inline: 0.4rem; --bgl-list-item-title-size: 0.75rem; --bgl-list-item-subtitle-size: 0.625rem; }
399
510
  /* !important needed to beat the base .py-075/.py-05 utility classes. */
400
511
  .list-item-xs .list-item { padding-block: 0.1rem !important; gap: 0.4rem; }
401
- .list-item-xs .list-item-icon { width: 22px; height: 22px; border-radius: 6px; }
402
- .list-item-xs .list-item-title { font-size: 0.75rem; }
403
- .list-item-xs .list-item-subtitle { font-size: 0.625rem; }
512
+ .list-item-xs .list-item-icon { width: var(--bgl-list-item-icon-chip-size, 22px); height: var(--bgl-list-item-icon-chip-size, 22px); border-radius: 6px; }
404
513
 
405
- .list-item-sm { --list-item-padding-inline: 0.55rem; }
514
+ .list-item-sm { --list-item-padding-inline: 0.55rem; --bgl-list-item-title-size: 0.8125rem; --bgl-list-item-subtitle-size: 0.6875rem; }
406
515
  .list-item-sm .list-item { padding-block: 0.2rem !important; gap: 0.5rem; }
407
- .list-item-sm .list-item-icon { width: 26px; height: 26px; border-radius: 7px; }
408
- .list-item-sm .list-item-title { font-size: 0.8125rem; }
409
- .list-item-sm .list-item-subtitle { font-size: 0.6875rem; }
516
+ .list-item-sm .list-item-icon { width: var(--bgl-list-item-icon-chip-size, 26px); height: var(--bgl-list-item-icon-chip-size, 26px); border-radius: 7px; }
410
517
 
411
518
  /* md = defaults (no overrides needed). */
412
519
 
520
+ .list-item-lg { --bgl-list-item-title-size: 1.0625rem; --bgl-list-item-subtitle-size: 0.875rem; }
413
521
  .list-item-lg .list-item { padding-block: 0.85rem !important; gap: 0.85rem; }
414
- .list-item-lg .list-item-icon { width: 42px; height: 42px; border-radius: 11px; }
415
- .list-item-lg .list-item-title { font-size: 1.0625rem; }
416
- .list-item-lg .list-item-subtitle { font-size: 0.875rem; }
522
+ .list-item-lg .list-item-icon { width: var(--bgl-list-item-icon-chip-size, 42px); height: var(--bgl-list-item-icon-chip-size, 42px); border-radius: 11px; }
417
523
 
524
+ .list-item-xl { --bgl-list-item-title-size: 1.1875rem; --bgl-list-item-subtitle-size: 0.9375rem; }
418
525
  .list-item-xl .list-item { padding-block: 1.1rem !important; gap: 1rem; }
419
- .list-item-xl .list-item-icon { width: 50px; height: 50px; border-radius: 13px; }
420
- .list-item-xl .list-item-title { font-size: 1.1875rem; }
421
- .list-item-xl .list-item-subtitle { font-size: 0.9375rem; }
526
+ .list-item-xl .list-item-icon { width: var(--bgl-list-item-icon-chip-size, 50px); height: var(--bgl-list-item-icon-chip-size, 50px); border-radius: 13px; }
422
527
 
423
528
  </style>
@@ -325,7 +325,8 @@ flex-grow: 1;
325
325
  the row and it wraps), rather than the header force-stacking every left side. */
326
326
  .endNavTools {
327
327
  margin-inline-start: auto;
328
- justify-content: flex-end;
328
+ /* justify-content: flex-end; */
329
+ width: auto;
329
330
  }
330
331
  /* Mobile: don't push the trailing side to the far edge — let it sit right after the
331
332
  leading side (no auto margin, start-aligned) so the header reads as one compact
@@ -333,7 +334,7 @@ justify-content: flex-end;
333
334
  @media screen and (max-width: 910px) {
334
335
  .endNavTools {
335
336
  margin-inline-start: 0;
336
- justify-content: flex-start;
337
+ /* justify-content: flex-start; */
337
338
  }
338
339
  }
339
340
 
@@ -444,7 +445,7 @@ padding-top: 0;
444
445
  .pageContent--fill {
445
446
  display: flex;
446
447
  flex-direction: column;
447
- overflow: hidden;
448
+ /* overflow: hidden; */
448
449
  }
449
450
  .pageContent--fill > * {
450
451
  flex: 1 1 auto;