@f-ewald/components 1.12.0 → 1.14.0

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 (62) hide show
  1. package/README.md +6 -0
  2. package/custom-elements.json +1376 -49
  3. package/dist/auto-scroll.d.ts +37 -0
  4. package/dist/auto-scroll.d.ts.map +1 -0
  5. package/dist/auto-scroll.js +100 -0
  6. package/dist/auto-scroll.js.map +1 -0
  7. package/dist/form-field.d.ts +39 -0
  8. package/dist/form-field.d.ts.map +1 -0
  9. package/dist/form-field.js +145 -0
  10. package/dist/form-field.js.map +1 -0
  11. package/dist/icons.d.ts +1 -0
  12. package/dist/icons.d.ts.map +1 -1
  13. package/dist/icons.js +1 -0
  14. package/dist/icons.js.map +1 -1
  15. package/dist/index.d.ts +6 -0
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +6 -0
  18. package/dist/index.js.map +1 -1
  19. package/dist/load-more.d.ts +33 -0
  20. package/dist/load-more.d.ts.map +1 -0
  21. package/dist/load-more.js +82 -0
  22. package/dist/load-more.js.map +1 -0
  23. package/dist/map-circle.d.ts +12 -9
  24. package/dist/map-circle.d.ts.map +1 -1
  25. package/dist/map-circle.js +26 -12
  26. package/dist/map-circle.js.map +1 -1
  27. package/dist/map-pin.d.ts +7 -5
  28. package/dist/map-pin.d.ts.map +1 -1
  29. package/dist/map-pin.js +14 -8
  30. package/dist/map-pin.js.map +1 -1
  31. package/dist/percent-bar-chart.d.ts +32 -3
  32. package/dist/percent-bar-chart.d.ts.map +1 -1
  33. package/dist/percent-bar-chart.js +101 -36
  34. package/dist/percent-bar-chart.js.map +1 -1
  35. package/dist/scroll-to-bottom.d.ts +41 -0
  36. package/dist/scroll-to-bottom.d.ts.map +1 -0
  37. package/dist/scroll-to-bottom.js +189 -0
  38. package/dist/scroll-to-bottom.js.map +1 -0
  39. package/dist/scroll-to-top.d.ts +41 -0
  40. package/dist/scroll-to-top.d.ts.map +1 -0
  41. package/dist/scroll-to-top.js +189 -0
  42. package/dist/scroll-to-top.js.map +1 -0
  43. package/dist/ui-checkbox.d.ts +49 -0
  44. package/dist/ui-checkbox.d.ts.map +1 -0
  45. package/dist/ui-checkbox.js +228 -0
  46. package/dist/ui-checkbox.js.map +1 -0
  47. package/dist/utils/scroll.d.ts +9 -0
  48. package/dist/utils/scroll.d.ts.map +1 -0
  49. package/dist/utils/scroll.js +30 -0
  50. package/dist/utils/scroll.js.map +1 -0
  51. package/docs/auto-scroll.md +52 -0
  52. package/docs/form-field.md +63 -0
  53. package/docs/layouts/form-page.md +8 -4
  54. package/docs/load-more.md +43 -0
  55. package/docs/map-circle.md +11 -9
  56. package/docs/map-pin.md +6 -5
  57. package/docs/percent-bar-chart.md +19 -5
  58. package/docs/scroll-to-bottom.md +68 -0
  59. package/docs/scroll-to-top.md +59 -0
  60. package/docs/ui-checkbox.md +58 -0
  61. package/llms.txt +199 -20
  62. package/package.json +1 -1
package/llms.txt CHANGED
@@ -169,6 +169,37 @@ Example:
169
169
  </app-sidebar>
