@f-ewald/components 0.1.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.
- package/LICENSE +28 -0
- package/README.md +147 -0
- package/custom-elements.json +3398 -0
- package/dist/address-autocomplete.d.ts +75 -0
- package/dist/address-autocomplete.d.ts.map +1 -0
- package/dist/address-autocomplete.js +342 -0
- package/dist/address-autocomplete.js.map +1 -0
- package/dist/animate-confetti.d.ts +53 -0
- package/dist/animate-confetti.d.ts.map +1 -0
- package/dist/animate-confetti.js +180 -0
- package/dist/animate-confetti.js.map +1 -0
- package/dist/autocomplete-input.d.ts +73 -0
- package/dist/autocomplete-input.d.ts.map +1 -0
- package/dist/autocomplete-input.js +309 -0
- package/dist/autocomplete-input.js.map +1 -0
- package/dist/confirm-dialog.d.ts +36 -0
- package/dist/confirm-dialog.d.ts.map +1 -0
- package/dist/confirm-dialog.js +191 -0
- package/dist/confirm-dialog.js.map +1 -0
- package/dist/copy-link-button.d.ts +27 -0
- package/dist/copy-link-button.d.ts.map +1 -0
- package/dist/copy-link-button.js +84 -0
- package/dist/copy-link-button.js.map +1 -0
- package/dist/distance-value.d.ts +24 -0
- package/dist/distance-value.d.ts.map +1 -0
- package/dist/distance-value.js +50 -0
- package/dist/distance-value.js.map +1 -0
- package/dist/distribution-chart.d.ts +52 -0
- package/dist/distribution-chart.d.ts.map +1 -0
- package/dist/distribution-chart.js +299 -0
- package/dist/distribution-chart.js.map +1 -0
- package/dist/icons.d.ts +32 -0
- package/dist/icons.d.ts.map +1 -0
- package/dist/icons.js +35 -0
- package/dist/icons.js.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/percent-bar-chart.d.ts +29 -0
- package/dist/percent-bar-chart.d.ts.map +1 -0
- package/dist/percent-bar-chart.js +92 -0
- package/dist/percent-bar-chart.js.map +1 -0
- package/dist/price-history-chart.d.ts +39 -0
- package/dist/price-history-chart.d.ts.map +1 -0
- package/dist/price-history-chart.js +217 -0
- package/dist/price-history-chart.js.map +1 -0
- package/dist/relative-time.d.ts +26 -0
- package/dist/relative-time.d.ts.map +1 -0
- package/dist/relative-time.js +64 -0
- package/dist/relative-time.js.map +1 -0
- package/dist/reveal-button.d.ts +19 -0
- package/dist/reveal-button.d.ts.map +1 -0
- package/dist/reveal-button.js +62 -0
- package/dist/reveal-button.js.map +1 -0
- package/dist/roman-numeral.d.ts +18 -0
- package/dist/roman-numeral.d.ts.map +1 -0
- package/dist/roman-numeral.js +54 -0
- package/dist/roman-numeral.js.map +1 -0
- package/dist/slide-panel.d.ts +27 -0
- package/dist/slide-panel.d.ts.map +1 -0
- package/dist/slide-panel.js +149 -0
- package/dist/slide-panel.js.map +1 -0
- package/dist/toast-notification.d.ts +53 -0
- package/dist/toast-notification.d.ts.map +1 -0
- package/dist/toast-notification.js +173 -0
- package/dist/toast-notification.js.map +1 -0
- package/dist/tokens.css +21 -0
- package/dist/tokens.d.ts +16 -0
- package/dist/tokens.d.ts.map +1 -0
- package/dist/tokens.js +79 -0
- package/dist/tokens.js.map +1 -0
- package/dist/utils/distance.d.ts +11 -0
- package/dist/utils/distance.d.ts.map +1 -0
- package/dist/utils/distance.js +25 -0
- package/dist/utils/distance.js.map +1 -0
- package/dist/utils/time.d.ts +10 -0
- package/dist/utils/time.d.ts.map +1 -0
- package/dist/utils/time.js +33 -0
- package/dist/utils/time.js.map +1 -0
- package/dist/weight-bar-chart.d.ts +29 -0
- package/dist/weight-bar-chart.d.ts.map +1 -0
- package/dist/weight-bar-chart.js +96 -0
- package/dist/weight-bar-chart.js.map +1 -0
- package/docs/address-autocomplete.md +68 -0
- package/docs/animate-confetti.md +34 -0
- package/docs/autocomplete-input.md +79 -0
- package/docs/confirm-dialog.md +65 -0
- package/docs/copy-link-button.md +46 -0
- package/docs/distance-value.md +38 -0
- package/docs/distribution-chart.md +51 -0
- package/docs/mcp-evaluation.md +31 -0
- package/docs/percent-bar-chart.md +40 -0
- package/docs/price-history-chart.md +44 -0
- package/docs/relative-time.md +37 -0
- package/docs/reveal-button.md +43 -0
- package/docs/roman-numeral.md +33 -0
- package/docs/slide-panel.md +56 -0
- package/docs/toast-notification.md +52 -0
- package/docs/weight-bar-chart.md +53 -0
- package/llms.txt +361 -0
- package/package.json +69 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Formats a distance in miles, switching to feet below 0.25 mi.
|
|
3
|
+
* At ≥10 mi precision drops to whole miles.
|
|
4
|
+
*/
|
|
5
|
+
export function formatMiles(miles) {
|
|
6
|
+
if (miles < 0.25) {
|
|
7
|
+
return `${Math.round(miles * 5280).toLocaleString("en-US")} ft`;
|
|
8
|
+
}
|
|
9
|
+
if (miles >= 10)
|
|
10
|
+
return `${Math.round(miles).toLocaleString("en-US")} mi`;
|
|
11
|
+
return `${miles.toFixed(1)} mi`;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Formats a distance in kilometers, switching to meters below 0.5 km.
|
|
15
|
+
* At ≥10 km precision drops to whole kilometers. (Future use.)
|
|
16
|
+
*/
|
|
17
|
+
export function formatKm(km) {
|
|
18
|
+
if (km < 0.5) {
|
|
19
|
+
return `${Math.round(km * 1000).toLocaleString("en-US")} m`;
|
|
20
|
+
}
|
|
21
|
+
if (km >= 10)
|
|
22
|
+
return `${Math.round(km).toLocaleString("en-US")} km`;
|
|
23
|
+
return `${km.toFixed(1)} km`;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=distance.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"distance.js","sourceRoot":"","sources":["../../src/utils/distance.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,KAAa;IACvC,IAAI,KAAK,GAAG,IAAI,EAAE,CAAC;QACjB,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,cAAc,CAAC,OAAO,CAAC,KAAK,CAAC;IAClE,CAAC;IACD,IAAI,KAAK,IAAI,EAAE;QAAE,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,cAAc,CAAC,OAAO,CAAC,KAAK,CAAC;IAC1E,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;AAClC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,QAAQ,CAAC,EAAU;IACjC,IAAI,EAAE,GAAG,GAAG,EAAE,CAAC;QACb,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC,cAAc,CAAC,OAAO,CAAC,IAAI,CAAC;IAC9D,CAAC;IACD,IAAI,EAAE,IAAI,EAAE;QAAE,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,cAAc,CAAC,OAAO,CAAC,KAAK,CAAC;IACpE,OAAO,GAAG,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;AAC/B,CAAC","sourcesContent":["/**\n * Formats a distance in miles, switching to feet below 0.25 mi.\n * At ≥10 mi precision drops to whole miles.\n */\nexport function formatMiles(miles: number): string {\n if (miles < 0.25) {\n return `${Math.round(miles * 5280).toLocaleString(\"en-US\")} ft`;\n }\n if (miles >= 10) return `${Math.round(miles).toLocaleString(\"en-US\")} mi`;\n return `${miles.toFixed(1)} mi`;\n}\n\n/**\n * Formats a distance in kilometers, switching to meters below 0.5 km.\n * At ≥10 km precision drops to whole kilometers. (Future use.)\n */\nexport function formatKm(km: number): string {\n if (km < 0.5) {\n return `${Math.round(km * 1000).toLocaleString(\"en-US\")} m`;\n }\n if (km >= 10) return `${Math.round(km).toLocaleString(\"en-US\")} km`;\n return `${km.toFixed(1)} km`;\n}\n"]}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parses a timestamp from either SQLite's `datetime('now')` (a space-separated
|
|
3
|
+
* UTC string with no zone marker, e.g. "2026-07-15 10:23:00") or a standard
|
|
4
|
+
* ISO 8601 string. JS's `Date` parser treats the space-separated form as
|
|
5
|
+
* local time, so it's coerced to `T...Z` first to anchor it to UTC.
|
|
6
|
+
*/
|
|
7
|
+
export declare function parseTimestamp(s: string): Date;
|
|
8
|
+
/** Formats `date` relative to `now` (e.g. "3 hours ago", "in 2 days"). */
|
|
9
|
+
export declare function formatRelativeTime(date: Date, now?: number): string;
|
|
10
|
+
//# sourceMappingURL=time.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"time.d.ts","sourceRoot":"","sources":["../../src/utils/time.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI,CAG9C;AAaD,0EAA0E;AAC1E,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,GAAE,MAAmB,GAAG,MAAM,CAU/E"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parses a timestamp from either SQLite's `datetime('now')` (a space-separated
|
|
3
|
+
* UTC string with no zone marker, e.g. "2026-07-15 10:23:00") or a standard
|
|
4
|
+
* ISO 8601 string. JS's `Date` parser treats the space-separated form as
|
|
5
|
+
* local time, so it's coerced to `T...Z` first to anchor it to UTC.
|
|
6
|
+
*/
|
|
7
|
+
export function parseTimestamp(s) {
|
|
8
|
+
const iso = s.includes(" ") && !s.includes("T") ? `${s.replace(" ", "T")}Z` : s;
|
|
9
|
+
return new Date(iso);
|
|
10
|
+
}
|
|
11
|
+
const RELATIVE_UNITS = [
|
|
12
|
+
["year", 365 * 24 * 60 * 60],
|
|
13
|
+
["month", 30 * 24 * 60 * 60],
|
|
14
|
+
["week", 7 * 24 * 60 * 60],
|
|
15
|
+
["day", 24 * 60 * 60],
|
|
16
|
+
["hour", 60 * 60],
|
|
17
|
+
["minute", 60],
|
|
18
|
+
];
|
|
19
|
+
const relativeFormatter = new Intl.RelativeTimeFormat(undefined, { numeric: "auto" });
|
|
20
|
+
/** Formats `date` relative to `now` (e.g. "3 hours ago", "in 2 days"). */
|
|
21
|
+
export function formatRelativeTime(date, now = Date.now()) {
|
|
22
|
+
const diffSeconds = (date.getTime() - now) / 1000;
|
|
23
|
+
const absSeconds = Math.abs(diffSeconds);
|
|
24
|
+
if (absSeconds < 60)
|
|
25
|
+
return "just now";
|
|
26
|
+
for (const [unit, secondsInUnit] of RELATIVE_UNITS) {
|
|
27
|
+
if (absSeconds >= secondsInUnit) {
|
|
28
|
+
return relativeFormatter.format(Math.round(diffSeconds / secondsInUnit), unit);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return relativeFormatter.format(Math.round(diffSeconds / 60), "minute");
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=time.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"time.js","sourceRoot":"","sources":["../../src/utils/time.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,CAAS;IACtC,MAAM,GAAG,GAAG,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IAChF,OAAO,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC;AACvB,CAAC;AAED,MAAM,cAAc,GAAiD;IACnE,CAAC,MAAM,EAAE,GAAG,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;IAC5B,CAAC,OAAO,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;IAC5B,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;IAC1B,CAAC,KAAK,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;IACrB,CAAC,MAAM,EAAE,EAAE,GAAG,EAAE,CAAC;IACjB,CAAC,QAAQ,EAAE,EAAE,CAAC;CACf,CAAC;AAEF,MAAM,iBAAiB,GAAG,IAAI,IAAI,CAAC,kBAAkB,CAAC,SAAS,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;AAEtF,0EAA0E;AAC1E,MAAM,UAAU,kBAAkB,CAAC,IAAU,EAAE,GAAG,GAAW,IAAI,CAAC,GAAG,EAAE;IACrE,MAAM,WAAW,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,GAAG,CAAC,GAAG,IAAI,CAAC;IAClD,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IACzC,IAAI,UAAU,GAAG,EAAE;QAAE,OAAO,UAAU,CAAC;IACvC,KAAK,MAAM,CAAC,IAAI,EAAE,aAAa,CAAC,IAAI,cAAc,EAAE,CAAC;QACnD,IAAI,UAAU,IAAI,aAAa,EAAE,CAAC;YAChC,OAAO,iBAAiB,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,GAAG,aAAa,CAAC,EAAE,IAAI,CAAC,CAAC;QACjF,CAAC;IACH,CAAC;IACD,OAAO,iBAAiB,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,GAAG,EAAE,CAAC,EAAE,QAAQ,CAAC,CAAC;AAC1E,CAAC","sourcesContent":["/**\n * Parses a timestamp from either SQLite's `datetime('now')` (a space-separated\n * UTC string with no zone marker, e.g. \"2026-07-15 10:23:00\") or a standard\n * ISO 8601 string. JS's `Date` parser treats the space-separated form as\n * local time, so it's coerced to `T...Z` first to anchor it to UTC.\n */\nexport function parseTimestamp(s: string): Date {\n const iso = s.includes(\" \") && !s.includes(\"T\") ? `${s.replace(\" \", \"T\")}Z` : s;\n return new Date(iso);\n}\n\nconst RELATIVE_UNITS: Array<[Intl.RelativeTimeFormatUnit, number]> = [\n [\"year\", 365 * 24 * 60 * 60],\n [\"month\", 30 * 24 * 60 * 60],\n [\"week\", 7 * 24 * 60 * 60],\n [\"day\", 24 * 60 * 60],\n [\"hour\", 60 * 60],\n [\"minute\", 60],\n];\n\nconst relativeFormatter = new Intl.RelativeTimeFormat(undefined, { numeric: \"auto\" });\n\n/** Formats `date` relative to `now` (e.g. \"3 hours ago\", \"in 2 days\"). */\nexport function formatRelativeTime(date: Date, now: number = Date.now()): string {\n const diffSeconds = (date.getTime() - now) / 1000;\n const absSeconds = Math.abs(diffSeconds);\n if (absSeconds < 60) return \"just now\";\n for (const [unit, secondsInUnit] of RELATIVE_UNITS) {\n if (absSeconds >= secondsInUnit) {\n return relativeFormatter.format(Math.round(diffSeconds / secondsInUnit), unit);\n }\n }\n return relativeFormatter.format(Math.round(diffSeconds / 60), \"minute\");\n}\n"]}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { LitElement } from "lit";
|
|
2
|
+
export interface WeightBarItem {
|
|
3
|
+
id: string;
|
|
4
|
+
label: string;
|
|
5
|
+
/** Normalized fraction (fractions across all items sum to ~1). */
|
|
6
|
+
value: number;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Sorted horizontal bar chart of labeled weights (normalized fractions
|
|
10
|
+
* summing to ~1). Bars sort descending — the order IS the priority ranking.
|
|
11
|
+
* Widths scale relative to the largest weight (which fills its track); the
|
|
12
|
+
* percent labels carry the absolute values. Rows are keyed by item id
|
|
13
|
+
* (repeat directive) so a re-render with new weights moves the existing
|
|
14
|
+
* rows; bar widths animate via CSS, re-sorting is instant.
|
|
15
|
+
*
|
|
16
|
+
* @element weight-bar-chart
|
|
17
|
+
*/
|
|
18
|
+
export declare class WeightBarChart extends LitElement {
|
|
19
|
+
/** Items to render as weighted rows, sorted descending by value. */
|
|
20
|
+
items: WeightBarItem[];
|
|
21
|
+
static styles: import("lit").CSSResult[];
|
|
22
|
+
render(): import("lit-html").TemplateResult<1> | null;
|
|
23
|
+
}
|
|
24
|
+
declare global {
|
|
25
|
+
interface HTMLElementTagNameMap {
|
|
26
|
+
"weight-bar-chart": WeightBarChart;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=weight-bar-chart.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"weight-bar-chart.d.ts","sourceRoot":"","sources":["../src/weight-bar-chart.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAa,MAAM,KAAK,CAAC;AAK5C,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,kEAAkE;IAClE,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;GASG;AACH,qBACa,cAAe,SAAQ,UAAU;IAC5C,oEAAoE;IACpC,KAAK,EAAE,aAAa,EAAE,CAAM;IAE5D,OAAgB,MAAM,4BAuCpB;IAEO,MAAM,gDAuBd;CACF;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,kBAAkB,EAAE,cAAc,CAAC;KACpC;CACF"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
import { LitElement, css, html } from "lit";
|
|
8
|
+
import { customElement, property } from "lit/decorators.js";
|
|
9
|
+
import { repeat } from "lit/directives/repeat.js";
|
|
10
|
+
import { tokens } from "./tokens.js";
|
|
11
|
+
/**
|
|
12
|
+
* Sorted horizontal bar chart of labeled weights (normalized fractions
|
|
13
|
+
* summing to ~1). Bars sort descending — the order IS the priority ranking.
|
|
14
|
+
* Widths scale relative to the largest weight (which fills its track); the
|
|
15
|
+
* percent labels carry the absolute values. Rows are keyed by item id
|
|
16
|
+
* (repeat directive) so a re-render with new weights moves the existing
|
|
17
|
+
* rows; bar widths animate via CSS, re-sorting is instant.
|
|
18
|
+
*
|
|
19
|
+
* @element weight-bar-chart
|
|
20
|
+
*/
|
|
21
|
+
let WeightBarChart = class WeightBarChart extends LitElement {
|
|
22
|
+
constructor() {
|
|
23
|
+
super(...arguments);
|
|
24
|
+
/** Items to render as weighted rows, sorted descending by value. */
|
|
25
|
+
this.items = [];
|
|
26
|
+
}
|
|
27
|
+
static { this.styles = [
|
|
28
|
+
tokens,
|
|
29
|
+
css `
|
|
30
|
+
:host {
|
|
31
|
+
display: block;
|
|
32
|
+
}
|
|
33
|
+
.row {
|
|
34
|
+
display: flex;
|
|
35
|
+
align-items: center;
|
|
36
|
+
gap: 8px;
|
|
37
|
+
margin: 6px 0;
|
|
38
|
+
}
|
|
39
|
+
.label {
|
|
40
|
+
flex: 0 0 90px;
|
|
41
|
+
font-size: var(--ui-font-size-sm, 0.75rem);
|
|
42
|
+
font-family: var(--ui-font, ui-sans-serif, system-ui, sans-serif);
|
|
43
|
+
color: var(--ui-text, #0f172a);
|
|
44
|
+
}
|
|
45
|
+
.track {
|
|
46
|
+
flex: 1 1 auto;
|
|
47
|
+
height: 12px;
|
|
48
|
+
background: var(--ui-surface-muted, #f8fafc);
|
|
49
|
+
border-radius: var(--ui-radius-sm, 0.25rem);
|
|
50
|
+
overflow: hidden;
|
|
51
|
+
}
|
|
52
|
+
.bar {
|
|
53
|
+
height: 100%;
|
|
54
|
+
background: var(--ui-primary, #4f46e5);
|
|
55
|
+
border-radius: 0 var(--ui-radius-sm, 0.25rem) var(--ui-radius-sm, 0.25rem) 0;
|
|
56
|
+
transition: width 0.15s ease-out;
|
|
57
|
+
}
|
|
58
|
+
.pct {
|
|
59
|
+
flex: 0 0 40px;
|
|
60
|
+
text-align: right;
|
|
61
|
+
font-size: var(--ui-font-size-sm, 0.75rem);
|
|
62
|
+
font-variant-numeric: tabular-nums;
|
|
63
|
+
color: var(--ui-text-muted, #64748b);
|
|
64
|
+
}
|
|
65
|
+
`,
|
|
66
|
+
]; }
|
|
67
|
+
render() {
|
|
68
|
+
if (this.items.length === 0)
|
|
69
|
+
return null;
|
|
70
|
+
// Stable sort: ties keep the incoming item order, so the chart never flickers.
|
|
71
|
+
const sorted = [...this.items].sort((a, b) => b.value - a.value);
|
|
72
|
+
const max = Math.max(0.01, ...sorted.map((item) => item.value));
|
|
73
|
+
const summary = sorted.map((item) => `${item.label} ${Math.round(item.value * 100)}%`).join(", ");
|
|
74
|
+
return html `
|
|
75
|
+
<div role="img" aria-label="Weights: ${summary}">
|
|
76
|
+
${repeat(sorted, (item) => item.id, (item) => html `
|
|
77
|
+
<div class="row">
|
|
78
|
+
<span class="label">${item.label}</span>
|
|
79
|
+
<div class="track">
|
|
80
|
+
<div class="bar" style="width: ${(item.value / max) * 100}%"></div>
|
|
81
|
+
</div>
|
|
82
|
+
<span class="pct">${Math.round(item.value * 100)}%</span>
|
|
83
|
+
</div>
|
|
84
|
+
`)}
|
|
85
|
+
</div>
|
|
86
|
+
`;
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
__decorate([
|
|
90
|
+
property({ attribute: false })
|
|
91
|
+
], WeightBarChart.prototype, "items", void 0);
|
|
92
|
+
WeightBarChart = __decorate([
|
|
93
|
+
customElement("weight-bar-chart")
|
|
94
|
+
], WeightBarChart);
|
|
95
|
+
export { WeightBarChart };
|
|
96
|
+
//# sourceMappingURL=weight-bar-chart.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"weight-bar-chart.js","sourceRoot":"","sources":["../src/weight-bar-chart.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAC;AAClD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AASrC;;;;;;;;;GASG;AAEI,IAAM,cAAc,GAApB,MAAM,cAAe,SAAQ,UAAU;IAAvC;;QACL,oEAAoE;QACpC,UAAK,GAAoB,EAAE,CAAC;IAmE9D,CAAC;aAjEiB,WAAM,GAAG;QACvB,MAAM;QACN,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAoCF;KACF,AAvCqB,CAuCpB;IAEO,MAAM;QACb,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;QACzC,+EAA+E;QAC/E,MAAM,MAAM,GAAG,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;QACjE,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;QAChE,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClG,OAAO,IAAI,CAAA;6CAC8B,OAAO;UAC1C,MAAM,CACN,MAAM,EACN,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,EACjB,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAA;;oCAEY,IAAI,CAAC,KAAK;;iDAEG,CAAC,IAAI,CAAC,KAAK,GAAG,GAAG,CAAC,GAAG,GAAG;;kCAEvC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,GAAG,GAAG,CAAC;;WAEnD,CACF;;KAEJ,CAAC;IACJ,CAAC;CACF,CAAA;AAnEiC;IAA/B,QAAQ,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;6CAA6B;AAFjD,cAAc;IAD1B,aAAa,CAAC,kBAAkB,CAAC;GACrB,cAAc,CAqE1B","sourcesContent":["import { LitElement, css, html } from \"lit\";\nimport { customElement, property } from \"lit/decorators.js\";\nimport { repeat } from \"lit/directives/repeat.js\";\nimport { tokens } from \"./tokens.js\";\n\nexport interface WeightBarItem {\n id: string;\n label: string;\n /** Normalized fraction (fractions across all items sum to ~1). */\n value: number;\n}\n\n/**\n * Sorted horizontal bar chart of labeled weights (normalized fractions\n * summing to ~1). Bars sort descending — the order IS the priority ranking.\n * Widths scale relative to the largest weight (which fills its track); the\n * percent labels carry the absolute values. Rows are keyed by item id\n * (repeat directive) so a re-render with new weights moves the existing\n * rows; bar widths animate via CSS, re-sorting is instant.\n *\n * @element weight-bar-chart\n */\n@customElement(\"weight-bar-chart\")\nexport class WeightBarChart extends LitElement {\n /** Items to render as weighted rows, sorted descending by value. */\n @property({ attribute: false }) items: WeightBarItem[] = [];\n\n static override styles = [\n tokens,\n css`\n :host {\n display: block;\n }\n .row {\n display: flex;\n align-items: center;\n gap: 8px;\n margin: 6px 0;\n }\n .label {\n flex: 0 0 90px;\n font-size: var(--ui-font-size-sm, 0.75rem);\n font-family: var(--ui-font, ui-sans-serif, system-ui, sans-serif);\n color: var(--ui-text, #0f172a);\n }\n .track {\n flex: 1 1 auto;\n height: 12px;\n background: var(--ui-surface-muted, #f8fafc);\n border-radius: var(--ui-radius-sm, 0.25rem);\n overflow: hidden;\n }\n .bar {\n height: 100%;\n background: var(--ui-primary, #4f46e5);\n border-radius: 0 var(--ui-radius-sm, 0.25rem) var(--ui-radius-sm, 0.25rem) 0;\n transition: width 0.15s ease-out;\n }\n .pct {\n flex: 0 0 40px;\n text-align: right;\n font-size: var(--ui-font-size-sm, 0.75rem);\n font-variant-numeric: tabular-nums;\n color: var(--ui-text-muted, #64748b);\n }\n `,\n ];\n\n override render() {\n if (this.items.length === 0) return null;\n // Stable sort: ties keep the incoming item order, so the chart never flickers.\n const sorted = [...this.items].sort((a, b) => b.value - a.value);\n const max = Math.max(0.01, ...sorted.map((item) => item.value));\n const summary = sorted.map((item) => `${item.label} ${Math.round(item.value * 100)}%`).join(\", \");\n return html`\n <div role=\"img\" aria-label=\"Weights: ${summary}\">\n ${repeat(\n sorted,\n (item) => item.id,\n (item) => html`\n <div class=\"row\">\n <span class=\"label\">${item.label}</span>\n <div class=\"track\">\n <div class=\"bar\" style=\"width: ${(item.value / max) * 100}%\"></div>\n </div>\n <span class=\"pct\">${Math.round(item.value * 100)}%</span>\n </div>\n `,\n )}\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"weight-bar-chart\": WeightBarChart;\n }\n}\n"]}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# `<address-autocomplete>`
|
|
2
|
+
|
|
3
|
+
Form-associated text input with a suggestion dropdown. Works as a
|
|
4
|
+
drop-in replacement for a plain `<input name="address">`: consumers keep
|
|
5
|
+
reading `new FormData(form).get("address")` and calling `form.reset()`
|
|
6
|
+
unchanged.
|
|
7
|
+
|
|
8
|
+
Supports two suggestion sources:
|
|
9
|
+
- **API** (default): fetches from `endpoint`, a Mapbox Geocoding v6-shaped
|
|
10
|
+
forward-geocode URL, debounced as the user types.
|
|
11
|
+
- **Local array**: set `suggestions` to a fixed `AddressSuggestion[]` and
|
|
12
|
+
the component filters it client-side instead of making any network
|
|
13
|
+
request. Useful for small/fixed address lists, offline use, or tests.
|
|
14
|
+
Takes priority over `endpoint` whenever it's non-null.
|
|
15
|
+
|
|
16
|
+
## Install
|
|
17
|
+
|
|
18
|
+
```js
|
|
19
|
+
import "@f-ewald/components/address-autocomplete.js";
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Usage
|
|
23
|
+
|
|
24
|
+
```html
|
|
25
|
+
<address-autocomplete
|
|
26
|
+
placeholder="Start typing an address…"
|
|
27
|
+
access-token="pk.your-mapbox-token"
|
|
28
|
+
></address-autocomplete>
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Attributes / properties
|
|
32
|
+
|
|
33
|
+
| Property | Attribute | Type | Default | Description |
|
|
34
|
+
| --- | --- | --- | --- | --- |
|
|
35
|
+
| `value` | `value` | `string` | `""` | Current input value; also the form-associated value submitted with the form. |
|
|
36
|
+
| `placeholder` | `placeholder` | `string` | `""` | Placeholder text shown when the input is empty. |
|
|
37
|
+
| `required` | `required` | `boolean` | `false` | Marks the input as required for native form validation. |
|
|
38
|
+
| `types` | `types` | `string` | `"address"` | Mapbox `types` param, e.g. "address" or "address,poi". |
|
|
39
|
+
| `endpoint` | `endpoint` | `string` | `"https://api.mapbox.com/search/geocode/v6/forward"` | Geocoding endpoint URL. Defaults to the Mapbox Geocoding v6 forward URL. |
|
|
40
|
+
| `accessToken` | `access-token` | `string` | `""` | Mapbox access token. Required for requests to succeed. |
|
|
41
|
+
| `bbox` | `bbox` | `string` | `""` | Optional bounding box bias, comma-separated `west,south,east,north`. |
|
|
42
|
+
| `proximity` | `proximity` | `string` | `""` | Optional proximity bias, comma-separated `lng,lat`. |
|
|
43
|
+
| `debounce` | `debounce` | `number` | `300` | Debounce delay (ms) before firing a geocode request after typing. |
|
|
44
|
+
| `minLength` | `min-length` | `number` | `3` | Minimum query length before suggestions are fetched. |
|
|
45
|
+
| `suggestions` | _(JS property only)_ | `AddressSuggestion[] | null` | `null` | Fixed candidate list to filter locally instead of fetching from `endpoint`. When set (non-null), no network request is ever made — this takes priority over the API mode. |
|
|
46
|
+
| `selectedSuggestion` | _(JS property only)_ | `AddressSuggestion | null` | `—` | Last-picked suggestion, or null once the input diverges from it. _(read-only)_ |
|
|
47
|
+
|
|
48
|
+
## Events
|
|
49
|
+
|
|
50
|
+
| Event | Description |
|
|
51
|
+
| --- | --- |
|
|
52
|
+
| `address-select` | A suggestion was picked; detail: AddressSuggestion. |
|
|
53
|
+
|
|
54
|
+
## Slots
|
|
55
|
+
|
|
56
|
+
_None._
|
|
57
|
+
|
|
58
|
+
## CSS custom properties
|
|
59
|
+
|
|
60
|
+
| Custom property |
|
|
61
|
+
| --- |
|
|
62
|
+
| `--ui-border` |
|
|
63
|
+
| `--ui-font` |
|
|
64
|
+
| `--ui-font-size-sm` |
|
|
65
|
+
| `--ui-radius-sm` |
|
|
66
|
+
| `--ui-shadow` |
|
|
67
|
+
| `--ui-surface` |
|
|
68
|
+
| `--ui-surface-muted` |
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# `<animate-confetti>`
|
|
2
|
+
|
|
3
|
+
Fullscreen confetti animation overlay, rendered on a fixed-position canvas.
|
|
4
|
+
Starts automatically on first render and stops after `duration` ms.
|
|
5
|
+
|
|
6
|
+
## Install
|
|
7
|
+
|
|
8
|
+
```js
|
|
9
|
+
import "@f-ewald/components/animate-confetti.js";
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Usage
|
|
13
|
+
|
|
14
|
+
```html
|
|
15
|
+
<animate-confetti duration="6000"></animate-confetti>
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Attributes / properties
|
|
19
|
+
|
|
20
|
+
| Property | Attribute | Type | Default | Description |
|
|
21
|
+
| --- | --- | --- | --- | --- |
|
|
22
|
+
| `duration` | `duration` | `number` | `6000` | How long the confetti streams before stopping, in ms. |
|
|
23
|
+
|
|
24
|
+
## Events
|
|
25
|
+
|
|
26
|
+
_None._
|
|
27
|
+
|
|
28
|
+
## Slots
|
|
29
|
+
|
|
30
|
+
_None._
|
|
31
|
+
|
|
32
|
+
## CSS custom properties
|
|
33
|
+
|
|
34
|
+
_None._
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# `<autocomplete-input>`
|
|
2
|
+
|
|
3
|
+
Generic form-associated text input with a suggestion dropdown, for any
|
|
4
|
+
"type to filter a list of `{key, value}` options" use case. Works as a
|
|
5
|
+
drop-in replacement for a plain `<input>` inside a `<form>`: set `name` on
|
|
6
|
+
the element itself and consumers keep reading
|
|
7
|
+
`new FormData(form).get(name)` and calling `form.reset()` unchanged — the
|
|
8
|
+
submitted value is the picked option's `value`, while `key` is available
|
|
9
|
+
via the `option-select` event and the `selectedOption` getter for cases
|
|
10
|
+
that need the underlying id rather than the display text.
|
|
11
|
+
|
|
12
|
+
Supports two suggestion sources:
|
|
13
|
+
- **API**: set `endpoint` to a URL that, given a `?<query-param>=<text>`
|
|
14
|
+
query string, responds with a JSON array of `{key, value}` objects.
|
|
15
|
+
Requests are debounced as the user types.
|
|
16
|
+
- **Local array**: set `options` to a fixed `AutocompleteOption[]` and the
|
|
17
|
+
component filters it client-side instead of making any network
|
|
18
|
+
request. Useful for small/fixed lists, offline use, or tests. Takes
|
|
19
|
+
priority over `endpoint` whenever it's non-null.
|
|
20
|
+
|
|
21
|
+
## Install
|
|
22
|
+
|
|
23
|
+
```js
|
|
24
|
+
import "@f-ewald/components/autocomplete-input.js";
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Usage
|
|
28
|
+
|
|
29
|
+
```html
|
|
30
|
+
<form>
|
|
31
|
+
<autocomplete-input name="language" placeholder="Start typing a language…"></autocomplete-input>
|
|
32
|
+
</form>
|
|
33
|
+
<script type="module">
|
|
34
|
+
// Local mode: filters client-side, no network request.
|
|
35
|
+
document.querySelector("autocomplete-input").options = [
|
|
36
|
+
{ key: "ts", value: "TypeScript" },
|
|
37
|
+
{ key: "py", value: "Python" },
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
// API mode: omit `options` and set `endpoint` instead — it's queried as
|
|
41
|
+
// `${endpoint}?${queryParam}=<text>` and must respond with [{key, value}].
|
|
42
|
+
</script>
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Attributes / properties
|
|
46
|
+
|
|
47
|
+
| Property | Attribute | Type | Default | Description |
|
|
48
|
+
| --- | --- | --- | --- | --- |
|
|
49
|
+
| `value` | `value` | `string` | `""` | Current input value; also the form-associated value submitted with the form. |
|
|
50
|
+
| `placeholder` | `placeholder` | `string` | `""` | Placeholder text shown when the input is empty. |
|
|
51
|
+
| `required` | `required` | `boolean` | `false` | Marks the input as required for native form validation. |
|
|
52
|
+
| `endpoint` | `endpoint` | `string` | `""` | API endpoint queried in API mode. Ignored when `options` is set. |
|
|
53
|
+
| `queryParam` | `query-param` | `string` | `"q"` | Query string parameter name the current input text is sent under. |
|
|
54
|
+
| `debounce` | `debounce` | `number` | `300` | Debounce delay (ms) before firing a request/filter after typing. |
|
|
55
|
+
| `minLength` | `min-length` | `number` | `3` | Minimum query length before suggestions are shown. |
|
|
56
|
+
| `options` | _(JS property only)_ | `AutocompleteOption[] | null` | `null` | Fixed candidate list to filter locally instead of fetching from `endpoint`. When set (non-null), no network request is ever made — this takes priority over the API mode. |
|
|
57
|
+
| `selectedOption` | _(JS property only)_ | `AutocompleteOption | null` | `—` | Last-picked option, or null once the input diverges from it. _(read-only)_ |
|
|
58
|
+
|
|
59
|
+
## Events
|
|
60
|
+
|
|
61
|
+
| Event | Description |
|
|
62
|
+
| --- | --- |
|
|
63
|
+
| `option-select` | An option was picked; detail: AutocompleteOption. |
|
|
64
|
+
|
|
65
|
+
## Slots
|
|
66
|
+
|
|
67
|
+
_None._
|
|
68
|
+
|
|
69
|
+
## CSS custom properties
|
|
70
|
+
|
|
71
|
+
| Custom property |
|
|
72
|
+
| --- |
|
|
73
|
+
| `--ui-border` |
|
|
74
|
+
| `--ui-font` |
|
|
75
|
+
| `--ui-font-size-sm` |
|
|
76
|
+
| `--ui-radius-sm` |
|
|
77
|
+
| `--ui-shadow` |
|
|
78
|
+
| `--ui-surface` |
|
|
79
|
+
| `--ui-surface-muted` |
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# `<confirm-dialog>`
|
|
2
|
+
|
|
3
|
+
Reusable confirmation dialog: overlay + centered card with a slotted body,
|
|
4
|
+
an optional error line, and Cancel/Confirm actions. Instant `display:none`
|
|
5
|
+
→ `display:flex` toggle (no transitions). Fires `confirm`/`cancel`
|
|
6
|
+
(bubbling, composed) instead of owning any deletion logic itself —
|
|
7
|
+
callers stay in charge of the request.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```js
|
|
12
|
+
import "@f-ewald/components/confirm-dialog.js";
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
```html
|
|
18
|
+
<confirm-dialog open confirm-label="Delete" cancel-label="Cancel">
|
|
19
|
+
Are you sure you want to delete this item?
|
|
20
|
+
</confirm-dialog>
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Attributes / properties
|
|
24
|
+
|
|
25
|
+
| Property | Attribute | Type | Default | Description |
|
|
26
|
+
| --- | --- | --- | --- | --- |
|
|
27
|
+
| `open` | `open` | `boolean` | `false` | Whether the dialog is visible. |
|
|
28
|
+
| `confirmLabel` | `confirm-label` | `string` | `"Delete"` | Label for the confirm button. |
|
|
29
|
+
| `cancelLabel` | `cancel-label` | `string` | `"Cancel"` | Label for the cancel button. |
|
|
30
|
+
| `danger` | `danger` | `boolean` | `true` | Danger (red) vs. primary (indigo) styling for the confirm button. |
|
|
31
|
+
| `busy` | `busy` | `boolean` | `false` | Shows a spinner and disables both buttons while a request is in flight. |
|
|
32
|
+
| `error` | `error` | `string | null` | `null` | Inline error line shown below the body, or null for none. |
|
|
33
|
+
|
|
34
|
+
## Events
|
|
35
|
+
|
|
36
|
+
| Event | Description |
|
|
37
|
+
| --- | --- |
|
|
38
|
+
| `confirm` | User clicked the confirm button. |
|
|
39
|
+
| `cancel` | User clicked the cancel button. |
|
|
40
|
+
|
|
41
|
+
## Slots
|
|
42
|
+
|
|
43
|
+
| Slot | Description |
|
|
44
|
+
| --- | --- |
|
|
45
|
+
| `(default)` | Dialog body content. |
|
|
46
|
+
|
|
47
|
+
## CSS custom properties
|
|
48
|
+
|
|
49
|
+
| Custom property |
|
|
50
|
+
| --- |
|
|
51
|
+
| `--ui-border` |
|
|
52
|
+
| `--ui-danger` |
|
|
53
|
+
| `--ui-danger-hover` |
|
|
54
|
+
| `--ui-font` |
|
|
55
|
+
| `--ui-font-size` |
|
|
56
|
+
| `--ui-font-size-sm` |
|
|
57
|
+
| `--ui-overlay` |
|
|
58
|
+
| `--ui-primary` |
|
|
59
|
+
| `--ui-primary-hover` |
|
|
60
|
+
| `--ui-radius` |
|
|
61
|
+
| `--ui-radius-sm` |
|
|
62
|
+
| `--ui-shadow-lg` |
|
|
63
|
+
| `--ui-surface` |
|
|
64
|
+
| `--ui-text` |
|
|
65
|
+
| `--ui-text-muted` |
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# `<copy-link-button>`
|
|
2
|
+
|
|
3
|
+
Small icon button that copies `value` to the clipboard and shows a toast
|
|
4
|
+
on success/failure (if a `<toast-notification>` element is present), and
|
|
5
|
+
always dispatches a `copy-success`/`copy-error` CustomEvent so consumers
|
|
6
|
+
without a toast element can react. Defaults to the current page URL if
|
|
7
|
+
`value` is unset.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```js
|
|
12
|
+
import "@f-ewald/components/copy-link-button.js";
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
```html
|
|
18
|
+
<copy-link-button value="https://example.com/listing/42" label="Copy listing link"></copy-link-button>
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Attributes / properties
|
|
22
|
+
|
|
23
|
+
| Property | Attribute | Type | Default | Description |
|
|
24
|
+
| --- | --- | --- | --- | --- |
|
|
25
|
+
| `value` | `value` | `string` | `""` | Text to copy. Defaults to `window.location.href` at click time. |
|
|
26
|
+
| `label` | `label` | `string` | `"Copy link"` | Accessible label / tooltip text. |
|
|
27
|
+
|
|
28
|
+
## Events
|
|
29
|
+
|
|
30
|
+
| Event | Description |
|
|
31
|
+
| --- | --- |
|
|
32
|
+
| `copy-success` | The value was copied to the clipboard. |
|
|
33
|
+
| `copy-error` | Copying to the clipboard failed. |
|
|
34
|
+
|
|
35
|
+
## Slots
|
|
36
|
+
|
|
37
|
+
_None._
|
|
38
|
+
|
|
39
|
+
## CSS custom properties
|
|
40
|
+
|
|
41
|
+
| Custom property |
|
|
42
|
+
| --- |
|
|
43
|
+
| `--ui-radius-sm` |
|
|
44
|
+
| `--ui-surface-muted` |
|
|
45
|
+
| `--ui-text` |
|
|
46
|
+
| `--ui-text-muted` |
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# `<distance-value>`
|
|
2
|
+
|
|
3
|
+
Inline distance display. Renders miles/feet or km/m, switching units at
|
|
4
|
+
sensible thresholds (< 0.25 mi → ft; < 0.5 km → m).
|
|
5
|
+
|
|
6
|
+
Supply exactly one of `miles` or `km`; the other stays null.
|
|
7
|
+
km support is present but reserved for future use.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```js
|
|
12
|
+
import "@f-ewald/components/distance-value.js";
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
```html
|
|
18
|
+
<distance-value miles="5"></distance-value>
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Attributes / properties
|
|
22
|
+
|
|
23
|
+
| Property | Attribute | Type | Default | Description |
|
|
24
|
+
| --- | --- | --- | --- | --- |
|
|
25
|
+
| `miles` | `miles` | `number | null` | `null` | Distance in miles (imperial). Switches to feet below 0.25 mi. |
|
|
26
|
+
| `km` | `km` | `number | null` | `null` | Distance in kilometers (metric, future). Switches to meters below 0.5 km. |
|
|
27
|
+
|
|
28
|
+
## Events
|
|
29
|
+
|
|
30
|
+
_None._
|
|
31
|
+
|
|
32
|
+
## Slots
|
|
33
|
+
|
|
34
|
+
_None._
|
|
35
|
+
|
|
36
|
+
## CSS custom properties
|
|
37
|
+
|
|
38
|
+
_None._
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# `<distribution-chart>`
|
|
2
|
+
|
|
3
|
+
Renders a KDE distribution curve for a named metric with one or more value
|
|
4
|
+
markers. The SVG viewBox is kept in sync with the element's pixel width via
|
|
5
|
+
ResizeObserver so that font sizes and stroke widths are always in real pixels
|
|
6
|
+
regardless of container width.
|
|
7
|
+
|
|
8
|
+
Pass `fontSize` to control all text size (default 11). Pass a single
|
|
9
|
+
`{label:'', value}` for a single-value display or multiple
|
|
10
|
+
`{label:'A'|'B'|...}` entries to compare several values.
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
```js
|
|
15
|
+
import "@f-ewald/components/distribution-chart.js";
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Usage
|
|
19
|
+
|
|
20
|
+
```html
|
|
21
|
+
<distribution-chart metric="sqft"></distribution-chart>
|
|
22
|
+
<script type="module">
|
|
23
|
+
document.querySelector("distribution-chart").values = [{ label: "", value: 1450 }];
|
|
24
|
+
</script>
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Attributes / properties
|
|
28
|
+
|
|
29
|
+
| Property | Attribute | Type | Default | Description |
|
|
30
|
+
| --- | --- | --- | --- | --- |
|
|
31
|
+
| `metric` | `metric` | `string` | `""` | Metric name, fetched from `/api/distribution/<metric>` on change. |
|
|
32
|
+
| `values` | _(JS property only)_ | `DistributionValue[]` | `[]` | One or more values to mark on the distribution curve. |
|
|
33
|
+
| `markerColors` | _(JS property only)_ | `string[]` | `["#4f46e5", "#d97706", "#0d9488", "#e11d48"]` | Colors assigned to markers in order. Defaults to indigo/amber/teal/rose 600s. |
|
|
34
|
+
| `fontSize` | `font-size` | `number` | `11` | Target font size in CSS pixels (default 11). Always renders at this size regardless of container width. |
|
|
35
|
+
|
|
36
|
+
## Events
|
|
37
|
+
|
|
38
|
+
_None._
|
|
39
|
+
|
|
40
|
+
## Slots
|
|
41
|
+
|
|
42
|
+
_None._
|
|
43
|
+
|
|
44
|
+
## CSS custom properties
|
|
45
|
+
|
|
46
|
+
| Custom property |
|
|
47
|
+
| --- |
|
|
48
|
+
| `--ui-danger` |
|
|
49
|
+
| `--ui-font-size-sm` |
|
|
50
|
+
| `--ui-radius-sm` |
|
|
51
|
+
| `--ui-text-muted` |
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# MCP server evaluation
|
|
2
|
+
|
|
3
|
+
**Recommendation: do not build an MCP server for this package now.**
|
|
4
|
+
|
|
5
|
+
## Rationale
|
|
6
|
+
|
|
7
|
+
The component catalog is small (14 tags). `llms.txt` ships in the npm
|
|
8
|
+
package and is fetchable directly from unpkg or GitHub raw, so any LLM or
|
|
9
|
+
agent can load the full API surface — every component's attributes,
|
|
10
|
+
properties, events, slots, tokens, and a usage example — in one small file,
|
|
11
|
+
with no server, process, or additional versioning/packaging burden. An MCP
|
|
12
|
+
server would duplicate that same data behind a stdio process that needs its
|
|
13
|
+
own install, auth-free-but-still-a-process lifecycle, and release cadence
|
|
14
|
+
kept in sync with the package — for zero functional gain at this scale.
|
|
15
|
+
|
|
16
|
+
## When to revisit
|
|
17
|
+
|
|
18
|
+
- The catalog grows past a size where `llms.txt` stops being comfortable to
|
|
19
|
+
load into a single context window (rule of thumb: ~30+ components).
|
|
20
|
+
- Consumers want live search, fuzzy matching, or filtered/paginated results
|
|
21
|
+
instead of "load everything."
|
|
22
|
+
- Consumers want examples or docs that go beyond what's derivable from
|
|
23
|
+
`custom-elements.json` (e.g. runnable playground snippets, live preview).
|
|
24
|
+
|
|
25
|
+
## Upgrade path
|
|
26
|
+
|
|
27
|
+
If/when it's warranted, a thin stdio MCP server exposing two tools —
|
|
28
|
+
`list_components` and `get_component_docs(tag)` — backed by the same
|
|
29
|
+
`custom-elements.json` this package already generates and ships. No new
|
|
30
|
+
data source would be needed; the server would just be a different transport
|
|
31
|
+
over data that already exists.
|