@f-ewald/components 1.11.0 → 1.13.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 (57) hide show
  1. package/README.md +7 -0
  2. package/custom-elements.json +1651 -115
  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 +7 -0
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +7 -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/percent-bar-chart.d.ts +32 -3
  24. package/dist/percent-bar-chart.d.ts.map +1 -1
  25. package/dist/percent-bar-chart.js +101 -36
  26. package/dist/percent-bar-chart.js.map +1 -1
  27. package/dist/scroll-to-bottom.d.ts +41 -0
  28. package/dist/scroll-to-bottom.d.ts.map +1 -0
  29. package/dist/scroll-to-bottom.js +189 -0
  30. package/dist/scroll-to-bottom.js.map +1 -0
  31. package/dist/scroll-to-top.d.ts +41 -0
  32. package/dist/scroll-to-top.d.ts.map +1 -0
  33. package/dist/scroll-to-top.js +189 -0
  34. package/dist/scroll-to-top.js.map +1 -0
  35. package/dist/tree-view.d.ts +44 -0
  36. package/dist/tree-view.d.ts.map +1 -0
  37. package/dist/tree-view.js +200 -0
  38. package/dist/tree-view.js.map +1 -0
  39. package/dist/ui-checkbox.d.ts +49 -0
  40. package/dist/ui-checkbox.d.ts.map +1 -0
  41. package/dist/ui-checkbox.js +228 -0
  42. package/dist/ui-checkbox.js.map +1 -0
  43. package/dist/utils/scroll.d.ts +9 -0
  44. package/dist/utils/scroll.d.ts.map +1 -0
  45. package/dist/utils/scroll.js +30 -0
  46. package/dist/utils/scroll.js.map +1 -0
  47. package/docs/auto-scroll.md +52 -0
  48. package/docs/form-field.md +63 -0
  49. package/docs/layouts/form-page.md +8 -4
  50. package/docs/load-more.md +43 -0
  51. package/docs/percent-bar-chart.md +19 -5
  52. package/docs/scroll-to-bottom.md +68 -0
  53. package/docs/scroll-to-top.md +59 -0
  54. package/docs/tree-view.md +69 -0
  55. package/docs/ui-checkbox.md +58 -0
  56. package/llms.txt +223 -6
  57. 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,6 +973,25 @@ 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
997
  A plain circular map marker: a light-to-dark gradient fill with a white
@@ -1108,12 +1191,16 @@ Example:
1108
1191
 
1109
1192
  ## <percent-bar-chart>
1110
1193
 
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.
1194
+ Bar chart for labeled rows, using D3's linear scale. Horizontal (default)
1195
+ renders stacked rows with bars growing rightward; `orientation="vertical"`
1196
+ renders side-by-side columns growing upward instead. `mode="percent"`
1197
+ (default) scales `value` against a fixed 0-100 domain and labels it with a
1198
+ `%` suffix; `mode="value"` scales it against `max` (or the largest `value`
1199
+ present) and formats it with `valueFormat`.
1113
1200
 
1114
1201
  Import: `import "@f-ewald/components/percent-bar-chart.js";`
1115
1202
 
1116
- Properties: `groups` (JS property only) : PercentBarGroup[], default []
1203
+ 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
1204
  Events: none
1118
1205
  CSS custom properties: `--ui-font`, `--ui-text-muted`
1119
1206
 
@@ -1121,10 +1208,16 @@ Example:
1121
1208
  ```html
1122
1209
  <percent-bar-chart></percent-bar-chart>
1123
1210
  <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" },
1211
+ const chart = document.querySelector("percent-bar-chart");
1212
+ chart.groups = [
1213
+ { key: "a", label: "White", value: 45.2, color: "#4f46e5" },
1214
+ { key: "b", label: "Asian", value: 28.1, color: "#0d9488" },
1127
1215
  ];
1216
+
1217
+ // Absolute values instead of percentages, as vertical columns:
1218
+ chart.mode = "value";
1219
+ chart.orientation = "vertical";
1220
+ chart.valueFormat = (value) => `$${value.toLocaleString()}`;
1128
1221
  </script>
1129
1222
  ```
1130
1223
 
@@ -1325,6 +1418,67 @@ Example:
1325
1418
  <roman-numeral value="2004"></roman-numeral>
