@star-ng/ui 0.0.0-stage → 0.2.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 +21 -0
- package/README.md +162 -2
- package/fesm2022/star-ng-ui-accordion.mjs +376 -0
- package/fesm2022/star-ng-ui-accordion.mjs.map +1 -0
- package/fesm2022/star-ng-ui-autocomplete.mjs +786 -0
- package/fesm2022/star-ng-ui-autocomplete.mjs.map +1 -0
- package/fesm2022/star-ng-ui-avatar.mjs +154 -0
- package/fesm2022/star-ng-ui-avatar.mjs.map +1 -0
- package/fesm2022/star-ng-ui-badge.mjs +153 -0
- package/fesm2022/star-ng-ui-badge.mjs.map +1 -0
- package/fesm2022/star-ng-ui-breadcrumb.mjs +199 -0
- package/fesm2022/star-ng-ui-breadcrumb.mjs.map +1 -0
- package/fesm2022/star-ng-ui-button.mjs +282 -0
- package/fesm2022/star-ng-ui-button.mjs.map +1 -0
- package/fesm2022/star-ng-ui-card.mjs +112 -0
- package/fesm2022/star-ng-ui-card.mjs.map +1 -0
- package/fesm2022/star-ng-ui-chart.mjs +1904 -0
- package/fesm2022/star-ng-ui-chart.mjs.map +1 -0
- package/fesm2022/star-ng-ui-checkbox.mjs +345 -0
- package/fesm2022/star-ng-ui-checkbox.mjs.map +1 -0
- package/fesm2022/star-ng-ui-core.mjs +484 -0
- package/fesm2022/star-ng-ui-core.mjs.map +1 -0
- package/fesm2022/star-ng-ui-data-display.mjs +1870 -0
- package/fesm2022/star-ng-ui-data-display.mjs.map +1 -0
- package/fesm2022/star-ng-ui-date-picker.mjs +2786 -0
- package/fesm2022/star-ng-ui-date-picker.mjs.map +1 -0
- package/fesm2022/star-ng-ui-dialog.mjs +948 -0
- package/fesm2022/star-ng-ui-dialog.mjs.map +1 -0
- package/fesm2022/star-ng-ui-drawer.mjs +158 -0
- package/fesm2022/star-ng-ui-drawer.mjs.map +1 -0
- package/fesm2022/star-ng-ui-editor.mjs +416 -0
- package/fesm2022/star-ng-ui-editor.mjs.map +1 -0
- package/fesm2022/star-ng-ui-field.mjs +378 -0
- package/fesm2022/star-ng-ui-field.mjs.map +1 -0
- package/fesm2022/star-ng-ui-file-upload.mjs +762 -0
- package/fesm2022/star-ng-ui-file-upload.mjs.map +1 -0
- package/fesm2022/star-ng-ui-i18n-de.mjs +179 -0
- package/fesm2022/star-ng-ui-i18n-de.mjs.map +1 -0
- package/fesm2022/star-ng-ui-i18n-es.mjs +179 -0
- package/fesm2022/star-ng-ui-i18n-es.mjs.map +1 -0
- package/fesm2022/star-ng-ui-input-group.mjs +188 -0
- package/fesm2022/star-ng-ui-input-group.mjs.map +1 -0
- package/fesm2022/star-ng-ui-input-mask.mjs +434 -0
- package/fesm2022/star-ng-ui-input-mask.mjs.map +1 -0
- package/fesm2022/star-ng-ui-input-number.mjs +691 -0
- package/fesm2022/star-ng-ui-input-number.mjs.map +1 -0
- package/fesm2022/star-ng-ui-input-otp.mjs +343 -0
- package/fesm2022/star-ng-ui-input-otp.mjs.map +1 -0
- package/fesm2022/star-ng-ui-input-tags.mjs +616 -0
- package/fesm2022/star-ng-ui-input-tags.mjs.map +1 -0
- package/fesm2022/star-ng-ui-knob.mjs +514 -0
- package/fesm2022/star-ng-ui-knob.mjs.map +1 -0
- package/fesm2022/star-ng-ui-layout.mjs +608 -0
- package/fesm2022/star-ng-ui-layout.mjs.map +1 -0
- package/fesm2022/star-ng-ui-listbox.mjs +886 -0
- package/fesm2022/star-ng-ui-listbox.mjs.map +1 -0
- package/fesm2022/star-ng-ui-media.mjs +833 -0
- package/fesm2022/star-ng-ui-media.mjs.map +1 -0
- package/fesm2022/star-ng-ui-menu.mjs +1840 -0
- package/fesm2022/star-ng-ui-menu.mjs.map +1 -0
- package/fesm2022/star-ng-ui-message.mjs +191 -0
- package/fesm2022/star-ng-ui-message.mjs.map +1 -0
- package/fesm2022/star-ng-ui-paginator.mjs +387 -0
- package/fesm2022/star-ng-ui-paginator.mjs.map +1 -0
- package/fesm2022/star-ng-ui-panel.mjs +324 -0
- package/fesm2022/star-ng-ui-panel.mjs.map +1 -0
- package/fesm2022/star-ng-ui-password.mjs +517 -0
- package/fesm2022/star-ng-ui-password.mjs.map +1 -0
- package/fesm2022/star-ng-ui-popover.mjs +241 -0
- package/fesm2022/star-ng-ui-popover.mjs.map +1 -0
- package/fesm2022/star-ng-ui-progress-bar.mjs +183 -0
- package/fesm2022/star-ng-ui-progress-bar.mjs.map +1 -0
- package/fesm2022/star-ng-ui-radio.mjs +411 -0
- package/fesm2022/star-ng-ui-radio.mjs.map +1 -0
- package/fesm2022/star-ng-ui-rating.mjs +211 -0
- package/fesm2022/star-ng-ui-rating.mjs.map +1 -0
- package/fesm2022/star-ng-ui-select-button.mjs +613 -0
- package/fesm2022/star-ng-ui-select-button.mjs.map +1 -0
- package/fesm2022/star-ng-ui-select.mjs +2547 -0
- package/fesm2022/star-ng-ui-select.mjs.map +1 -0
- package/fesm2022/star-ng-ui-skeleton.mjs +160 -0
- package/fesm2022/star-ng-ui-skeleton.mjs.map +1 -0
- package/fesm2022/star-ng-ui-slider.mjs +552 -0
- package/fesm2022/star-ng-ui-slider.mjs.map +1 -0
- package/fesm2022/star-ng-ui-spinner.mjs +148 -0
- package/fesm2022/star-ng-ui-spinner.mjs.map +1 -0
- package/fesm2022/star-ng-ui-stepper.mjs +360 -0
- package/fesm2022/star-ng-ui-stepper.mjs.map +1 -0
- package/fesm2022/star-ng-ui-table.mjs +3531 -0
- package/fesm2022/star-ng-ui-table.mjs.map +1 -0
- package/fesm2022/star-ng-ui-tabs.mjs +464 -0
- package/fesm2022/star-ng-ui-tabs.mjs.map +1 -0
- package/fesm2022/star-ng-ui-tag.mjs +129 -0
- package/fesm2022/star-ng-ui-tag.mjs.map +1 -0
- package/fesm2022/star-ng-ui-theme-switch.mjs +105 -0
- package/fesm2022/star-ng-ui-theme-switch.mjs.map +1 -0
- package/fesm2022/star-ng-ui-theme.mjs +98 -0
- package/fesm2022/star-ng-ui-theme.mjs.map +1 -0
- package/fesm2022/star-ng-ui-toast.mjs +298 -0
- package/fesm2022/star-ng-ui-toast.mjs.map +1 -0
- package/fesm2022/star-ng-ui-toggle-switch.mjs +316 -0
- package/fesm2022/star-ng-ui-toggle-switch.mjs.map +1 -0
- package/fesm2022/star-ng-ui-tooltip.mjs +297 -0
- package/fesm2022/star-ng-ui-tooltip.mjs.map +1 -0
- package/fesm2022/star-ng-ui-transloco.mjs +40 -0
- package/fesm2022/star-ng-ui-transloco.mjs.map +1 -0
- package/fesm2022/star-ng-ui-tree.mjs +1446 -0
- package/fesm2022/star-ng-ui-tree.mjs.map +1 -0
- package/fesm2022/star-ng-ui-utility.mjs +2134 -0
- package/fesm2022/star-ng-ui-utility.mjs.map +1 -0
- package/fesm2022/star-ng-ui.mjs +51 -0
- package/package.json +285 -3
- package/styles/_base.scss +70 -0
- package/styles/_shared.scss +2 -0
- package/styles/_tokens.scss +190 -0
- package/styles/controls/_accordion.scss +75 -0
- package/styles/controls/_autocomplete.scss +44 -0
- package/styles/controls/_avatar.scss +39 -0
- package/styles/controls/_breadcrumb.scss +45 -0
- package/styles/controls/_button.scss +137 -0
- package/styles/controls/_card.scss +38 -0
- package/styles/controls/_chart.scss +214 -0
- package/styles/controls/_chip.scss +51 -0
- package/styles/controls/_choice.scss +153 -0
- package/styles/controls/_data-display.scss +124 -0
- package/styles/controls/_date-picker.scss +300 -0
- package/styles/controls/_dialog.scss +123 -0
- package/styles/controls/_drawer.scss +118 -0
- package/styles/controls/_editor.scss +74 -0
- package/styles/controls/_field.scss +92 -0
- package/styles/controls/_file-upload.scss +136 -0
- package/styles/controls/_input-group.scss +104 -0
- package/styles/controls/_input-mask.scss +46 -0
- package/styles/controls/_input-number.scss +125 -0
- package/styles/controls/_knob.scss +76 -0
- package/styles/controls/_layout.scss +182 -0
- package/styles/controls/_listbox.scss +38 -0
- package/styles/controls/_media.scss +222 -0
- package/styles/controls/_menu.scss +254 -0
- package/styles/controls/_message.scss +48 -0
- package/styles/controls/_org-chart.scss +128 -0
- package/styles/controls/_paginator.scss +59 -0
- package/styles/controls/_panel.scss +83 -0
- package/styles/controls/_popover.scss +40 -0
- package/styles/controls/_progress-bar.scss +74 -0
- package/styles/controls/_select-button.scss +61 -0
- package/styles/controls/_select.scss +216 -0
- package/styles/controls/_skeleton.scss +43 -0
- package/styles/controls/_slider.scss +163 -0
- package/styles/controls/_spinner.scss +34 -0
- package/styles/controls/_stepper.scss +109 -0
- package/styles/controls/_table.scss +523 -0
- package/styles/controls/_tabs.scss +93 -0
- package/styles/controls/_tag.scss +84 -0
- package/styles/controls/_toast.scss +141 -0
- package/styles/controls/_tooltip.scss +14 -0
- package/styles/controls/_tree.scss +113 -0
- package/styles/controls/_utility.scss +654 -0
- package/styles/star-ng.css +5807 -0
- package/styles/star-ng.min.css +1 -0
- package/styles/star-ng.scss +50 -0
- package/theme/star-theme-first-paint.js +1 -0
- package/types/star-ng-ui-accordion.d.ts +82 -0
- package/types/star-ng-ui-autocomplete.d.ts +171 -0
- package/types/star-ng-ui-avatar.d.ts +43 -0
- package/types/star-ng-ui-badge.d.ts +46 -0
- package/types/star-ng-ui-breadcrumb.d.ts +39 -0
- package/types/star-ng-ui-button.d.ts +79 -0
- package/types/star-ng-ui-card.d.ts +29 -0
- package/types/star-ng-ui-chart.d.ts +337 -0
- package/types/star-ng-ui-checkbox.d.ts +92 -0
- package/types/star-ng-ui-core.d.ts +373 -0
- package/types/star-ng-ui-data-display.d.ts +438 -0
- package/types/star-ng-ui-date-picker.d.ts +701 -0
- package/types/star-ng-ui-dialog.d.ts +254 -0
- package/types/star-ng-ui-drawer.d.ts +29 -0
- package/types/star-ng-ui-editor.d.ts +96 -0
- package/types/star-ng-ui-field.d.ts +146 -0
- package/types/star-ng-ui-file-upload.d.ts +192 -0
- package/types/star-ng-ui-i18n-de.d.ts +7 -0
- package/types/star-ng-ui-i18n-es.d.ts +7 -0
- package/types/star-ng-ui-input-group.d.ts +58 -0
- package/types/star-ng-ui-input-mask.d.ts +126 -0
- package/types/star-ng-ui-input-number.d.ts +193 -0
- package/types/star-ng-ui-input-otp.d.ts +86 -0
- package/types/star-ng-ui-input-tags.d.ts +159 -0
- package/types/star-ng-ui-knob.d.ts +121 -0
- package/types/star-ng-ui-layout.d.ts +154 -0
- package/types/star-ng-ui-listbox.d.ts +170 -0
- package/types/star-ng-ui-media.d.ts +161 -0
- package/types/star-ng-ui-menu.d.ts +332 -0
- package/types/star-ng-ui-message.d.ts +52 -0
- package/types/star-ng-ui-paginator.d.ts +85 -0
- package/types/star-ng-ui-panel.d.ts +65 -0
- package/types/star-ng-ui-password.d.ts +129 -0
- package/types/star-ng-ui-popover.d.ts +57 -0
- package/types/star-ng-ui-progress-bar.d.ts +49 -0
- package/types/star-ng-ui-radio.d.ts +116 -0
- package/types/star-ng-ui-rating.d.ts +52 -0
- package/types/star-ng-ui-select-button.d.ts +169 -0
- package/types/star-ng-ui-select.d.ts +563 -0
- package/types/star-ng-ui-skeleton.d.ts +47 -0
- package/types/star-ng-ui-slider.d.ts +145 -0
- package/types/star-ng-ui-spinner.d.ts +29 -0
- package/types/star-ng-ui-stepper.d.ts +89 -0
- package/types/star-ng-ui-table.d.ts +885 -0
- package/types/star-ng-ui-tabs.d.ts +109 -0
- package/types/star-ng-ui-tag.d.ts +35 -0
- package/types/star-ng-ui-theme-switch.d.ts +36 -0
- package/types/star-ng-ui-theme.d.ts +95 -0
- package/types/star-ng-ui-toast.d.ts +84 -0
- package/types/star-ng-ui-toggle-switch.d.ts +81 -0
- package/types/star-ng-ui-tooltip.d.ts +66 -0
- package/types/star-ng-ui-transloco.d.ts +14 -0
- package/types/star-ng-ui-tree.d.ts +343 -0
- package/types/star-ng-ui-utility.d.ts +533 -0
- package/types/star-ng-ui.d.ts +51 -0
|
@@ -0,0 +1,484 @@
|
|
|
1
|
+
import { Directionality } from "@angular/cdk/bidi";
|
|
2
|
+
import { DOCUMENT } from "@angular/common";
|
|
3
|
+
import * as i0 from "@angular/core";
|
|
4
|
+
import { DestroyRef, Injectable, InjectionToken, Injector, effect, inject, provideEnvironmentInitializer, signal, untracked } from "@angular/core";
|
|
5
|
+
import { FormArray, FormGroup } from "@angular/forms";
|
|
6
|
+
import { SignalFormControl } from "@angular/forms/signals/compat";
|
|
7
|
+
function starDirectionOf(element) {
|
|
8
|
+
if (!element) return "ltr";
|
|
9
|
+
const attribute = element.closest("[dir]")?.getAttribute("dir")?.toLowerCase();
|
|
10
|
+
if (attribute === "rtl" || attribute === "ltr") return attribute;
|
|
11
|
+
return element.ownerDocument.defaultView?.getComputedStyle(element).direction === "rtl" ? "rtl" : "ltr";
|
|
12
|
+
}
|
|
13
|
+
function starIsRtl(element) {
|
|
14
|
+
return starDirectionOf(element) === "rtl";
|
|
15
|
+
}
|
|
16
|
+
function starInlineKey(event, element) {
|
|
17
|
+
if (event.key !== "ArrowLeft" && event.key !== "ArrowRight") return event.key;
|
|
18
|
+
if (!starIsRtl(element ?? (event.currentTarget instanceof Element ? event.currentTarget : null) ?? (event.target instanceof Element ? event.target : null))) return event.key;
|
|
19
|
+
return event.key === "ArrowLeft" ? "ArrowRight" : "ArrowLeft";
|
|
20
|
+
}
|
|
21
|
+
var StarDirectionService = class StarDirectionService {
|
|
22
|
+
document = inject(DOCUMENT);
|
|
23
|
+
directionality = inject(Directionality, { optional: true });
|
|
24
|
+
current = signal(this.read(), ...ngDevMode ? [{ debugName: "current" }] : /* istanbul ignore next */ []);
|
|
25
|
+
value = this.current.asReadonly();
|
|
26
|
+
constructor() {
|
|
27
|
+
this.sync();
|
|
28
|
+
const view = this.document.defaultView;
|
|
29
|
+
if (!view || typeof view.MutationObserver !== "function") return;
|
|
30
|
+
const observer = new view.MutationObserver(() => this.refresh());
|
|
31
|
+
const options = {
|
|
32
|
+
attributes: true,
|
|
33
|
+
attributeFilter: ["dir"]
|
|
34
|
+
};
|
|
35
|
+
observer.observe(this.document.documentElement, options);
|
|
36
|
+
if (this.document.body) observer.observe(this.document.body, options);
|
|
37
|
+
inject(DestroyRef).onDestroy(() => observer.disconnect());
|
|
38
|
+
}
|
|
39
|
+
refresh() {
|
|
40
|
+
const next = this.read();
|
|
41
|
+
if (next === this.current()) return;
|
|
42
|
+
this.current.set(next);
|
|
43
|
+
this.sync();
|
|
44
|
+
}
|
|
45
|
+
read() {
|
|
46
|
+
const raw = (this.document.body?.dir || this.document.documentElement.dir || "ltr").toLowerCase();
|
|
47
|
+
if (raw === "auto") return this.document.defaultView?.getComputedStyle(this.document.documentElement).direction === "rtl" ? "rtl" : "ltr";
|
|
48
|
+
return raw === "rtl" ? "rtl" : "ltr";
|
|
49
|
+
}
|
|
50
|
+
sync() {
|
|
51
|
+
const target = this.directionality;
|
|
52
|
+
if (!target || target.valueSignal() === this.current()) return;
|
|
53
|
+
target.valueSignal.set(this.current());
|
|
54
|
+
target.change.emit(this.current());
|
|
55
|
+
}
|
|
56
|
+
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
57
|
+
minVersion: "12.0.0",
|
|
58
|
+
version: "22.2.6",
|
|
59
|
+
ngImport: i0,
|
|
60
|
+
type: StarDirectionService,
|
|
61
|
+
deps: [],
|
|
62
|
+
target: i0.ɵɵFactoryTarget.Injectable
|
|
63
|
+
});
|
|
64
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({
|
|
65
|
+
minVersion: "12.0.0",
|
|
66
|
+
version: "22.2.6",
|
|
67
|
+
ngImport: i0,
|
|
68
|
+
type: StarDirectionService,
|
|
69
|
+
providedIn: "root"
|
|
70
|
+
});
|
|
71
|
+
};
|
|
72
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
73
|
+
minVersion: "12.0.0",
|
|
74
|
+
version: "22.2.6",
|
|
75
|
+
ngImport: i0,
|
|
76
|
+
type: StarDirectionService,
|
|
77
|
+
decorators: [{
|
|
78
|
+
type: Injectable,
|
|
79
|
+
args: [{ providedIn: "root" }]
|
|
80
|
+
}],
|
|
81
|
+
ctorParameters: () => []
|
|
82
|
+
});
|
|
83
|
+
function provideStarDirection() {
|
|
84
|
+
return provideEnvironmentInitializer(() => {
|
|
85
|
+
inject(StarDirectionService);
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
function starListLayout(options, groupOf, idPrefix) {
|
|
89
|
+
const rows = [];
|
|
90
|
+
const rowOfOption = [];
|
|
91
|
+
let current = null;
|
|
92
|
+
let groupId = null;
|
|
93
|
+
let groupRow = -1;
|
|
94
|
+
options.forEach((option, index) => {
|
|
95
|
+
if (groupOf) {
|
|
96
|
+
const label = groupOf(option);
|
|
97
|
+
if (index === 0 || label !== current) {
|
|
98
|
+
current = label;
|
|
99
|
+
groupId = `${idPrefix}-group-${index}`;
|
|
100
|
+
groupRow = rows.length;
|
|
101
|
+
rows.push({
|
|
102
|
+
kind: "group",
|
|
103
|
+
key: `g:${index}`,
|
|
104
|
+
label,
|
|
105
|
+
id: groupId
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
rowOfOption.push(rows.length);
|
|
110
|
+
rows.push({
|
|
111
|
+
kind: "option",
|
|
112
|
+
key: `o:${index}`,
|
|
113
|
+
option,
|
|
114
|
+
index,
|
|
115
|
+
groupId,
|
|
116
|
+
groupRow
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
return {
|
|
120
|
+
rows,
|
|
121
|
+
rowOfOption
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
function starListPageSize(viewportHeight, itemSize) {
|
|
125
|
+
const lines = Math.floor(viewportHeight / Math.max(1, itemSize));
|
|
126
|
+
return Math.max(1, lines - 1);
|
|
127
|
+
}
|
|
128
|
+
function starListKeyboardTarget(key, active, count, page) {
|
|
129
|
+
if (count <= 0) return -1;
|
|
130
|
+
const last = count - 1;
|
|
131
|
+
let target;
|
|
132
|
+
switch (key) {
|
|
133
|
+
case "ArrowDown":
|
|
134
|
+
target = active + 1;
|
|
135
|
+
break;
|
|
136
|
+
case "ArrowUp":
|
|
137
|
+
target = active - 1;
|
|
138
|
+
break;
|
|
139
|
+
case "Home":
|
|
140
|
+
target = 0;
|
|
141
|
+
break;
|
|
142
|
+
case "End":
|
|
143
|
+
target = last;
|
|
144
|
+
break;
|
|
145
|
+
case "PageDown":
|
|
146
|
+
target = (active < 0 ? 0 : active) + page;
|
|
147
|
+
break;
|
|
148
|
+
case "PageUp": target = (active < 0 ? last : active) - page;
|
|
149
|
+
}
|
|
150
|
+
return Math.min(Math.max(target, 0), last);
|
|
151
|
+
}
|
|
152
|
+
function starListTypeaheadIndex(count, labelAt, typed) {
|
|
153
|
+
for (let i = 0; i < count; i++) if (labelAt(i).toLowerCase().startsWith(typed)) return i;
|
|
154
|
+
return -1;
|
|
155
|
+
}
|
|
156
|
+
function starListRevealRows(layout, index) {
|
|
157
|
+
const row = layout.rowOfOption[index];
|
|
158
|
+
if (row === void 0) return [0, 0];
|
|
159
|
+
return [layout.rows[row - 1]?.kind === "group" ? row - 1 : row, row];
|
|
160
|
+
}
|
|
161
|
+
function starListRevealOffset(first, last, itemSize, scrollOffset, viewportHeight, inset = 0) {
|
|
162
|
+
const top = inset + first * itemSize;
|
|
163
|
+
const bottom = inset + (last + 1) * itemSize;
|
|
164
|
+
if (top < scrollOffset) return first === 0 ? 0 : top;
|
|
165
|
+
if (viewportHeight > 0 && bottom > scrollOffset + viewportHeight) return bottom - viewportHeight;
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
function starListAriaPosition(index, count) {
|
|
169
|
+
return {
|
|
170
|
+
setsize: count,
|
|
171
|
+
posinset: index + 1
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
function starListHeight(scrollHeight, rowCount, itemSize) {
|
|
175
|
+
return `min(${scrollHeight}, ${rowCount * Math.max(1, itemSize)}px)`;
|
|
176
|
+
}
|
|
177
|
+
function starListRowRendered(row, range) {
|
|
178
|
+
return row >= range.start && row < range.end;
|
|
179
|
+
}
|
|
180
|
+
function readOption(option, key) {
|
|
181
|
+
if (key === null || key === void 0) return option;
|
|
182
|
+
if (typeof key === "function") return key(option);
|
|
183
|
+
return option !== null && typeof option === "object" ? option[key] : void 0;
|
|
184
|
+
}
|
|
185
|
+
function optionText(option, key) {
|
|
186
|
+
const value = readOption(option, key ?? null);
|
|
187
|
+
return value === null || value === void 0 ? "" : String(value);
|
|
188
|
+
}
|
|
189
|
+
const collator = new Intl.Collator(void 0, {
|
|
190
|
+
numeric: true,
|
|
191
|
+
sensitivity: "base"
|
|
192
|
+
});
|
|
193
|
+
function compareValues(a, b) {
|
|
194
|
+
const aEmpty = a === null || a === void 0;
|
|
195
|
+
const bEmpty = b === null || b === void 0;
|
|
196
|
+
if (aEmpty && bEmpty) return 0;
|
|
197
|
+
if (aEmpty) return -1;
|
|
198
|
+
if (bEmpty) return 1;
|
|
199
|
+
if (a instanceof Date && b instanceof Date) return a.getTime() - b.getTime();
|
|
200
|
+
if (typeof a === "number" && typeof b === "number") return a - b;
|
|
201
|
+
return collator.compare(String(a), String(b));
|
|
202
|
+
}
|
|
203
|
+
function matchesFilter(text, query) {
|
|
204
|
+
if (!query) return true;
|
|
205
|
+
const normalize = (value) => value.normalize("NFD").replace(/\p{Diacritic}/gu, "").toLocaleLowerCase();
|
|
206
|
+
return normalize(text).includes(normalize(query.trim()));
|
|
207
|
+
}
|
|
208
|
+
function signalControls(control) {
|
|
209
|
+
if (control instanceof SignalFormControl) return [control];
|
|
210
|
+
if (control instanceof FormGroup || control instanceof FormArray) return Object.values(control.controls).flatMap(signalControls);
|
|
211
|
+
return [];
|
|
212
|
+
}
|
|
213
|
+
function syncSignalFormControls(group, injector = inject(Injector)) {
|
|
214
|
+
const controls = signalControls(group);
|
|
215
|
+
return effect(() => {
|
|
216
|
+
for (const control of controls) {
|
|
217
|
+
const state = control.fieldTree();
|
|
218
|
+
state.valid();
|
|
219
|
+
state.disabled();
|
|
220
|
+
state.pending();
|
|
221
|
+
}
|
|
222
|
+
untracked(() => group.updateValueAndValidity());
|
|
223
|
+
}, { injector });
|
|
224
|
+
}
|
|
225
|
+
const STAR_UI_TEXT_DEFAULTS = {
|
|
226
|
+
close: "Close",
|
|
227
|
+
clear: "Clear",
|
|
228
|
+
loading: "Loading",
|
|
229
|
+
actions: "Actions",
|
|
230
|
+
moreActions: "More actions",
|
|
231
|
+
ofTotal: (index, total) => `${index} of ${total}`,
|
|
232
|
+
expandItem: (label) => `Expand ${label}`,
|
|
233
|
+
collapseItem: (label) => `Collapse ${label}`,
|
|
234
|
+
removeItem: (label) => `Remove ${label}`,
|
|
235
|
+
tagsHint: "Press Enter or comma to add. Backspace removes the last item.",
|
|
236
|
+
tagAdded: (label) => `${label} added`,
|
|
237
|
+
tagRemoved: (label) => `${label} removed`,
|
|
238
|
+
tagInvalid: (label) => `${label} is not valid.`,
|
|
239
|
+
tagDuplicate: (label) => `${label} is already added.`,
|
|
240
|
+
tagLimit: (max) => `You can add up to ${max} items.`,
|
|
241
|
+
notifications: "Notifications",
|
|
242
|
+
accept: "Confirm",
|
|
243
|
+
reject: "Cancel",
|
|
244
|
+
selectPlaceholder: "Select",
|
|
245
|
+
filterPlaceholder: "Search",
|
|
246
|
+
emptyMessage: "No options available",
|
|
247
|
+
emptyFilterMessage: "No results found",
|
|
248
|
+
selectAll: "Select all",
|
|
249
|
+
selectedItems: (count) => `${count} items selected`,
|
|
250
|
+
searching: "Searching…",
|
|
251
|
+
showOptions: "Show options",
|
|
252
|
+
resultsAvailable: (count) => count === 1 ? "1 result available" : `${count} results available`,
|
|
253
|
+
pagination: "Pagination",
|
|
254
|
+
firstPage: "First page",
|
|
255
|
+
previousPage: "Previous page",
|
|
256
|
+
nextPage: "Next page",
|
|
257
|
+
lastPage: "Last page",
|
|
258
|
+
page: (page) => `Page ${page}`,
|
|
259
|
+
rowsPerPage: "Rows per page",
|
|
260
|
+
pageReport: (first, last, total) => `${first}–${last} of ${total}`,
|
|
261
|
+
noData: "No records found",
|
|
262
|
+
selectRow: "Select row",
|
|
263
|
+
selectAllRows: "Select all rows",
|
|
264
|
+
filterBy: (column) => `Filter by ${column}`,
|
|
265
|
+
filterAll: "All",
|
|
266
|
+
filterMin: (column) => `${column} from`,
|
|
267
|
+
filterMax: (column) => `${column} to`,
|
|
268
|
+
filterMinShort: "Min",
|
|
269
|
+
filterMaxShort: "Max",
|
|
270
|
+
expandRow: "Expand row",
|
|
271
|
+
collapseRow: "Collapse row",
|
|
272
|
+
rowDetails: "Details",
|
|
273
|
+
reorderRows: "Order",
|
|
274
|
+
sortMultiHint: "Shift + click adds this column to the sort",
|
|
275
|
+
terminalCommand: "Command",
|
|
276
|
+
compareImages: "Compare images",
|
|
277
|
+
editCell: (column) => `Edit ${column}`,
|
|
278
|
+
columns: "Columns",
|
|
279
|
+
resizeColumn: (column) => `Resize ${column}`,
|
|
280
|
+
moveColumnEarlier: (column) => `Move ${column} earlier`,
|
|
281
|
+
moveColumnLater: (column) => `Move ${column} later`,
|
|
282
|
+
layout: "Layout",
|
|
283
|
+
layoutList: "List",
|
|
284
|
+
layoutGrid: "Grid",
|
|
285
|
+
moveSelectedItem: "Move selected item",
|
|
286
|
+
orderListHint: "Use Alt and the arrow keys to move the selected item.",
|
|
287
|
+
moveTop: "Move to top",
|
|
288
|
+
moveUp: "Move up",
|
|
289
|
+
moveDown: "Move down",
|
|
290
|
+
moveBottom: "Move to bottom",
|
|
291
|
+
itemPosition: (label, position, total) => `${label}, position ${position} of ${total}`,
|
|
292
|
+
transfer: "Transfer",
|
|
293
|
+
moveSelectedToTarget: "Move selected to target",
|
|
294
|
+
moveAllToTarget: "Move all to target",
|
|
295
|
+
moveSelectedToSource: "Move selected to source",
|
|
296
|
+
moveAllToSource: "Move all to source",
|
|
297
|
+
pickListSource: "Available",
|
|
298
|
+
pickListTarget: "Selected",
|
|
299
|
+
chooseDate: "Choose date",
|
|
300
|
+
chooseMonth: "Choose month",
|
|
301
|
+
chooseYear: "Choose year",
|
|
302
|
+
previousMonth: "Previous month",
|
|
303
|
+
nextMonth: "Next month",
|
|
304
|
+
previousYears: "Previous years",
|
|
305
|
+
nextYears: "Next years",
|
|
306
|
+
today: "Today",
|
|
307
|
+
time: "Time",
|
|
308
|
+
startTime: "Start time",
|
|
309
|
+
weekNumber: "Week number",
|
|
310
|
+
weekShort: "Wk",
|
|
311
|
+
endTime: "End time",
|
|
312
|
+
done: "Done",
|
|
313
|
+
increase: "Increase",
|
|
314
|
+
decrease: "Decrease",
|
|
315
|
+
showPassword: "Show password",
|
|
316
|
+
hidePassword: "Hide password",
|
|
317
|
+
passwordWeak: "Password strength: weak",
|
|
318
|
+
passwordFair: "Password strength: fair",
|
|
319
|
+
passwordGood: "Password strength: good",
|
|
320
|
+
passwordStrong: "Password strength: strong",
|
|
321
|
+
passwordRuleLength: (min) => `At least ${min} characters`,
|
|
322
|
+
passwordRuleUpper: "An uppercase letter",
|
|
323
|
+
passwordRuleLower: "A lowercase letter",
|
|
324
|
+
passwordRuleNumber: "A number",
|
|
325
|
+
passwordRuleSymbol: "A symbol",
|
|
326
|
+
ruleMet: "met",
|
|
327
|
+
ruleNotMet: "not met",
|
|
328
|
+
otpLabel: "Verification code",
|
|
329
|
+
otpCharacter: (position, total) => `Character ${position} of ${total}`,
|
|
330
|
+
ratingLabel: "Rating",
|
|
331
|
+
sliderMinimum: (label) => `${label} minimum`.trim(),
|
|
332
|
+
sliderMaximum: (label) => `${label} maximum`.trim(),
|
|
333
|
+
colorLabel: "Color",
|
|
334
|
+
customColor: "Custom color",
|
|
335
|
+
hexValue: (label) => `${label} hex value`,
|
|
336
|
+
chooseFiles: "Choose files",
|
|
337
|
+
dropFiles: "Drag files here, or",
|
|
338
|
+
fileMaxSize: (size) => `max ${size}`,
|
|
339
|
+
fileUpTo: (count) => `up to ${count} files`,
|
|
340
|
+
uploadingFile: (name) => `Uploading ${name}`,
|
|
341
|
+
uploaded: "Uploaded",
|
|
342
|
+
fileTypeNotAllowed: (name) => `${name}: this file type is not allowed.`,
|
|
343
|
+
fileTooLarge: (name, max) => `${name}: the file is larger than ${max}.`,
|
|
344
|
+
fileLimitReached: (limit) => `You can add up to ${limit} files.`,
|
|
345
|
+
editorLabel: "Editor",
|
|
346
|
+
editorFormatting: (label) => `${label} formatting`,
|
|
347
|
+
editorBold: "Bold",
|
|
348
|
+
editorItalic: "Italic",
|
|
349
|
+
editorUnderline: "Underline",
|
|
350
|
+
editorHeading: "Heading",
|
|
351
|
+
editorBulletList: "Bulleted list",
|
|
352
|
+
editorNumberedList: "Numbered list",
|
|
353
|
+
editorClearFormatting: "Clear formatting",
|
|
354
|
+
editorLink: "Link",
|
|
355
|
+
editorLinkPrompt: "Link address (https://…)",
|
|
356
|
+
breadcrumb: "Breadcrumb",
|
|
357
|
+
stepperLabel: "Progress",
|
|
358
|
+
stepCompleted: "(completed)",
|
|
359
|
+
resizePanes: "Resize panes",
|
|
360
|
+
dock: "Dock",
|
|
361
|
+
scrollToTop: "Scroll to top",
|
|
362
|
+
theme: "Theme",
|
|
363
|
+
themeSystem: "System",
|
|
364
|
+
themeLight: "Light",
|
|
365
|
+
themeDark: "Dark",
|
|
366
|
+
pauseSlides: "Pause slides",
|
|
367
|
+
playSlides: "Play slides",
|
|
368
|
+
previousSlides: "Previous slides",
|
|
369
|
+
nextSlides: "Next slides",
|
|
370
|
+
galleryLabel: "Gallery",
|
|
371
|
+
previousImage: "Previous image",
|
|
372
|
+
nextImage: "Next image",
|
|
373
|
+
showImage: (alt) => `Show ${alt}`,
|
|
374
|
+
previewImage: (alt) => `Preview: ${alt}`,
|
|
375
|
+
zoomIn: "Zoom in",
|
|
376
|
+
zoomOut: "Zoom out",
|
|
377
|
+
chartView: "Chart",
|
|
378
|
+
tableView: "Table",
|
|
379
|
+
chartTotal: "Total",
|
|
380
|
+
chartOther: "Other",
|
|
381
|
+
chartCategory: "Category",
|
|
382
|
+
chartValue: "Value",
|
|
383
|
+
chartShare: "Share",
|
|
384
|
+
chartSeries: "Series",
|
|
385
|
+
chartX: "X",
|
|
386
|
+
chartY: "Y",
|
|
387
|
+
chartTypeBar: "Bar chart",
|
|
388
|
+
chartTypeStackedBar: "Stacked bar chart",
|
|
389
|
+
chartTypeLine: "Line chart",
|
|
390
|
+
chartTypeArea: "Area chart",
|
|
391
|
+
chartTypeDoughnut: "Doughnut chart",
|
|
392
|
+
chartTypePie: "Pie chart",
|
|
393
|
+
chartTypeRadar: "Radar chart",
|
|
394
|
+
chartTypePolarArea: "Polar area chart",
|
|
395
|
+
chartTypeScatter: "Scatter chart",
|
|
396
|
+
chartDescription: (kind, series, items, min, max) => `${kind} with ${series} series and ${items} ${items === 1 ? "item" : "items"}, values from ${min} to ${max}. The same data is in the table.`,
|
|
397
|
+
meterValue: (value, total) => `${value} of ${total}`
|
|
398
|
+
};
|
|
399
|
+
const STAR_UI_TEXT_PARAMS = {
|
|
400
|
+
ofTotal: ["index", "total"],
|
|
401
|
+
expandItem: ["label"],
|
|
402
|
+
collapseItem: ["label"],
|
|
403
|
+
removeItem: ["label"],
|
|
404
|
+
tagAdded: ["label"],
|
|
405
|
+
tagRemoved: ["label"],
|
|
406
|
+
tagInvalid: ["label"],
|
|
407
|
+
tagDuplicate: ["label"],
|
|
408
|
+
tagLimit: ["max"],
|
|
409
|
+
selectedItems: ["count"],
|
|
410
|
+
resultsAvailable: ["count"],
|
|
411
|
+
page: ["page"],
|
|
412
|
+
pageReport: [
|
|
413
|
+
"first",
|
|
414
|
+
"last",
|
|
415
|
+
"total"
|
|
416
|
+
],
|
|
417
|
+
filterBy: ["column"],
|
|
418
|
+
filterMin: ["column"],
|
|
419
|
+
filterMax: ["column"],
|
|
420
|
+
editCell: ["column"],
|
|
421
|
+
resizeColumn: ["column"],
|
|
422
|
+
moveColumnEarlier: ["column"],
|
|
423
|
+
moveColumnLater: ["column"],
|
|
424
|
+
itemPosition: [
|
|
425
|
+
"label",
|
|
426
|
+
"position",
|
|
427
|
+
"total"
|
|
428
|
+
],
|
|
429
|
+
passwordRuleLength: ["min"],
|
|
430
|
+
otpCharacter: ["position", "total"],
|
|
431
|
+
sliderMinimum: ["label"],
|
|
432
|
+
sliderMaximum: ["label"],
|
|
433
|
+
hexValue: ["label"],
|
|
434
|
+
fileMaxSize: ["size"],
|
|
435
|
+
fileUpTo: ["count"],
|
|
436
|
+
uploadingFile: ["name"],
|
|
437
|
+
fileTypeNotAllowed: ["name"],
|
|
438
|
+
fileTooLarge: ["name", "max"],
|
|
439
|
+
fileLimitReached: ["limit"],
|
|
440
|
+
editorFormatting: ["label"],
|
|
441
|
+
showImage: ["alt"],
|
|
442
|
+
previewImage: ["alt"],
|
|
443
|
+
meterValue: ["value", "total"],
|
|
444
|
+
chartDescription: [
|
|
445
|
+
"kind",
|
|
446
|
+
"series",
|
|
447
|
+
"items",
|
|
448
|
+
"min",
|
|
449
|
+
"max"
|
|
450
|
+
]
|
|
451
|
+
};
|
|
452
|
+
const STAR_UI_TEXT = new InjectionToken("STAR_UI_TEXT", {
|
|
453
|
+
providedIn: "root",
|
|
454
|
+
factory: () => STAR_UI_TEXT_DEFAULTS
|
|
455
|
+
});
|
|
456
|
+
function provideStarUiText(overrides) {
|
|
457
|
+
return {
|
|
458
|
+
provide: STAR_UI_TEXT,
|
|
459
|
+
useValue: {
|
|
460
|
+
...STAR_UI_TEXT_DEFAULTS,
|
|
461
|
+
...overrides
|
|
462
|
+
}
|
|
463
|
+
};
|
|
464
|
+
}
|
|
465
|
+
function starUiTextFromSignal(source) {
|
|
466
|
+
const text = {};
|
|
467
|
+
for (const name of Object.keys(STAR_UI_TEXT_DEFAULTS)) if (typeof STAR_UI_TEXT_DEFAULTS[name] === "function") text[name] = (...args) => {
|
|
468
|
+
return (source()[name] ?? STAR_UI_TEXT_DEFAULTS[name])(...args);
|
|
469
|
+
};
|
|
470
|
+
else Object.defineProperty(text, name, {
|
|
471
|
+
enumerable: true,
|
|
472
|
+
get: () => source()[name] ?? STAR_UI_TEXT_DEFAULTS[name]
|
|
473
|
+
});
|
|
474
|
+
return text;
|
|
475
|
+
}
|
|
476
|
+
function provideStarUiTextFromSignal(source) {
|
|
477
|
+
return {
|
|
478
|
+
provide: STAR_UI_TEXT,
|
|
479
|
+
useFactory: () => starUiTextFromSignal(source)
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
export { STAR_UI_TEXT, STAR_UI_TEXT_DEFAULTS, STAR_UI_TEXT_PARAMS, StarDirectionService, compareValues, matchesFilter, optionText, provideStarDirection, provideStarUiText, provideStarUiTextFromSignal, readOption, starDirectionOf, starInlineKey, starIsRtl, starListAriaPosition, starListHeight, starListKeyboardTarget, starListLayout, starListPageSize, starListRevealOffset, starListRevealRows, starListRowRendered, starListTypeaheadIndex, starUiTextFromSignal, syncSignalFormControls };
|
|
483
|
+
|
|
484
|
+
//# sourceMappingURL=star-ng-ui-core.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"star-ng-ui-core.mjs","names":[],"sources":["../../../projects/star-ng/core/direction.ts","../../../projects/star-ng/core/list-windowing.ts","../../../projects/star-ng/core/options.ts","../../../projects/star-ng/core/reactive-forms.ts","../../../projects/star-ng/core/ui-text.ts"],"sourcesContent":["import { Directionality } from '@angular/cdk/bidi';\nimport { DOCUMENT } from '@angular/common';\nimport {\n DestroyRef,\n EnvironmentProviders,\n Injectable,\n inject,\n provideEnvironmentInitializer,\n signal,\n} from '@angular/core';\n\n/** Text direction of an element: left-to-right or right-to-left. */\nexport type StarDirection = 'ltr' | 'rtl';\n\n/**\n * Text direction in force at one element. A `dir` attribute on the element or on an ancestor\n * decides; with `dir=\"auto\"` or no attribute the computed `direction` decides. The function\n * reads the DOM each time it is called, so a handler that calls it follows a direction change at\n * run time, and a part of the page that has its own `dir` attribute is respected.\n *\n * Controls use it in key handlers and pointer math: the horizontal arrow keys follow the visual\n * direction, so in `rtl` the left arrow means \"next\".\n */\nexport function starDirectionOf(element: Element | null | undefined): StarDirection {\n if (!element) return 'ltr';\n const attribute = element.closest('[dir]')?.getAttribute('dir')?.toLowerCase();\n if (attribute === 'rtl' || attribute === 'ltr') return attribute;\n const view = element.ownerDocument.defaultView;\n return view?.getComputedStyle(element).direction === 'rtl' ? 'rtl' : 'ltr';\n}\n\n/** `true` when the element is laid out right-to-left. See `starDirectionOf`. */\nexport function starIsRtl(element: Element | null | undefined): boolean {\n return starDirectionOf(element) === 'rtl';\n}\n\n/**\n * Key of a keyboard event in \"inline\" terms: `'ArrowRight'` means toward the end of the line and\n * `'ArrowLeft'` toward the start, whatever the direction. In right-to-left the two arrow keys are\n * swapped; every other key is returned as it is. A handler can then be written once, as for\n * left-to-right text:\n *\n * ```ts\n * switch (starInlineKey(event)) {\n * case 'ArrowRight': // next item in reading order\n * }\n * ```\n *\n * The element decides the direction; it defaults to `event.currentTarget`.\n */\nexport function starInlineKey(event: KeyboardEvent, element?: Element | null): string {\n if (event.key !== 'ArrowLeft' && event.key !== 'ArrowRight') return event.key;\n const target = element ?? (event.currentTarget instanceof Element ? event.currentTarget : null);\n if (!starIsRtl(target ?? (event.target instanceof Element ? event.target : null)))\n return event.key;\n return event.key === 'ArrowLeft' ? 'ArrowRight' : 'ArrowLeft';\n}\n\n/**\n * Direction of the application: the `dir` attribute of `<html>` (or `<body>`), followed at run\n * time. Controls that open an overlay or a menu (CDK `Overlay`, `CdkMenu`) inject it. It does\n * two things:\n *\n * 1. `value` is a signal, so a control can react when the application switches the direction.\n * 2. It keeps the root `Directionality` of `@angular/cdk/bidi` in step with the page. That\n * service reads the attribute once when it is created; without this, overlays and menus\n * would keep the old direction after a switch.\n *\n * The application only sets `dir` on `<html>`: `document.documentElement.dir = 'rtl'`.\n */\n@Injectable({ providedIn: 'root' })\nexport class StarDirectionService {\n private readonly document = inject(DOCUMENT);\n private readonly directionality = inject(Directionality, { optional: true });\n\n private readonly current = signal<StarDirection>(this.read());\n /** The direction of the page now. */\n readonly value = this.current.asReadonly();\n\n constructor() {\n this.sync();\n const view = this.document.defaultView;\n if (!view || typeof view.MutationObserver !== 'function') return;\n const observer = new view.MutationObserver(() => this.refresh());\n const options = { attributes: true, attributeFilter: ['dir'] };\n observer.observe(this.document.documentElement, options);\n if (this.document.body) observer.observe(this.document.body, options);\n inject(DestroyRef).onDestroy(() => observer.disconnect());\n }\n\n /** Reads the attribute again. The observer calls it; call it by hand after a change that the observer cannot see. */\n refresh(): void {\n const next = this.read();\n if (next === this.current()) return;\n this.current.set(next);\n this.sync();\n }\n\n private read(): StarDirection {\n const raw = (\n this.document.body?.dir ||\n this.document.documentElement.dir ||\n 'ltr'\n ).toLowerCase();\n if (raw === 'auto') {\n return this.document.defaultView?.getComputedStyle(this.document.documentElement)\n .direction === 'rtl'\n ? 'rtl'\n : 'ltr';\n }\n return raw === 'rtl' ? 'rtl' : 'ltr';\n }\n\n private sync(): void {\n const target = this.directionality;\n if (!target || target.valueSignal() === this.current()) return;\n target.valueSignal.set(this.current());\n target.change.emit(this.current());\n }\n}\n\n/**\n * Starts `StarDirectionService` when the application starts. Optional: the controls that open\n * an overlay start the service themselves. Use it when the direction changes at run time before\n * any such control exists, or when your own code reads `StarDirectionService.value`.\n *\n * ```ts\n * bootstrapApplication(App, { providers: [provideStarDirection()] });\n * ```\n */\nexport function provideStarDirection(): EnvironmentProviders {\n return provideEnvironmentInitializer(() => {\n inject(StarDirectionService);\n });\n}\n","/**\n * Pure helpers of the windowed option lists: row model, index math, keyboard target and ARIA\n * values. `star-listbox` and the select family (`star-select`, `star-multi-select`,\n * `star-autocomplete`) use them. They have no DOM access, so unit tests can check them without\n * layout. They are exported because entry points import each other by package name. They are\n * helpers of the library, not a stable extension point.\n */\n\n/** One row of a windowed list. Group headings and options have the same height. */\nexport type StarWindowRow<T> =\n | { readonly kind: 'group'; readonly key: string; readonly label: string; readonly id: string }\n | {\n readonly kind: 'option';\n readonly key: string;\n readonly option: T;\n /** Index in the visible options (keyboard order). */\n readonly index: number;\n /** Id of the heading row of the group, or null without groups. */\n readonly groupId: string | null;\n /** Index of the heading row of the group, or -1 without groups. */\n readonly groupRow: number;\n };\n\n/** The rows, and for each visible option the index of its row. */\nexport interface StarWindowLayout<T> {\n readonly rows: readonly StarWindowRow<T>[];\n readonly rowOfOption: readonly number[];\n}\n\n/**\n * Flattens headings and options into one list of rows of equal height. `options` must already be\n * in display order (each group together). A heading comes before the first option of each group.\n * The heading id has the form `<idPrefix>-group-<index of the first option of the group>`.\n */\nexport function starListLayout<T>(\n options: readonly T[],\n groupOf: ((option: T) => string) | null,\n idPrefix: string,\n): StarWindowLayout<T> {\n const rows: StarWindowRow<T>[] = [];\n const rowOfOption: number[] = [];\n let current: string | null = null;\n let groupId: string | null = null;\n let groupRow = -1;\n options.forEach((option, index) => {\n if (groupOf) {\n const label = groupOf(option);\n if (index === 0 || label !== current) {\n current = label;\n groupId = `${idPrefix}-group-${index}`;\n groupRow = rows.length;\n rows.push({ kind: 'group', key: `g:${index}`, label, id: groupId });\n }\n }\n rowOfOption.push(rows.length);\n rows.push({ kind: 'option', key: `o:${index}`, option, index, groupId, groupRow });\n });\n return { rows, rowOfOption };\n}\n\n/** Number of options a Page up / Page down key moves: the lines in view, minus one for overlap. */\nexport function starListPageSize(viewportHeight: number, itemSize: number): number {\n const lines = Math.floor(viewportHeight / Math.max(1, itemSize));\n return Math.max(1, lines - 1);\n}\n\nexport type StarListNavigationKey =\n 'ArrowDown' | 'ArrowUp' | 'Home' | 'End' | 'PageDown' | 'PageUp';\n\n/** Index of the option a navigation key moves to, limited to the list. -1 for an empty list. */\nexport function starListKeyboardTarget(\n key: StarListNavigationKey,\n active: number,\n count: number,\n page: number,\n): number {\n if (count <= 0) return -1;\n const last = count - 1;\n let target: number;\n switch (key) {\n case 'ArrowDown':\n target = active + 1;\n break;\n case 'ArrowUp':\n target = active - 1;\n break;\n case 'Home':\n target = 0;\n break;\n case 'End':\n target = last;\n break;\n case 'PageDown':\n target = (active < 0 ? 0 : active) + page;\n break;\n case 'PageUp':\n target = (active < 0 ? last : active) - page;\n break;\n }\n return Math.min(Math.max(target, 0), last);\n}\n\n/** Index of the first option whose label starts with the typed text (compared in lower case). */\nexport function starListTypeaheadIndex(\n count: number,\n labelAt: (index: number) => string,\n typed: string,\n): number {\n for (let i = 0; i < count; i++) {\n if (labelAt(i).toLowerCase().startsWith(typed)) return i;\n }\n return -1;\n}\n\n/** First and last row to bring into view for an option: its heading row comes with the first option. */\nexport function starListRevealRows<T>(\n layout: StarWindowLayout<T>,\n index: number,\n): [number, number] {\n const row = layout.rowOfOption[index];\n if (row === undefined) return [0, 0];\n const above = layout.rows[row - 1];\n return [above?.kind === 'group' ? row - 1 : row, row];\n}\n\n/**\n * Scroll offset that brings rows `first` to `last` into view with the least movement, or null when\n * they are in view. Rows have the height `itemSize`. `inset` is the space above the first row\n * (the padding of the scroll container): a row below the view is moved by it, and the first row\n * scrolls to the very top so that the padding shows.\n */\nexport function starListRevealOffset(\n first: number,\n last: number,\n itemSize: number,\n scrollOffset: number,\n viewportHeight: number,\n inset = 0,\n): number | null {\n const top = inset + first * itemSize;\n const bottom = inset + (last + 1) * itemSize;\n if (top < scrollOffset) return first === 0 ? 0 : top;\n if (viewportHeight > 0 && bottom > scrollOffset + viewportHeight) return bottom - viewportHeight;\n return null;\n}\n\n/** `aria-setsize` and `aria-posinset` of an option, for the whole list. */\nexport function starListAriaPosition(\n index: number,\n count: number,\n): { setsize: number; posinset: number } {\n return { setsize: count, posinset: index + 1 };\n}\n\n/** CSS height of the list: `scrollHeight` at most, and no taller than the rows need. */\nexport function starListHeight(scrollHeight: string, rowCount: number, itemSize: number): string {\n return `min(${scrollHeight}, ${rowCount * Math.max(1, itemSize)}px)`;\n}\n\n/** True when a row is inside the rendered range `[start, end)`. */\nexport function starListRowRendered(row: number, range: { start: number; end: number }): boolean {\n return row >= range.start && row < range.end;\n}\n","/**\n * How a list control reads a property from an option: a property name or an accessor function.\n * Same idea as PrimeNG `optionLabel` / `optionValue` / `optionDisabled`, but type safe.\n */\nexport type StarOptionKey<T, R = unknown> = (keyof T & string) | ((option: T) => R);\n\nexport type StarSeverity = 'primary' | 'secondary' | 'success' | 'info' | 'warn' | 'danger';\n\nexport type StarSize = 'sm' | 'md' | 'lg';\n\nexport function readOption<T, R>(option: T, key: StarOptionKey<T, R> | null | undefined): unknown {\n if (key === null || key === undefined) return option;\n if (typeof key === 'function') return key(option);\n return option !== null && typeof option === 'object'\n ? (option as Record<string, unknown>)[key]\n : undefined;\n}\n\n/** Text shown for an option. Falls back to the option itself for primitive options. */\nexport function optionText<T>(option: T, key: StarOptionKey<T> | null | undefined): string {\n const value = readOption(option, key ?? null);\n return value === null || value === undefined ? '' : String(value);\n}\n\nconst collator = new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' });\n\n/** Locale-aware compare used by sorting: numbers and dates numerically, text with Intl.Collator. */\nexport function compareValues(a: unknown, b: unknown): number {\n const aEmpty = a === null || a === undefined;\n const bEmpty = b === null || b === undefined;\n if (aEmpty && bEmpty) return 0;\n if (aEmpty) return -1;\n if (bEmpty) return 1;\n if (a instanceof Date && b instanceof Date) return a.getTime() - b.getTime();\n if (typeof a === 'number' && typeof b === 'number') return a - b;\n return collator.compare(String(a), String(b));\n}\n\n/** Case- and accent-insensitive \"contains\" match used by filters. */\nexport function matchesFilter(text: string, query: string): boolean {\n if (!query) return true;\n const normalize = (value: string) =>\n value\n .normalize('NFD')\n .replace(/\\p{Diacritic}/gu, '')\n .toLocaleLowerCase();\n return normalize(text).includes(normalize(query.trim()));\n}\n","import { effect, EffectRef, inject, Injector, untracked } from '@angular/core';\nimport { AbstractControl, FormArray, FormGroup } from '@angular/forms';\nimport { SignalFormControl } from '@angular/forms/signals/compat';\n\nfunction signalControls(control: AbstractControl): SignalFormControl<unknown>[] {\n if (control instanceof SignalFormControl) return [control as SignalFormControl<unknown>];\n if (control instanceof FormGroup || control instanceof FormArray) {\n return Object.values(control.controls).flatMap(signalControls);\n }\n return [];\n}\n\n/**\n * Keeps a Reactive `FormGroup` (or `FormArray`) in step with the `SignalFormControl`s inside it.\n *\n * Angular 22 updates a parent group when a `SignalFormControl` value changes, but not when a\n * Signal Forms rule (for example `disabled({ when })` or a conditional `required`) changes the\n * child's status without a value change. The group then reports a stale `valid` / `status`.\n * This effect watches every signal control's validity, disabled and pending state and calls\n * `updateValueAndValidity()` on the group when one changes.\n *\n * Call it in an injection context (a field initializer or constructor):\n * ```ts\n * readonly form = new FormGroup({ country: new SignalFormControl<string | null>(null, rules) });\n * private readonly sync = syncSignalFormControls(this.form);\n * ```\n */\nexport function syncSignalFormControls(\n group: FormGroup | FormArray,\n injector: Injector = inject(Injector),\n): EffectRef {\n const controls = signalControls(group);\n return effect(\n () => {\n for (const control of controls) {\n const state = control.fieldTree();\n state.valid();\n state.disabled();\n state.pending();\n }\n untracked(() => group.updateValueAndValidity());\n },\n { injector },\n );\n}\n","import { InjectionToken, Provider } from '@angular/core';\n\n/**\n * Built-in texts of the StarNG UI controls (screen-reader labels, empty states, reports).\n * Defaults are English. Override them once at app level:\n * - `provideStarUiTextFromTransloco()` reads them from the active Transloco language (`ui.*` keys)\n * and updates the controls live when the language changes.\n * - `provideStarUiText({...})` sets fixed texts.\n *\n * Function entries take values (for example a count) and return the full sentence, so word order\n * can change per language. Their parameter names are listed in `STAR_UI_TEXT_PARAMS`.\n */\nexport interface StarUiText {\n // General\n readonly close: string;\n readonly clear: string;\n readonly loading: string;\n readonly actions: string;\n readonly moreActions: string;\n readonly ofTotal: (index: number, total: number) => string;\n readonly expandItem: (label: string) => string;\n readonly collapseItem: (label: string) => string;\n readonly removeItem: (label: string) => string;\n // Tags field\n readonly tagsHint: string;\n readonly tagAdded: (label: string) => string;\n readonly tagRemoved: (label: string) => string;\n readonly tagInvalid: (label: string) => string;\n readonly tagDuplicate: (label: string) => string;\n readonly tagLimit: (max: number) => string;\n readonly notifications: string;\n readonly accept: string;\n readonly reject: string;\n // Select, listbox, autocomplete\n readonly selectPlaceholder: string;\n readonly filterPlaceholder: string;\n readonly emptyMessage: string;\n readonly emptyFilterMessage: string;\n readonly selectAll: string;\n readonly selectedItems: (count: number) => string;\n readonly searching: string;\n readonly showOptions: string;\n readonly resultsAvailable: (count: number) => string;\n // Table, paginator, data view\n readonly pagination: string;\n readonly firstPage: string;\n readonly previousPage: string;\n readonly nextPage: string;\n readonly lastPage: string;\n readonly page: (page: number) => string;\n readonly rowsPerPage: string;\n readonly pageReport: (first: number, last: number, total: number) => string;\n readonly noData: string;\n readonly selectRow: string;\n readonly selectAllRows: string;\n readonly filterBy: (column: string) => string;\n readonly filterAll: string;\n readonly filterMin: (column: string) => string;\n readonly filterMax: (column: string) => string;\n readonly filterMinShort: string;\n readonly filterMaxShort: string;\n readonly expandRow: string;\n readonly collapseRow: string;\n readonly rowDetails: string;\n readonly reorderRows: string;\n readonly sortMultiHint: string;\n readonly terminalCommand: string;\n readonly compareImages: string;\n readonly editCell: (column: string) => string;\n readonly columns: string;\n readonly resizeColumn: (column: string) => string;\n readonly moveColumnEarlier: (column: string) => string;\n readonly moveColumnLater: (column: string) => string;\n readonly layout: string;\n readonly layoutList: string;\n readonly layoutGrid: string;\n // Order list, pick list\n readonly moveSelectedItem: string;\n readonly orderListHint: string;\n readonly moveTop: string;\n readonly moveUp: string;\n readonly moveDown: string;\n readonly moveBottom: string;\n readonly itemPosition: (label: string, position: number, total: number) => string;\n readonly transfer: string;\n readonly moveSelectedToTarget: string;\n readonly moveAllToTarget: string;\n readonly moveSelectedToSource: string;\n readonly moveAllToSource: string;\n readonly pickListSource: string;\n readonly pickListTarget: string;\n // Dates\n readonly chooseDate: string;\n readonly chooseMonth: string;\n readonly chooseYear: string;\n readonly previousMonth: string;\n readonly nextMonth: string;\n readonly previousYears: string;\n readonly nextYears: string;\n readonly today: string;\n readonly time: string;\n readonly startTime: string;\n readonly weekNumber: string;\n readonly weekShort: string;\n readonly endTime: string;\n readonly done: string;\n // Inputs\n readonly increase: string;\n readonly decrease: string;\n readonly showPassword: string;\n readonly hidePassword: string;\n readonly passwordWeak: string;\n readonly passwordFair: string;\n readonly passwordGood: string;\n readonly passwordStrong: string;\n readonly passwordRuleLength: (min: number) => string;\n readonly passwordRuleUpper: string;\n readonly passwordRuleLower: string;\n readonly passwordRuleNumber: string;\n readonly passwordRuleSymbol: string;\n readonly ruleMet: string;\n readonly ruleNotMet: string;\n readonly otpLabel: string;\n readonly otpCharacter: (position: number, total: number) => string;\n readonly ratingLabel: string;\n readonly sliderMinimum: (label: string) => string;\n readonly sliderMaximum: (label: string) => string;\n readonly colorLabel: string;\n readonly customColor: string;\n readonly hexValue: (label: string) => string;\n // File upload\n readonly chooseFiles: string;\n readonly dropFiles: string;\n readonly fileMaxSize: (size: string) => string;\n readonly fileUpTo: (count: number) => string;\n readonly uploadingFile: (name: string) => string;\n readonly uploaded: string;\n readonly fileTypeNotAllowed: (name: string) => string;\n readonly fileTooLarge: (name: string, max: string) => string;\n readonly fileLimitReached: (limit: number) => string;\n // Editor\n readonly editorLabel: string;\n readonly editorFormatting: (label: string) => string;\n readonly editorBold: string;\n readonly editorItalic: string;\n readonly editorUnderline: string;\n readonly editorHeading: string;\n readonly editorBulletList: string;\n readonly editorNumberedList: string;\n readonly editorClearFormatting: string;\n readonly editorLink: string;\n readonly editorLinkPrompt: string;\n // Navigation and layout\n readonly breadcrumb: string;\n readonly stepperLabel: string;\n readonly stepCompleted: string;\n readonly resizePanes: string;\n readonly dock: string;\n readonly scrollToTop: string;\n readonly theme: string;\n readonly themeSystem: string;\n readonly themeLight: string;\n readonly themeDark: string;\n // Media\n readonly pauseSlides: string;\n readonly playSlides: string;\n readonly previousSlides: string;\n readonly nextSlides: string;\n readonly galleryLabel: string;\n readonly previousImage: string;\n readonly nextImage: string;\n readonly showImage: (alt: string) => string;\n readonly previewImage: (alt: string) => string;\n readonly zoomIn: string;\n readonly zoomOut: string;\n // Charts and meters\n readonly chartView: string;\n readonly tableView: string;\n readonly chartTotal: string;\n readonly chartOther: string;\n readonly chartCategory: string;\n readonly chartValue: string;\n readonly chartShare: string;\n readonly chartSeries: string;\n readonly chartX: string;\n readonly chartY: string;\n readonly chartTypeBar: string;\n readonly chartTypeStackedBar: string;\n readonly chartTypeLine: string;\n readonly chartTypeArea: string;\n readonly chartTypeDoughnut: string;\n readonly chartTypePie: string;\n readonly chartTypeRadar: string;\n readonly chartTypePolarArea: string;\n readonly chartTypeScatter: string;\n readonly chartDescription: (\n kind: string,\n series: number,\n items: number,\n min: string,\n max: string,\n ) => string;\n readonly meterValue: (value: string, total: number) => string;\n}\n\nexport const STAR_UI_TEXT_DEFAULTS: StarUiText = {\n close: 'Close',\n clear: 'Clear',\n loading: 'Loading',\n actions: 'Actions',\n moreActions: 'More actions',\n ofTotal: (index, total) => `${index} of ${total}`,\n expandItem: (label) => `Expand ${label}`,\n collapseItem: (label) => `Collapse ${label}`,\n removeItem: (label) => `Remove ${label}`,\n tagsHint: 'Press Enter or comma to add. Backspace removes the last item.',\n tagAdded: (label) => `${label} added`,\n tagRemoved: (label) => `${label} removed`,\n tagInvalid: (label) => `${label} is not valid.`,\n tagDuplicate: (label) => `${label} is already added.`,\n tagLimit: (max) => `You can add up to ${max} items.`,\n notifications: 'Notifications',\n accept: 'Confirm',\n reject: 'Cancel',\n\n selectPlaceholder: 'Select',\n filterPlaceholder: 'Search',\n emptyMessage: 'No options available',\n emptyFilterMessage: 'No results found',\n selectAll: 'Select all',\n selectedItems: (count) => `${count} items selected`,\n searching: 'Searching…',\n showOptions: 'Show options',\n resultsAvailable: (count) => (count === 1 ? '1 result available' : `${count} results available`),\n\n pagination: 'Pagination',\n firstPage: 'First page',\n previousPage: 'Previous page',\n nextPage: 'Next page',\n lastPage: 'Last page',\n page: (page) => `Page ${page}`,\n rowsPerPage: 'Rows per page',\n pageReport: (first, last, total) => `${first}–${last} of ${total}`,\n noData: 'No records found',\n selectRow: 'Select row',\n selectAllRows: 'Select all rows',\n filterBy: (column) => `Filter by ${column}`,\n filterAll: 'All',\n filterMin: (column) => `${column} from`,\n filterMax: (column) => `${column} to`,\n filterMinShort: 'Min',\n filterMaxShort: 'Max',\n expandRow: 'Expand row',\n collapseRow: 'Collapse row',\n rowDetails: 'Details',\n reorderRows: 'Order',\n sortMultiHint: 'Shift + click adds this column to the sort',\n terminalCommand: 'Command',\n compareImages: 'Compare images',\n editCell: (column) => `Edit ${column}`,\n columns: 'Columns',\n resizeColumn: (column) => `Resize ${column}`,\n moveColumnEarlier: (column) => `Move ${column} earlier`,\n moveColumnLater: (column) => `Move ${column} later`,\n layout: 'Layout',\n layoutList: 'List',\n layoutGrid: 'Grid',\n\n moveSelectedItem: 'Move selected item',\n orderListHint: 'Use Alt and the arrow keys to move the selected item.',\n moveTop: 'Move to top',\n moveUp: 'Move up',\n moveDown: 'Move down',\n moveBottom: 'Move to bottom',\n itemPosition: (label, position, total) => `${label}, position ${position} of ${total}`,\n transfer: 'Transfer',\n moveSelectedToTarget: 'Move selected to target',\n moveAllToTarget: 'Move all to target',\n moveSelectedToSource: 'Move selected to source',\n moveAllToSource: 'Move all to source',\n pickListSource: 'Available',\n pickListTarget: 'Selected',\n\n chooseDate: 'Choose date',\n chooseMonth: 'Choose month',\n chooseYear: 'Choose year',\n previousMonth: 'Previous month',\n nextMonth: 'Next month',\n previousYears: 'Previous years',\n nextYears: 'Next years',\n today: 'Today',\n time: 'Time',\n startTime: 'Start time',\n weekNumber: 'Week number',\n weekShort: 'Wk',\n endTime: 'End time',\n done: 'Done',\n\n increase: 'Increase',\n decrease: 'Decrease',\n showPassword: 'Show password',\n hidePassword: 'Hide password',\n passwordWeak: 'Password strength: weak',\n passwordFair: 'Password strength: fair',\n passwordGood: 'Password strength: good',\n passwordStrong: 'Password strength: strong',\n passwordRuleLength: (min) => `At least ${min} characters`,\n passwordRuleUpper: 'An uppercase letter',\n passwordRuleLower: 'A lowercase letter',\n passwordRuleNumber: 'A number',\n passwordRuleSymbol: 'A symbol',\n ruleMet: 'met',\n ruleNotMet: 'not met',\n otpLabel: 'Verification code',\n otpCharacter: (position, total) => `Character ${position} of ${total}`,\n ratingLabel: 'Rating',\n sliderMinimum: (label) => `${label} minimum`.trim(),\n sliderMaximum: (label) => `${label} maximum`.trim(),\n colorLabel: 'Color',\n customColor: 'Custom color',\n hexValue: (label) => `${label} hex value`,\n\n chooseFiles: 'Choose files',\n dropFiles: 'Drag files here, or',\n fileMaxSize: (size) => `max ${size}`,\n fileUpTo: (count) => `up to ${count} files`,\n uploadingFile: (name) => `Uploading ${name}`,\n uploaded: 'Uploaded',\n fileTypeNotAllowed: (name) => `${name}: this file type is not allowed.`,\n fileTooLarge: (name, max) => `${name}: the file is larger than ${max}.`,\n fileLimitReached: (limit) => `You can add up to ${limit} files.`,\n\n editorLabel: 'Editor',\n editorFormatting: (label) => `${label} formatting`,\n editorBold: 'Bold',\n editorItalic: 'Italic',\n editorUnderline: 'Underline',\n editorHeading: 'Heading',\n editorBulletList: 'Bulleted list',\n editorNumberedList: 'Numbered list',\n editorClearFormatting: 'Clear formatting',\n editorLink: 'Link',\n editorLinkPrompt: 'Link address (https://…)',\n\n breadcrumb: 'Breadcrumb',\n stepperLabel: 'Progress',\n stepCompleted: '(completed)',\n resizePanes: 'Resize panes',\n dock: 'Dock',\n scrollToTop: 'Scroll to top',\n theme: 'Theme',\n themeSystem: 'System',\n themeLight: 'Light',\n themeDark: 'Dark',\n\n pauseSlides: 'Pause slides',\n playSlides: 'Play slides',\n previousSlides: 'Previous slides',\n nextSlides: 'Next slides',\n galleryLabel: 'Gallery',\n previousImage: 'Previous image',\n nextImage: 'Next image',\n showImage: (alt) => `Show ${alt}`,\n previewImage: (alt) => `Preview: ${alt}`,\n zoomIn: 'Zoom in',\n zoomOut: 'Zoom out',\n\n chartView: 'Chart',\n tableView: 'Table',\n chartTotal: 'Total',\n chartOther: 'Other',\n chartCategory: 'Category',\n chartValue: 'Value',\n chartShare: 'Share',\n chartSeries: 'Series',\n chartX: 'X',\n chartY: 'Y',\n chartTypeBar: 'Bar chart',\n chartTypeStackedBar: 'Stacked bar chart',\n chartTypeLine: 'Line chart',\n chartTypeArea: 'Area chart',\n chartTypeDoughnut: 'Doughnut chart',\n chartTypePie: 'Pie chart',\n chartTypeRadar: 'Radar chart',\n chartTypePolarArea: 'Polar area chart',\n chartTypeScatter: 'Scatter chart',\n chartDescription: (kind, series, items, min, max) =>\n `${kind} with ${series} series and ${items} ${items === 1 ? 'item' : 'items'}, values from ${min} to ${max}. The same data is in the table.`,\n meterValue: (value, total) => `${value} of ${total}`,\n};\n\n/** Parameter names of the function entries, in call order (used for `{{ name }}` in translations). */\nexport const STAR_UI_TEXT_PARAMS: { readonly [K in keyof StarUiText]?: readonly string[] } = {\n ofTotal: ['index', 'total'],\n expandItem: ['label'],\n collapseItem: ['label'],\n removeItem: ['label'],\n tagAdded: ['label'],\n tagRemoved: ['label'],\n tagInvalid: ['label'],\n tagDuplicate: ['label'],\n tagLimit: ['max'],\n selectedItems: ['count'],\n resultsAvailable: ['count'],\n page: ['page'],\n pageReport: ['first', 'last', 'total'],\n filterBy: ['column'],\n filterMin: ['column'],\n filterMax: ['column'],\n editCell: ['column'],\n resizeColumn: ['column'],\n moveColumnEarlier: ['column'],\n moveColumnLater: ['column'],\n itemPosition: ['label', 'position', 'total'],\n passwordRuleLength: ['min'],\n otpCharacter: ['position', 'total'],\n sliderMinimum: ['label'],\n sliderMaximum: ['label'],\n hexValue: ['label'],\n fileMaxSize: ['size'],\n fileUpTo: ['count'],\n uploadingFile: ['name'],\n fileTypeNotAllowed: ['name'],\n fileTooLarge: ['name', 'max'],\n fileLimitReached: ['limit'],\n editorFormatting: ['label'],\n showImage: ['alt'],\n previewImage: ['alt'],\n meterValue: ['value', 'total'],\n chartDescription: ['kind', 'series', 'items', 'min', 'max'],\n};\n\nexport const STAR_UI_TEXT = new InjectionToken<StarUiText>('STAR_UI_TEXT', {\n providedIn: 'root',\n factory: () => STAR_UI_TEXT_DEFAULTS,\n});\n\n/** Overrides some or all built-in control texts with fixed values. */\nexport function provideStarUiText(overrides: Partial<StarUiText>): Provider {\n return { provide: STAR_UI_TEXT, useValue: { ...STAR_UI_TEXT_DEFAULTS, ...overrides } };\n}\n\n/**\n * Builds a `StarUiText` whose entries read `source()` on every access. When `source` reads a\n * signal, every OnPush control that renders a built-in text updates when that signal changes.\n * Entries that `source()` does not return fall back to the English default.\n *\n * Use it in a factory when the signal lives in a service:\n * `{ provide: STAR_UI_TEXT, useFactory: () => starUiTextFromSignal(inject(Language).text) }`.\n */\nexport function starUiTextFromSignal(source: () => Partial<StarUiText>): StarUiText {\n const text = {} as Record<string, unknown>;\n for (const name of Object.keys(STAR_UI_TEXT_DEFAULTS) as (keyof StarUiText)[]) {\n if (typeof STAR_UI_TEXT_DEFAULTS[name] === 'function') {\n text[name] = (...args: unknown[]) => {\n const entry = (source()[name] ?? STAR_UI_TEXT_DEFAULTS[name]) as (\n ...a: unknown[]\n ) => string;\n return entry(...args);\n };\n } else {\n Object.defineProperty(text, name, {\n enumerable: true,\n get: () => source()[name] ?? STAR_UI_TEXT_DEFAULTS[name],\n });\n }\n }\n return text as unknown as StarUiText;\n}\n\n/**\n * Provides the control texts from a function that reads a signal, so the language can change at\n * run time without a translation library:\n *\n * ```ts\n * const language = signal<'en' | 'de'>('en');\n * provideStarUiTextFromSignal(() => (language() === 'de' ? STAR_UI_TEXT_DE : STAR_UI_TEXT_DEFAULTS));\n * ```\n *\n * Do not spread a pack with getters into an object (`{ ...text }`): that reads every getter once and\n * freezes the result. Pass a function, as shown above.\n */\nexport function provideStarUiTextFromSignal(source: () => Partial<StarUiText>): Provider {\n return { provide: STAR_UI_TEXT, useFactory: () => starUiTextFromSignal(source) };\n}\n"],"mappings":"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"}
|