170
170
  ```
171
171
 
172
+ ## <auto-scroll>
173
+
174
+ Wraps arbitrary slotted content (e.g. `timeline-container`) and keeps it
175
+ scrolled to the bottom as new children are appended — but only while the
176
+ user is already scrolled near the bottom ("stick to bottom", a chat/log-
177
+ viewer convention). If the user has scrolled up to read earlier content,
178
+ new content does not yank the scroll position back down.
179
+
180
+ New content is detected via a `MutationObserver`, so no cooperation from
181
+ whatever is slotted in is required. The host itself is the scrollable
182
+ region (`overflow-y: auto`) and needs a consumer-supplied bounded height to
183
+ have anything to scroll, e.g. `auto-scroll { height: 24rem; }`.
184
+
185
+ Import: `import "@f-ewald/components/auto-scroll.js";`
186
+
187
+ Properties: `threshold` (attribute `threshold`) : number, default 24; `pinned` (JS property only) : boolean, default —
188
+ Events: `pinned-change`
189
+ CSS custom properties: none
190
+
191
+ Example:
192
+ ```html
193
+ <auto-scroll style="height: 24rem">
194
+ <timeline-container>
195
+ <timeline-entry datetime="2026-07-23T09:00:00Z">
196
+ <span slot="headline">Deployment started</span>
197
+ Release v1.4.0 is rolling out.
198
+ </timeline-entry>
199
+ </timeline-container>
200
+ </auto-scroll>
201
+ ```
202
+
172
203
  ## <autocomplete-input>
173
204
 
174
205
  Generic form-associated text input with a suggestion dropdown, for any
@@ -608,6 +639,39 @@ Example:
608
639
  </form-actions>
609
640
  ```
610
641
 
642
+ ## <form-field>
643
+
644
+ Per-field wrapper for a form control: label, slotted control, and an
645
+ optional hint or error message, in one consistent unit repeated across a
646
+ form. Purely presentational — composes whatever control is slotted
647
+ (`form-select`, `multi-select`, `autocomplete-input`, `ui-checkbox`, etc.)
648
+ without intercepting its events or value.
649
+
650
+ The label wraps the default slot for a best-effort visual/click
651
+ association only: every existing value-entry control encapsulates its
652
+ real `<input>` inside its own shadow DOM, so there is no light-DOM `id` a
653
+ `for` attribute could target from outside, and this component cannot set
654
+ `aria-describedby`/`aria-invalid` on an arbitrary slotted control's
655
+ shadow-encapsulated input for the same reason. The error message uses
656
+ `role="alert"` as the practical accessibility mitigation instead of true
657
+ `aria-describedby` association.
658
+
659
+ Import: `import "@f-ewald/components/form-field.js";`
660
+
661
+ Properties: `label` (attribute `label`) : string, default ""; `hint` (attribute `hint`) : string, default ""; `error` (attribute `error`) : string, default ""; `required` (attribute `required`) : boolean, default false
662
+ Events: none
663
+ CSS custom properties: `--ui-danger`, `--ui-font`, `--ui-font-size-sm`, `--ui-font-size-xs`, `--ui-font-weight-medium`, `--ui-line-height-tight`, `--ui-text`, `--ui-text-muted`
664
+
665
+ Example:
666
+ ```html
667
+ <form-field label="Task state" hint="Only affects your own view">
668
+ <form-select></form-select>
669
+ </form-field>
670
+ <form-field label="Terms" required error="You must accept to continue">
671
+ <ui-checkbox label="I agree to the terms"></ui-checkbox>
672
+ </form-field>
673
+ ```
674
+
611
675
  ## <form-select>
612
676
 
613
677
  A styled dropdown select: a trigger button showing the current option's
@@ -909,20 +973,40 @@ Example:
909
973
  <live-timer since="2026-07-19T12:00:00Z" format="compact" prefix="running for "></live-timer>