1326
1419
  ```
1327
1420
 
1421
+ ## <scroll-to-bottom>
1422
+
1423
+ Overlay button that appears once the page (or a given `target` container)
1424
+ has scrolled more than `threshold` pixels away from the bottom edge, and
1425
+ scrolls back to the bottom on click.
1426
+
1427
+ With no `target` (default), the button is `position: fixed` to the
1428
+ viewport. When `target` is set, the button switches to `position:
1429
+ absolute` and expects to be placed as a descendant of a `position:
1430
+ relative` (or otherwise positioned) ancestor that establishes the visual
1431
+ bounds to float within — typically `target` itself, given `overflow-y:
1432
+ auto; position: relative`, so the button stays pinned to that container's
1433
+ own visible corner as its content scrolls, rather than floating over the
1434
+ whole page.
1435
+
1436
+ Import: `import "@f-ewald/components/scroll-to-bottom.js";`
1437
+
1438
+ Properties: `target` (JS property only) : HTMLElement | null, default null; `threshold` (attribute `threshold`) : number, default 200; `label` (attribute `label`) : string, default "Scroll to bottom"
1439
+ Events: `scroll-to-bottom-triggered`
1440
+ 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`
1441
+
1442
+ Example:
1443
+ ```html
1444
+ <scroll-to-bottom></scroll-to-bottom>
1445
+
1446
+ <!-- Floats inside its own scrollport instead of the whole page: -->
1447
+ <div id="log" style="position: relative; overflow-y: auto; height: 10rem">
1448
+ ...
1449
+ <scroll-to-bottom threshold="20"></scroll-to-bottom>
1450
+ </div>
1451
+ <script type="module">
1452
+ document.querySelector('scroll-to-bottom').target = document.querySelector('#log');
1453
+ </script>
1454
+ ```
1455
+
1456
+ ## <scroll-to-top>
1457
+
1458
+ Overlay button that appears once the page (or a given `target` container)
1459
+ has scrolled more than `threshold` pixels away from the top edge, and
1460
+ scrolls back to the top on click.
1461
+
1462
+ With no `target` (default), the button is `position: fixed` to the
1463
+ viewport. When `target` is set, the button switches to `position:
1464
+ absolute` and expects to be placed as a descendant of a `position:
1465
+ relative` (or otherwise positioned) ancestor that establishes the visual
1466
+ bounds to float within — typically `target` itself, given `overflow-y:
1467
+ auto; position: relative`, so the button stays pinned to that container's
1468
+ own visible corner as its content scrolls, rather than floating over the
1469
+ whole page.
1470
+
1471
+ Import: `import "@f-ewald/components/scroll-to-top.js";`
1472
+
1473
+ Properties: `target` (JS property only) : HTMLElement | null, default null; `threshold` (attribute `threshold`) : number, default 200; `label` (attribute `label`) : string, default "Scroll to top"
1474
+ Events: `scroll-to-top-triggered`
1475
+ 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`
1476
+
1477
+ Example:
1478
+ ```html
1479
+ <scroll-to-top></scroll-to-top>
1480
+ ```
1481
+
1328
1482
  ## <slide-panel>
1329
1483
 
1330
1484
  Generic sliding panel shell. Handles positioning, open/close animation,
@@ -1534,6 +1688,45 @@ Example:
1534
1688
  </script>
1535
1689
  ```
1536
1690
 
1691
+ ## <tree-view>
1692
+
1693
+ A generic, presentational tree shell: renders `nodes` recursively, one row
1694
+ per node, with each row's content produced by `renderNode` (default: plain
1695
+ label). Modeled on `data-table`'s headless pattern — knows nothing about
1696
+ what a node's `data` means beyond what `renderNode` does with it.
1697
+
1698
+ A node with a `children` array (even empty) is a folder: clicking or
1699
+ activating its row toggles expand/collapse instead of firing `node-click`.
1700
+ A node with no `children` is a leaf: clicking or activating its row fires
1701
+ `node-click`. Folders start collapsed; set `default-expanded` to start
1702
+ every folder expanded instead. Expansion state is otherwise managed
1703
+ internally and untouched by later `nodes` updates, so a user's manual
1704
+ toggles survive a data refresh.
1705
+
1706
+ Import: `import "@f-ewald/components/tree-view.js";`
1707
+
1708
+ Properties: `nodes` (JS property only) : TreeNode[], default []; `renderNode` (JS property only) : (node: TreeNode) => unknown, default —; `defaultExpanded` (attribute `default-expanded`) : boolean, default false
1709
+ Events: `node-click`
1710
+ CSS custom properties: `--ui-focus-ring`, `--ui-font`, `--ui-font-size-sm`, `--ui-radius-sm`, `--ui-surface-muted`, `--ui-text`, `--ui-text-muted`
1711
+
1712
+ Example:
1713
+ ```html
1714
+ <tree-view></tree-view>
1715
+ <script type="module">
1716
+ const tree = document.querySelector("tree-view");
1717
+ tree.nodes = [
1718
+ {
1719
+ id: "docs",
1720
+ label: "docs",
1721
+ children: [{ id: "fil_1", label: "notes.txt", data: { id: "fil_1" } }],
1722
+ },
1723
+ { id: "fil_2", label: "readme.md", data: { id: "fil_2" } },
1724
+ ];
1725
+ tree.renderNode = (node) => node.label;
1726
+ tree.addEventListener("node-click", (e) => console.log(e.detail));
1727
+ </script>
1728
+ ```
1729
+
1537
1730
  ## <ui-button>
1538
1731
 
1539
1732
  Button (or link styled as one) with an optional leading icon, in three
@@ -1565,6 +1758,30 @@ Example:
1565
1758
  <ui-button variant="secondary" href="/properties?edit=42">Edit</ui-button>
1566
1759
  ```
1567
1760
 
1761
+ ## <ui-checkbox>
1762
+
1763
+ A form-associated boolean checkbox, usable standalone or inside a native
1764
+ `<form>`. Submits `name=on` when checked (matching native
1765
+ `<input type="checkbox">` semantics) and participates fully in form
1766
+ `reset()`, ancestor `<fieldset disabled>`, and `required` validity.
1767
+
1768
+ Renders a native `<input type="checkbox">` wrapped in a `<label>`, styled
1769
+ via `:has()` on the wrapping label (matching `radio-pills`/`radio-cards`)
1770
+ rather than styling the native input directly; the checkbox itself renders
1771
+ at `1rem`, matching the existing radio-input convention.
1772
+
1773
+ Import: `import "@f-ewald/components/ui-checkbox.js";`
1774
+
1775
+ 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 ""
1776
+ Events: `change`
1777
+ 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`
1778
+
1779
+ Example:
1780
+ ```html
1781
+ <ui-checkbox label="Subscribe to updates"></ui-checkbox>
1782
+ <ui-checkbox name="terms" label="I agree to the terms" required></ui-checkbox>
1783
+ ```
1784
+
1568
1785
  ## <user-avatar>
1569
1786
 
1570
1787
  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.11.0",
4
+ "version": "1.13.0",
5
5
  "description": "A collection of universally usable web components for various tasks.",
6
6
  "type": "module",
7
7
  "main": "dist/index.js",