vanillaforge 1.9.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/CHANGELOG.md +466 -0
- package/README.md +198 -0
- package/package.json +91 -0
- package/src/components/base-component.js +925 -0
- package/src/core/component-manager.js +306 -0
- package/src/core/dom-morph.js +234 -0
- package/src/core/event-bus.js +229 -0
- package/src/core/router.js +487 -0
- package/src/core/signal.js +114 -0
- package/src/framework.js +323 -0
- package/src/plugins/alerts/alerts-plugin.js +427 -0
- package/src/plugins/fonts/files/inter.js +4 -0
- package/src/plugins/fonts/files/jetbrains-mono.js +4 -0
- package/src/plugins/fonts/font-manifests.js +53 -0
- package/src/plugins/fonts/fonts-plugin.js +246 -0
- package/src/plugins/icons/default-icons.js +51 -0
- package/src/plugins/icons/icons-plugin.js +130 -0
- package/src/plugins/store/store-plugin.js +127 -0
- package/src/plugins/theme/base-styles.js +58 -0
- package/src/plugins/theme/theme-plugin.js +160 -0
- package/src/utils/decorators.js +51 -0
- package/src/utils/dom.js +40 -0
- package/src/utils/error-handler.js +442 -0
- package/src/utils/framework-debug.js +375 -0
- package/src/utils/logger.js +324 -0
- package/src/utils/notification.js +123 -0
- package/src/utils/performance.js +281 -0
- package/src/utils/storage.js +86 -0
- package/src/utils/sweet-alert.js +84 -0
- package/src/utils/validation.js +70 -0
- package/src/utils/validators.js +129 -0
- package/types/index.d.ts +524 -0
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Individual validation functions
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export function validateEmail(email) {
|
|
6
|
+
const result = {
|
|
7
|
+
isValid: false,
|
|
8
|
+
errors: [],
|
|
9
|
+
sanitized: null
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
if (!email || typeof email !== 'string') {
|
|
13
|
+
result.errors.push('Email is required');
|
|
14
|
+
return result;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const sanitized = email.trim().toLowerCase();
|
|
18
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
19
|
+
|
|
20
|
+
if (!emailRegex.test(sanitized)) {
|
|
21
|
+
result.errors.push('Invalid email format');
|
|
22
|
+
} else if (sanitized.length > 254) {
|
|
23
|
+
result.errors.push('Email address is too long');
|
|
24
|
+
} else {
|
|
25
|
+
result.isValid = true;
|
|
26
|
+
result.sanitized = sanitized;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return result;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function validatePhoneNumber(phone, countryCode = 'US') {
|
|
33
|
+
const result = {
|
|
34
|
+
isValid: false,
|
|
35
|
+
errors: [],
|
|
36
|
+
sanitized: null,
|
|
37
|
+
formatted: null
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
if (!phone || typeof phone !== 'string') {
|
|
41
|
+
result.errors.push('Phone number is required');
|
|
42
|
+
return result;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
let sanitized = phone.replace(/[^\d+]/g, '');
|
|
46
|
+
|
|
47
|
+
if (countryCode === 'US') {
|
|
48
|
+
if (sanitized.startsWith('+1')) {
|
|
49
|
+
sanitized = sanitized.substring(2);
|
|
50
|
+
} else if (sanitized.startsWith('1') && sanitized.length === 11) {
|
|
51
|
+
sanitized = sanitized.substring(1);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (sanitized.length !== 10) {
|
|
55
|
+
result.errors.push('US phone number must be 10 digits');
|
|
56
|
+
} else {
|
|
57
|
+
result.isValid = true;
|
|
58
|
+
result.sanitized = sanitized;
|
|
59
|
+
result.formatted = `(${sanitized.substring(0, 3)}) ${sanitized.substring(3, 6)}-${sanitized.substring(6)}`;
|
|
60
|
+
}
|
|
61
|
+
} else {
|
|
62
|
+
if (sanitized.length < 7 || sanitized.length > 15) {
|
|
63
|
+
result.errors.push('Phone number must be between 7 and 15 digits');
|
|
64
|
+
} else {
|
|
65
|
+
result.isValid = true;
|
|
66
|
+
result.sanitized = sanitized;
|
|
67
|
+
result.formatted = sanitized;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return result;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function validateCurrencyAmount(amount, options = {}) {
|
|
75
|
+
const result = {
|
|
76
|
+
isValid: false,
|
|
77
|
+
errors: [],
|
|
78
|
+
sanitized: null,
|
|
79
|
+
formatted: null
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const {
|
|
83
|
+
minAmount = 0.01,
|
|
84
|
+
maxAmount = 1000000,
|
|
85
|
+
currency = 'USD',
|
|
86
|
+
allowZero = false
|
|
87
|
+
} = options;
|
|
88
|
+
|
|
89
|
+
if (amount === null || amount === undefined) {
|
|
90
|
+
result.errors.push('Amount is required');
|
|
91
|
+
return result;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
let numericAmount;
|
|
95
|
+
|
|
96
|
+
if (typeof amount === 'string') {
|
|
97
|
+
const cleanAmount = amount.replace(/[$,\s]/g, '');
|
|
98
|
+
numericAmount = parseFloat(cleanAmount);
|
|
99
|
+
} else if (typeof amount === 'number') {
|
|
100
|
+
numericAmount = amount;
|
|
101
|
+
} else {
|
|
102
|
+
result.errors.push('Amount must be a number or string');
|
|
103
|
+
return result;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (isNaN(numericAmount) || !isFinite(numericAmount)) {
|
|
107
|
+
result.errors.push('Invalid amount format');
|
|
108
|
+
return result;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
numericAmount = Math.round(numericAmount * 100) / 100;
|
|
112
|
+
|
|
113
|
+
if ((!allowZero && numericAmount <= 0) || (allowZero && numericAmount < 0)) {
|
|
114
|
+
result.errors.push('Amount must be a positive number');
|
|
115
|
+
} else if (numericAmount < minAmount) {
|
|
116
|
+
result.errors.push(`Amount must be at least ${minAmount}`);
|
|
117
|
+
} else if (numericAmount > maxAmount) {
|
|
118
|
+
result.errors.push(`Amount cannot exceed ${maxAmount}`);
|
|
119
|
+
} else {
|
|
120
|
+
result.isValid = true;
|
|
121
|
+
result.sanitized = numericAmount;
|
|
122
|
+
result.formatted = new Intl.NumberFormat('en-US', {
|
|
123
|
+
style: 'currency',
|
|
124
|
+
currency: currency
|
|
125
|
+
}).format(numericAmount);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return result;
|
|
129
|
+
}
|
package/types/index.d.ts
ADDED
|
@@ -0,0 +1,524 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VanillaForge — TypeScript declarations.
|
|
3
|
+
*
|
|
4
|
+
* These types cover the full public API exported from `vanillaforge`
|
|
5
|
+
* (src/framework.js). Import them the same way you import the runtime:
|
|
6
|
+
*
|
|
7
|
+
* import { createApp, BaseComponent, iconsPlugin } from 'vanillaforge';
|
|
8
|
+
*
|
|
9
|
+
* For plain JavaScript projects, add to jsconfig.json:
|
|
10
|
+
* { "compilerOptions": { "checkJs": true } }
|
|
11
|
+
* and the types will be picked up automatically via the package's
|
|
12
|
+
* "types" export condition.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
// Signal — reactive primitive
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* A reactive value. Reading `.value` returns the current value; calling
|
|
21
|
+
* `.set(newValue)` updates it, notifies all subscribers, and (when the
|
|
22
|
+
* signal was created via `this.signal()` inside a component) schedules a
|
|
23
|
+
* single batched morph re-render for the next microtask.
|
|
24
|
+
*
|
|
25
|
+
* Multiple `.set()` calls within the same synchronous block collapse into
|
|
26
|
+
* one render.
|
|
27
|
+
*/
|
|
28
|
+
export declare class Signal<T = unknown> {
|
|
29
|
+
constructor(initialValue: T);
|
|
30
|
+
|
|
31
|
+
/** The current value. */
|
|
32
|
+
readonly value: T;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Update the value. Identical values (via Object.is) are ignored.
|
|
36
|
+
* Notifies subscribers and schedules a component re-render.
|
|
37
|
+
*/
|
|
38
|
+
set(newValue: T): void;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Subscribe to value changes.
|
|
42
|
+
* Returns an unsubscribe function.
|
|
43
|
+
*/
|
|
44
|
+
subscribe(fn: (value: T) => void): () => void;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// ---------------------------------------------------------------------------
|
|
48
|
+
// Shared primitives
|
|
49
|
+
// ---------------------------------------------------------------------------
|
|
50
|
+
|
|
51
|
+
export type AnyRecord = Record<string, unknown>;
|
|
52
|
+
|
|
53
|
+
// ---------------------------------------------------------------------------
|
|
54
|
+
// Configuration
|
|
55
|
+
// ---------------------------------------------------------------------------
|
|
56
|
+
|
|
57
|
+
export interface RouterConfig {
|
|
58
|
+
/** 'history' (default) or 'hash' */
|
|
59
|
+
mode?: 'history' | 'hash';
|
|
60
|
+
/** Route to redirect to when no match is found. Default: '/404'. */
|
|
61
|
+
fallback?: string;
|
|
62
|
+
/** URL prefix for all routes (e.g. '/vanillaforge' on GitHub Pages). */
|
|
63
|
+
basePath?: string;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface LoggingConfig {
|
|
67
|
+
level?: 'debug' | 'info' | 'warn' | 'error';
|
|
68
|
+
console?: boolean;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export interface AppConfig {
|
|
72
|
+
appName?: string;
|
|
73
|
+
debug?: boolean;
|
|
74
|
+
/** DOM id that route components are mounted into. Default: 'main-content'. */
|
|
75
|
+
mountId?: string;
|
|
76
|
+
router?: RouterConfig;
|
|
77
|
+
logging?: LoggingConfig;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export interface InitOptions {
|
|
81
|
+
/** Map of URL path → component class or route config for client-side routing. */
|
|
82
|
+
routes?: Record<string, ComponentClass | RouteConfig>;
|
|
83
|
+
/** Named component registry (for lookup by string in child()). */
|
|
84
|
+
components?: Record<string, ComponentClass>;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// ---------------------------------------------------------------------------
|
|
88
|
+
// Plugin
|
|
89
|
+
// ---------------------------------------------------------------------------
|
|
90
|
+
|
|
91
|
+
export interface Plugin {
|
|
92
|
+
name: string;
|
|
93
|
+
install(app: FrameworkApp, options?: AnyRecord): void;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export type PluginFunction = (app: FrameworkApp, options?: AnyRecord) => void;
|
|
97
|
+
|
|
98
|
+
// ---------------------------------------------------------------------------
|
|
99
|
+
// EventBus
|
|
100
|
+
// ---------------------------------------------------------------------------
|
|
101
|
+
|
|
102
|
+
export declare class EventBus {
|
|
103
|
+
/**
|
|
104
|
+
* Subscribe to an event. Returns an unsubscribe function.
|
|
105
|
+
* @param priority Higher numbers run first. Default: 0.
|
|
106
|
+
*/
|
|
107
|
+
on(event: string, handler: (data: unknown) => void, priority?: number): () => void;
|
|
108
|
+
/** Subscribe once — auto-unsubscribes after the first delivery. */
|
|
109
|
+
once(event: string, handler: (data: unknown) => void): () => void;
|
|
110
|
+
emit(event: string, data?: unknown): void;
|
|
111
|
+
off(event: string, handler: (data: unknown) => void): void;
|
|
112
|
+
cleanup(): void;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// ---------------------------------------------------------------------------
|
|
116
|
+
// Router
|
|
117
|
+
// ---------------------------------------------------------------------------
|
|
118
|
+
|
|
119
|
+
export interface RouteMatch {
|
|
120
|
+
path: string;
|
|
121
|
+
params: Record<string, string>;
|
|
122
|
+
query: Record<string, string>;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Context object passed to a route loader function.
|
|
127
|
+
*/
|
|
128
|
+
export interface RouteLoaderContext {
|
|
129
|
+
/** Dynamic segments captured from the URL (e.g. `{ id: '42' }` for `/users/:id`). */
|
|
130
|
+
params: Record<string, string>;
|
|
131
|
+
/** The matched URL path (without base path). */
|
|
132
|
+
path: string;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Async function that fetches data before a route component mounts.
|
|
137
|
+
* Its return value is available as `this.props.data` inside the component.
|
|
138
|
+
*/
|
|
139
|
+
export type RouteLoader = (context: RouteLoaderContext) => Promise<unknown>;
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Full route configuration object accepted by `Router.addRoute()`.
|
|
143
|
+
* Pass a plain `ComponentClass` as a shorthand when no extra options are needed.
|
|
144
|
+
*/
|
|
145
|
+
export interface RouteConfig {
|
|
146
|
+
component: ComponentClass;
|
|
147
|
+
/**
|
|
148
|
+
* Called before the component mounts. Its return value is available as
|
|
149
|
+
* `this.props.data` on the first render. When the loader throws, the
|
|
150
|
+
* component still mounts with `props.data` set to `undefined`.
|
|
151
|
+
*/
|
|
152
|
+
loader?: RouteLoader;
|
|
153
|
+
title?: string;
|
|
154
|
+
protected?: boolean;
|
|
155
|
+
beforeEnter?: (route: RouteMatch, path: string) => boolean | Promise<boolean>;
|
|
156
|
+
afterEnter?: (route: RouteMatch, path: string) => void | Promise<void>;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export declare class Router {
|
|
160
|
+
addRoute(path: string, component: ComponentClass | RouteConfig): void;
|
|
161
|
+
navigate(path: string, options?: AnyRecord): void;
|
|
162
|
+
beforeNavigation(callback: (route: RouteMatch, path: string) => boolean | Promise<boolean>): void;
|
|
163
|
+
afterNavigation(callback: (route: RouteMatch, path: string) => void | Promise<void>): void;
|
|
164
|
+
initialize(): Promise<void>;
|
|
165
|
+
start(): Promise<void>;
|
|
166
|
+
cleanup(): Promise<void>;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// ---------------------------------------------------------------------------
|
|
170
|
+
// Component
|
|
171
|
+
// ---------------------------------------------------------------------------
|
|
172
|
+
|
|
173
|
+
export type ComponentClass = new (
|
|
174
|
+
eventBus: EventBus,
|
|
175
|
+
props?: AnyRecord
|
|
176
|
+
) => BaseComponent;
|
|
177
|
+
|
|
178
|
+
export interface ComponentLifecycle {
|
|
179
|
+
onMount?: () => void | Promise<void>;
|
|
180
|
+
onUnmount?: () => void | Promise<void>;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export declare class BaseComponent {
|
|
184
|
+
/** Component name — used in logging and as the CSS class on the wrapper. */
|
|
185
|
+
name: string;
|
|
186
|
+
props: AnyRecord;
|
|
187
|
+
state: AnyRecord;
|
|
188
|
+
/** The outermost DOM wrapper managed by the framework. */
|
|
189
|
+
element: Element | null;
|
|
190
|
+
container: Element | null;
|
|
191
|
+
eventBus: EventBus;
|
|
192
|
+
/** The FrameworkApp instance. Set by ComponentManager on mount. */
|
|
193
|
+
app: FrameworkApp | null;
|
|
194
|
+
isInitialized: boolean;
|
|
195
|
+
isRendered: boolean;
|
|
196
|
+
isDestroyed: boolean;
|
|
197
|
+
|
|
198
|
+
constructor(eventBus: EventBus, props?: AnyRecord);
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Return an HTML string. Called on every render. Use `this.child()` inside
|
|
202
|
+
* it to embed child components declaratively.
|
|
203
|
+
*/
|
|
204
|
+
getTemplate(): string;
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Return a map of action name → handler function. Handlers are wired to DOM
|
|
208
|
+
* elements via data-action / data-change / data-input / data-keydown /
|
|
209
|
+
* data-submit attributes — no manual addEventListener needed.
|
|
210
|
+
*/
|
|
211
|
+
getMethods(): Record<string, (event?: Event) => void | Promise<void>>;
|
|
212
|
+
|
|
213
|
+
/** Override to register mount/unmount hooks. */
|
|
214
|
+
getLifecycle(): ComponentLifecycle;
|
|
215
|
+
|
|
216
|
+
/** Hook called once during init, before the first render. */
|
|
217
|
+
onInit(): Promise<void>;
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Merge `patch` into state and re-render via DOM morph.
|
|
221
|
+
* Pass `render = false` to update state silently (no re-render).
|
|
222
|
+
*/
|
|
223
|
+
setState(patch: Partial<AnyRecord>, render?: boolean): void;
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Reach a plugin service by name. Returns null when the service is absent
|
|
227
|
+
* so templates degrade gracefully without needing explicit null checks.
|
|
228
|
+
*/
|
|
229
|
+
service<T = unknown>(name: string): T | null;
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Render an inline SVG icon by name. Shortcut for
|
|
233
|
+
* `this.service('icons').render(name, options)`.
|
|
234
|
+
* Returns an empty string when the icons service is absent.
|
|
235
|
+
*/
|
|
236
|
+
icon(name: string, options?: IconOptions): string;
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Embed a child component at the current position in getTemplate().
|
|
240
|
+
* Returns a placeholder `<div data-vf-host>` that the framework replaces
|
|
241
|
+
* with the mounted child after each render.
|
|
242
|
+
*
|
|
243
|
+
* @param Component - Class or registered name string.
|
|
244
|
+
* @param props - Props passed to the child on mount / update.
|
|
245
|
+
* @param key - Stable identity key for list reconciliation.
|
|
246
|
+
*/
|
|
247
|
+
child(
|
|
248
|
+
Component: ComponentClass | string,
|
|
249
|
+
props?: AnyRecord,
|
|
250
|
+
key?: string | number
|
|
251
|
+
): string;
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Create a reactive signal linked to this component. When
|
|
255
|
+
* `signal.set(newValue)` is called, the component re-renders via the DOM
|
|
256
|
+
* morph (multiple calls in the same tick are batched into one render).
|
|
257
|
+
* The signal is automatically destroyed when the component is destroyed.
|
|
258
|
+
*/
|
|
259
|
+
signal<T>(initialValue: T): Signal<T>;
|
|
260
|
+
|
|
261
|
+
/** Emit an event on the shared EventBus. */
|
|
262
|
+
emit(event: string, data?: unknown): void;
|
|
263
|
+
|
|
264
|
+
/** Subscribe to an EventBus event. Returns an unsubscribe function. */
|
|
265
|
+
on(event: string, handler: (data: unknown) => void): () => void;
|
|
266
|
+
|
|
267
|
+
init(): Promise<void>;
|
|
268
|
+
render(container: Element): void;
|
|
269
|
+
cleanup(): Promise<void>;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// ---------------------------------------------------------------------------
|
|
273
|
+
// FrameworkApp
|
|
274
|
+
// ---------------------------------------------------------------------------
|
|
275
|
+
|
|
276
|
+
export declare class FrameworkApp {
|
|
277
|
+
config: AppConfig;
|
|
278
|
+
router: Router | null;
|
|
279
|
+
eventBus: EventBus;
|
|
280
|
+
isInitialized: boolean;
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Install a plugin. Idempotent — installing the same plugin (by name or
|
|
284
|
+
* reference) a second time is a no-op. Chainable.
|
|
285
|
+
*/
|
|
286
|
+
use(plugin: Plugin | PluginFunction, options?: AnyRecord): this;
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Register (or replace) a named service in the registry. Call this from a
|
|
290
|
+
* plugin's install() or from app-level setup code.
|
|
291
|
+
*/
|
|
292
|
+
provide(name: string, instance: unknown): this;
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Retrieve a service by name. Returns null when not found.
|
|
296
|
+
* Common names: 'icons', 'theme', 'alerts', 'fonts', 'store', 'router', 'eventBus'.
|
|
297
|
+
*/
|
|
298
|
+
get<T = unknown>(name: string): T | null;
|
|
299
|
+
|
|
300
|
+
navigate(path: string, options?: AnyRecord): void;
|
|
301
|
+
|
|
302
|
+
initialize(options?: InitOptions): Promise<void>;
|
|
303
|
+
start(): Promise<void>;
|
|
304
|
+
shutdown(): Promise<void>;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
export declare function createApp(config?: AppConfig): FrameworkApp;
|
|
308
|
+
|
|
309
|
+
// ---------------------------------------------------------------------------
|
|
310
|
+
// Icons plugin
|
|
311
|
+
// ---------------------------------------------------------------------------
|
|
312
|
+
|
|
313
|
+
export interface IconOptions {
|
|
314
|
+
/** Width and height in pixels. Default: 24. */
|
|
315
|
+
size?: number;
|
|
316
|
+
/** Extra CSS class on the <svg> element. */
|
|
317
|
+
className?: string;
|
|
318
|
+
/** Adds a <title> and role="img" for accessibility. */
|
|
319
|
+
title?: string;
|
|
320
|
+
/** Inline color override. Default: inherits currentColor. */
|
|
321
|
+
color?: string;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
export declare class IconsService {
|
|
325
|
+
/** Render an icon as an inline SVG string. Returns '' for unknown names. */
|
|
326
|
+
render(name: string, options?: IconOptions): string;
|
|
327
|
+
/** Register a custom icon SVG path string under `name`. */
|
|
328
|
+
register(name: string, svgPath: string): void;
|
|
329
|
+
/** Returns all registered icon names. */
|
|
330
|
+
list(): string[];
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
export declare const iconsPlugin: Plugin;
|
|
334
|
+
|
|
335
|
+
// ---------------------------------------------------------------------------
|
|
336
|
+
// Theme plugin
|
|
337
|
+
// ---------------------------------------------------------------------------
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Design token map. Keys are camelCase (e.g. 'primary', 'textMuted', 'fontSans').
|
|
341
|
+
* They are injected as --vf-<kebab-case> CSS custom properties on :root.
|
|
342
|
+
*/
|
|
343
|
+
export interface ThemeTokens {
|
|
344
|
+
primary?: string;
|
|
345
|
+
primaryDark?: string;
|
|
346
|
+
secondary?: string;
|
|
347
|
+
surface?: string;
|
|
348
|
+
background?: string;
|
|
349
|
+
text?: string;
|
|
350
|
+
textMuted?: string;
|
|
351
|
+
border?: string;
|
|
352
|
+
danger?: string;
|
|
353
|
+
success?: string;
|
|
354
|
+
warning?: string;
|
|
355
|
+
radius?: string;
|
|
356
|
+
radiusSm?: string;
|
|
357
|
+
radiusLg?: string;
|
|
358
|
+
fontSans?: string;
|
|
359
|
+
fontMono?: string;
|
|
360
|
+
shadowSm?: string;
|
|
361
|
+
shadowMd?: string;
|
|
362
|
+
shadowLg?: string;
|
|
363
|
+
space?: string;
|
|
364
|
+
[key: string]: string | undefined;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
export declare class ThemeService {
|
|
368
|
+
/** Live-update one or more design tokens. Changes are applied immediately to :root. */
|
|
369
|
+
setTokens(tokens: Partial<ThemeTokens>): void;
|
|
370
|
+
/** Read a token value by its camelCase name (e.g. 'primary', 'fontSans'). */
|
|
371
|
+
getToken(name: string): string | undefined;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
export interface ThemePluginOptions {
|
|
375
|
+
/** Token overrides applied on top of the 20 defaults. */
|
|
376
|
+
tokens?: Partial<ThemeTokens>;
|
|
377
|
+
/** Set to false to skip the base stylesheet (.vf-card, .vf-btn, etc.). Default: true. */
|
|
378
|
+
base?: boolean;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
export declare const themePlugin: Plugin;
|
|
382
|
+
|
|
383
|
+
// ---------------------------------------------------------------------------
|
|
384
|
+
// Alerts plugin
|
|
385
|
+
// ---------------------------------------------------------------------------
|
|
386
|
+
|
|
387
|
+
export interface ToastOptions {
|
|
388
|
+
/** How long (ms) before the toast auto-dismisses. */
|
|
389
|
+
duration?: number;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
export interface ConfirmOptions {
|
|
393
|
+
/** Optional heading displayed above the message. */
|
|
394
|
+
title?: string;
|
|
395
|
+
/** Styles the confirm button as destructive (red). Default: false. */
|
|
396
|
+
danger?: boolean;
|
|
397
|
+
/** Confirm button label. Default: 'Confirm'. */
|
|
398
|
+
confirmText?: string;
|
|
399
|
+
/** Cancel button label. Default: 'Cancel'. */
|
|
400
|
+
cancelText?: string;
|
|
401
|
+
/** Called when the user confirms (in addition to the returned Promise). */
|
|
402
|
+
onConfirm?: () => void;
|
|
403
|
+
/** Called when the user cancels. */
|
|
404
|
+
onCancel?: () => void;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
export declare class AlertsService {
|
|
408
|
+
success(message: string, options?: ToastOptions): void;
|
|
409
|
+
error(message: string, options?: ToastOptions): void;
|
|
410
|
+
warning(message: string, options?: ToastOptions): void;
|
|
411
|
+
info(message: string, options?: ToastOptions): void;
|
|
412
|
+
/** Shows a modal confirm dialog. Resolves true on confirm, false on cancel. */
|
|
413
|
+
confirm(message: string, options?: ConfirmOptions): Promise<boolean>;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
export declare const alertsPlugin: Plugin;
|
|
417
|
+
|
|
418
|
+
// ---------------------------------------------------------------------------
|
|
419
|
+
// Fonts plugin
|
|
420
|
+
// ---------------------------------------------------------------------------
|
|
421
|
+
|
|
422
|
+
export interface FontDescriptor {
|
|
423
|
+
/** Must match a bundled family name or one registered with addFamily(). */
|
|
424
|
+
name: string;
|
|
425
|
+
/**
|
|
426
|
+
* Weight subset. For variable fonts this sets the min/max of the
|
|
427
|
+
* font-weight range (e.g. [400, 700] → font-weight: 400 700).
|
|
428
|
+
*/
|
|
429
|
+
weights?: number[];
|
|
430
|
+
styles?: ('normal' | 'italic')[];
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
export interface FontManifest {
|
|
434
|
+
/** CSS font-family name used in @font-face declarations. */
|
|
435
|
+
cssFamily: string;
|
|
436
|
+
/** camelCase ThemeService token to update (e.g. 'fontSans'). */
|
|
437
|
+
themeToken?: string;
|
|
438
|
+
/** Fallback font stack appended when setting the theme token. */
|
|
439
|
+
fallback: string;
|
|
440
|
+
/** True when a single file covers all weights (variable font). */
|
|
441
|
+
variable: boolean;
|
|
442
|
+
weights: number[];
|
|
443
|
+
styles: string[];
|
|
444
|
+
/**
|
|
445
|
+
* Returns the bundled base64 data URI for a given style, or null to fall
|
|
446
|
+
* back to a URL-path source (requires the path option at install time).
|
|
447
|
+
*/
|
|
448
|
+
dataUri?: (style: string) => string | null;
|
|
449
|
+
/** Returns the filename for a given weight and style. */
|
|
450
|
+
filename: (weight: number | null, style: string) => string;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
export interface FontsPluginOptions {
|
|
454
|
+
/** Font families to load. Each entry is a name string or a descriptor object. */
|
|
455
|
+
families?: (string | FontDescriptor)[];
|
|
456
|
+
/**
|
|
457
|
+
* URL base for external font files (e.g. '/assets/fonts').
|
|
458
|
+
* When omitted, bundled data URIs are used for built-in families.
|
|
459
|
+
*/
|
|
460
|
+
path?: string;
|
|
461
|
+
/** CSS font-display value. Default: 'swap'. */
|
|
462
|
+
display?: string;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
export declare class FontsService {
|
|
466
|
+
constructor(options?: FontsPluginOptions);
|
|
467
|
+
/** Returns CSS family names of all fonts that have been loaded. */
|
|
468
|
+
getFamilies(): string[];
|
|
469
|
+
/**
|
|
470
|
+
* Register a custom font family and inject its @font-face block.
|
|
471
|
+
* Returns this for chaining.
|
|
472
|
+
*/
|
|
473
|
+
addFamily(name: string, manifest: FontManifest): this;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
export declare const fontsPlugin: Plugin;
|
|
477
|
+
|
|
478
|
+
// ---------------------------------------------------------------------------
|
|
479
|
+
// Store plugin
|
|
480
|
+
// ---------------------------------------------------------------------------
|
|
481
|
+
|
|
482
|
+
export declare class StoreService {
|
|
483
|
+
/**
|
|
484
|
+
* Write a value. Identical values (via Object.is) are ignored — no events fired.
|
|
485
|
+
*/
|
|
486
|
+
set(key: string, value: unknown): void;
|
|
487
|
+
|
|
488
|
+
/**
|
|
489
|
+
* Read the current value. Returns `undefined` when the key has never been written.
|
|
490
|
+
*/
|
|
491
|
+
get(key: string): unknown;
|
|
492
|
+
|
|
493
|
+
/**
|
|
494
|
+
* Subscribe to changes for a single key.
|
|
495
|
+
* The handler receives `(value, prev)` on each change.
|
|
496
|
+
* Returns an unsubscribe function.
|
|
497
|
+
*/
|
|
498
|
+
subscribe(key: string, handler: (value: unknown, prev: unknown) => void): () => void;
|
|
499
|
+
|
|
500
|
+
/**
|
|
501
|
+
* Subscribe to ALL store changes.
|
|
502
|
+
* The handler receives `(key, value, prev)` on each write.
|
|
503
|
+
* Returns an unsubscribe function.
|
|
504
|
+
*/
|
|
505
|
+
subscribeAll(handler: (key: string, value: unknown, prev: unknown) => void): () => void;
|
|
506
|
+
|
|
507
|
+
/**
|
|
508
|
+
* Remove a key and fire change events with `value: undefined`.
|
|
509
|
+
* No-op when the key does not exist.
|
|
510
|
+
*/
|
|
511
|
+
delete(key: string): void;
|
|
512
|
+
|
|
513
|
+
/** Returns all keys currently stored. */
|
|
514
|
+
keys(): string[];
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
export declare const storePlugin: Plugin;
|
|
518
|
+
|
|
519
|
+
// ---------------------------------------------------------------------------
|
|
520
|
+
// Constants
|
|
521
|
+
// ---------------------------------------------------------------------------
|
|
522
|
+
|
|
523
|
+
export declare const FRAMEWORK_VERSION: string;
|
|
524
|
+
export declare const FRAMEWORK_NAME: string;
|