910
974
  ```
911
975
 
976
+ ## <load-more>
977
+
978
+ Click-to-load button for either end of a list. Fully property-driven: the
979
+ consumer sets `loading` while a fetch is in flight and `exhausted` once
980
+ there's nothing left to load; this component never fetches or manages
981
+ state itself.
982
+
983
+ Import: `import "@f-ewald/components/load-more.js";`
984
+
985
+ Properties: `direction` (attribute `direction`) : "top" | "bottom", default "bottom"; `loading` (attribute `loading`) : boolean, default false; `exhausted` (attribute `exhausted`) : boolean, default false; `label` (attribute `label`) : string, default "Load more"; `exhaustedLabel` (attribute `exhausted-label`) : string, default "No more results"
986
+ Events: `load-more`
987
+ CSS custom properties: none
988
+
989
+ Example:
990
+ ```html
991
+ <load-more direction="top" label="Load older"></load-more>
992
+ <load-more></load-more>
993
+ ```
994
+
912
995
  ## <map-circle>
913
996
 
914
- A plain circular map marker: a light-to-dark gradient fill with a white
915
- outer ring, no point/tail (unlike `<map-pin>`) — for markers that don't
916
- need to visually "point" at their exact coordinate. Purely a visual
917
- primitive — it has no `mapbox-gl` (or any mapping library) dependency;
918
- the consumer positions it, e.g. via `new mapboxgl.Marker({ element: el })`.
919
- It can also replace the former `<map-point>` dense-layer primitive: use
920
- `size="14" ring-width="3"`, leave the slot empty, and rasterize one marker
921
- per color for use as a map `icon-image`.
997
+ A plain circular map marker: a radial-gradient fill with a soft highlight
998
+ and a translucent white outer ring, no point/tail (unlike `<map-pin>`) —
999
+ for markers that don't need to visually "point" at their exact
1000
+ coordinate. Purely a visual primitive — it has no `mapbox-gl` (or any
1001
+ mapping library) dependency; the consumer positions it, e.g. via
1002
+ `new mapboxgl.Marker({ element: el })`. It can also replace the former
1003
+ `<map-point>` dense-layer primitive: use `size="14" ring-width="3"`, leave
1004
+ the slot empty, and rasterize one marker per color for use as a map
1005
+ `icon-image`.
922
1006
 
923
1007
  Import: `import "@f-ewald/components/map-circle.js";`
924
1008
 
925
- Properties: `color` (attribute `color`) : string, default "#4f46e5"; `size` (attribute `size`) : number, default 18; `ringWidth` (attribute `ring-width`) : number, default 4; `highlighted` (attribute `highlighted`) : boolean, default false
1009
+ Properties: `color` (attribute `color`) : string, default "#4f46e5"; `size` (attribute `size`) : number, default 18; `ringWidth` (attribute `ring-width`) : number, default 4; `ringOpacity` (attribute `ring-opacity`) : number, default 0.6; `highlighted` (attribute `highlighted`) : boolean, default false
926
1010
  Events: none
927
1011
  CSS custom properties: `--ui-font`, `--ui-font-weight-bold`, `--ui-line-height-glyph`
928
1012
 
@@ -935,14 +1019,14 @@ Example:
935
1019
 
936
1020
  ## <map-pin>
937
1021
 
938
- A circular "Apple Maps"-style map pin: a light-to-dark gradient fill with
939
- a slight point at the bottom. Purely a visual primitive — it has no
940
- `mapbox-gl` (or any mapping library) dependency; the consumer positions
941
- it, e.g. via `new mapboxgl.Marker({ element: pinEl })`.
1022
+ A circular "Apple Maps"-style map pin: a radial-gradient fill with a soft
1023
+ highlight and a slight point at the bottom. Purely a visual primitive —
1024
+ it has no `mapbox-gl` (or any mapping library) dependency; the consumer
1025
+ positions it, e.g. via `new mapboxgl.Marker({ element: pinEl })`.
942
1026
 
943
1027
  Import: `import "@f-ewald/components/map-pin.js";`
944
1028
 
945
- Properties: `color` (attribute `color`) : string, default "#4f46e5"; `size` (attribute `size`) : number, default 32; `highlighted` (attribute `highlighted`) : boolean, default false
1029
+ Properties: `color` (attribute `color`) : string, default "#4f46e5"; `size` (attribute `size`) : number, default 32; `ringOpacity` (attribute `ring-opacity`) : number, default 0.6; `highlighted` (attribute `highlighted`) : boolean, default false
946
1030
  Events: none
947
1031
  CSS custom properties: `--ui-font`, `--ui-font-weight-bold`, `--ui-line-height-glyph`
948
1032
 
@@ -1108,12 +1192,16 @@ Example:
1108
1192
 
1109
1193
  ## <percent-bar-chart>
1110
1194
 
1111
- Horizontal bar chart for labeled percentage rows, using D3's linear scale.
1112
- Each group gets its own labeled row; bars are proportional to percentage of 100.
1195
+ Bar chart for labeled rows, using D3's linear scale. Horizontal (default)
1196
+ renders stacked rows with bars growing rightward; `orientation="vertical"`
1197
+ renders side-by-side columns growing upward instead. `mode="percent"`
1198
+ (default) scales `value` against a fixed 0-100 domain and labels it with a
1199
+ `%` suffix; `mode="value"` scales it against `max` (or the largest `value`
1200
+ present) and formats it with `valueFormat`.
1113
1201
 
1114
1202
  Import: `import "@f-ewald/components/percent-bar-chart.js";`
1115
1203
 
1116
- Properties: `groups` (JS property only) : PercentBarGroup[], default []
1204
+ Properties: `groups` (JS property only) : PercentBarGroup[], default []; `mode` (attribute `mode`) : PercentBarMode, default "percent"; `orientation` (attribute `orientation`) : PercentBarOrientation, default "horizontal"; `max` (attribute `max`) : number | undefined, default —; `valueFormat` (JS property only) : (value: number) => string, default —
1117
1205
  Events: none
1118
1206
  CSS custom properties: `--ui-font`, `--ui-text-muted`
1119
1207
 
@@ -1121,10 +1209,16 @@ Example:
1121
1209
  ```html
1122
1210
  <percent-bar-chart></percent-bar-chart>
1123
1211
  <script type="module">
1124
- document.querySelector("percent-bar-chart").groups = [
1125
- { key: "a", label: "White", pct: 45.2, color: "#4f46e5" },
1126
- { key: "b", label: "Asian", pct: 28.1, color: "#0d9488" },
1212
+ const chart = document.querySelector("percent-bar-chart");
1213
+ chart.groups = [
1214
+ { key: "a", label: "White", value: 45.2, color: "#4f46e5" },
1215
+ { key: "b", label: "Asian", value: 28.1, color: "#0d9488" },
1127
1216
  ];
1217
+
1218
+ // Absolute values instead of percentages, as vertical columns:
1219
+ chart.mode = "value";
1220
+ chart.orientation = "vertical";
1221
+ chart.valueFormat = (value) => `$${value.toLocaleString()}`;
1128
1222
  </script>
1129
1223
  ```
1130
1224
 
@@ -1325,6 +1419,67 @@ Example:
1325
1419
  <roman-numeral value="2004"></roman-numeral>
1326
1420
  ```
1327
1421
 
1422
+ ## <scroll-to-bottom>
1423
+
1424
+ Overlay button that appears once the page (or a given `target` container)
1425
+ has scrolled more than `threshold` pixels away from the bottom edge, and
1426
+ scrolls back to the bottom on click.
1427
+
1428
+ With no `target` (default), the button is `position: fixed` to the
1429
+ viewport. When `target` is set, the button switches to `position:
1430
+ absolute` and expects to be placed as a descendant of a `position:
1431
+ relative` (or otherwise positioned) ancestor that establishes the visual
1432
+ bounds to float within — typically `target` itself, given `overflow-y:
1433
+ auto; position: relative`, so the button stays pinned to that container's
1434
+ own visible corner as its content scrolls, rather than floating over the
1435
+ whole page.
1436
+
1437
+ Import: `import "@f-ewald/components/scroll-to-bottom.js";`
1438
+
1439
+ Properties: `target` (JS property only) : HTMLElement | null, default null; `threshold` (attribute `threshold`) : number, default 200; `label` (attribute `label`) : string, default "Scroll to bottom"
1440
+ Events: `scroll-to-bottom-triggered`
1441
+ CSS custom properties: `--ui-border`, `--ui-focus-ring`, `--ui-font`, `--ui-font-size-sm`, `--ui-font-weight-medium`, `--ui-line-height-tight`, `--ui-shadow`, `--ui-surface`, `--ui-surface-muted`, `--ui-text`
1442
+
1443
+ Example:
1444
+ ```html
1445
+ <scroll-to-bottom></scroll-to-bottom>
1446
+
1447
+ <!-- Floats inside its own scrollport instead of the whole page: -->
1448
+ <div id="log" style="position: relative; overflow-y: auto; height: 10rem">
1449
+ ...
1450
+ <scroll-to-bottom threshold="20"></scroll-to-bottom>
1451
+ </div>
1452
+ <script type="module">
1453
+ document.querySelector('scroll-to-bottom').target = document.querySelector('#log');
1454
+ </script>
1455
+ ```
1456
+
1457
+ ## <scroll-to-top>
1458
+
1459
+ Overlay button that appears once the page (or a given `target` container)
1460
+ has scrolled more than `threshold` pixels away from the top edge, and
1461
+ scrolls back to the top on click.
1462
+
1463
+ With no `target` (default), the button is `position: fixed` to the
1464
+ viewport. When `target` is set, the button switches to `position:
1465
+ absolute` and expects to be placed as a descendant of a `position:
1466
+ relative` (or otherwise positioned) ancestor that establishes the visual
1467
+ bounds to float within — typically `target` itself, given `overflow-y:
1468
+ auto; position: relative`, so the button stays pinned to that container's
1469
+ own visible corner as its content scrolls, rather than floating over the
1470
+ whole page.
1471
+
1472
+ Import: `import "@f-ewald/components/scroll-to-top.js";`
1473
+
1474
+ Properties: `target` (JS property only) : HTMLElement | null, default null; `threshold` (attribute `threshold`) : number, default 200; `label` (attribute `label`) : string, default "Scroll to top"
1475
+ Events: `scroll-to-top-triggered`
1476
+ CSS custom properties: `--ui-border`, `--ui-focus-ring`, `--ui-font`, `--ui-font-size-sm`, `--ui-font-weight-medium`, `--ui-line-height-tight`, `--ui-shadow`, `--ui-surface`, `--ui-surface-muted`, `--ui-text`
1477
+
1478
+ Example:
1479
+ ```html
1480
+ <scroll-to-top></scroll-to-top>
1481
+ ```
1482
+
1328
1483
  ## <slide-panel>
1329
1484
 
1330
1485
  Generic sliding panel shell. Handles positioning, open/close animation,
@@ -1604,6 +1759,30 @@ Example:
1604
1759
  <ui-button variant="secondary" href="/properties?edit=42">Edit</ui-button>
1605
1760
  ```
1606
1761
 
1762
+ ## <ui-checkbox>
1763
+
1764
+ A form-associated boolean checkbox, usable standalone or inside a native
1765
+ `<form>`. Submits `name=on` when checked (matching native
1766
+ `<input type="checkbox">` semantics) and participates fully in form
1767
+ `reset()`, ancestor `<fieldset disabled>`, and `required` validity.
1768
+
1769
+ Renders a native `<input type="checkbox">` wrapped in a `<label>`, styled
1770
+ via `:has()` on the wrapping label (matching `radio-pills`/`radio-cards`)
1771
+ rather than styling the native input directly; the checkbox itself renders
1772
+ at `1rem`, matching the existing radio-input convention.
1773
+
1774
+ Import: `import "@f-ewald/components/ui-checkbox.js";`
1775
+
1776
+ Properties: `checked` (attribute `checked`) : boolean, default false; `indeterminate` (attribute `indeterminate`) : boolean, default false; `disabled` (attribute `disabled`) : boolean, default false; `required` (attribute `required`) : boolean, default false; `name` (attribute `name`) : string, default ""; `label` (attribute `label`) : string, default ""
1777
+ Events: `change`
1778
+ CSS custom properties: `--ui-danger`, `--ui-focus-ring`, `--ui-font`, `--ui-font-size-sm`, `--ui-line-height-tight`, `--ui-primary`, `--ui-radius-sm`, `--ui-text`
1779
+
1780
+ Example:
1781
+ ```html
1782
+ <ui-checkbox label="Subscribe to updates"></ui-checkbox>
1783
+ <ui-checkbox name="terms" label="I agree to the terms" required></ui-checkbox>
1784
+ ```
1785
+
1607
1786
  ## <user-avatar>
1608
1787
 
1609
1788
  Circular avatar. Shows `src` when it loads successfully; falls back to the
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@f-ewald/components",
3
3
  "private": false,
4
- "version": "1.12.0",
4
+ "version": "1.14.0",
5
5
  "description": "A collection of universally usable web components for various tasks.",
6
6
  "type": "module",
7
7
  "main": "dist/index.js",