@pyreon/ui-core 0.0.2
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 +145 -0
- package/lib/index.d.ts +257 -0
- package/lib/index.js +560 -0
- package/package.json +42 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025-present Vit Bokisch
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# @pyreon/ui-core
|
|
2
|
+
|
|
3
|
+
Shared foundation for the Pyreon UI System ecosystem.
|
|
4
|
+
|
|
5
|
+
Provides utility functions, a styling engine bridge, theme context, and HTML tag definitions used across all `@pyreon` packages. No external utility dependencies — all implementations are built-in with prototype pollution protection where applicable.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
bun add @pyreon/ui-core
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## API
|
|
14
|
+
|
|
15
|
+
### Provider & Context
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { Provider, context, config, init } from '@pyreon/ui-core'
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
**Provider** wraps your app with a theme context. It bridges `@pyreon/styler`'s theming with the internal context system.
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
import { Provider } from '@pyreon/ui-core'
|
|
25
|
+
|
|
26
|
+
Provider({
|
|
27
|
+
theme: { rootSize: 16, breakpoints: { xs: 0, md: 768 } },
|
|
28
|
+
children: [/* your app */],
|
|
29
|
+
})
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
**config** — the styling engine singleton. Pyreon uses `@pyreon/styler` directly — no connector abstraction needed.
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
import { config } from '@pyreon/ui-core'
|
|
36
|
+
|
|
37
|
+
// Access the engine from anywhere
|
|
38
|
+
const { styled, css, keyframes } = config
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Utilities
|
|
42
|
+
|
|
43
|
+
#### compose
|
|
44
|
+
|
|
45
|
+
Right-to-left function composition.
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
import { compose } from '@pyreon/ui-core'
|
|
49
|
+
|
|
50
|
+
const transform = compose(toUpperCase, trim, normalize)
|
|
51
|
+
transform(' hello ') // => 'HELLO'
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
#### render
|
|
55
|
+
|
|
56
|
+
Flexible element renderer. Handles components, elements, primitives, and arrays.
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
import { render } from '@pyreon/ui-core'
|
|
60
|
+
|
|
61
|
+
render('hello') // => 'hello'
|
|
62
|
+
render(MyComponent) // => MyComponent({})
|
|
63
|
+
render(null) // => null
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
#### isEmpty
|
|
67
|
+
|
|
68
|
+
Type-safe emptiness check. Returns `true` for `null`, `undefined`, `{}`, `[]`, and non-object primitives.
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
import { isEmpty } from '@pyreon/ui-core'
|
|
72
|
+
|
|
73
|
+
isEmpty({}) // => true
|
|
74
|
+
isEmpty([]) // => true
|
|
75
|
+
isEmpty(null) // => true
|
|
76
|
+
isEmpty({ a: 1 }) // => false
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
#### omit / pick
|
|
80
|
+
|
|
81
|
+
Create objects without or with only specified keys. Accept nullable inputs.
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
import { omit, pick } from '@pyreon/ui-core'
|
|
85
|
+
|
|
86
|
+
omit({ a: 1, b: 2, c: 3 }, ['b']) // => { a: 1, c: 3 }
|
|
87
|
+
pick({ a: 1, b: 2, c: 3 }, ['a', 'b']) // => { a: 1, b: 2 }
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
#### set / get
|
|
91
|
+
|
|
92
|
+
Nested property access and mutation by dot/bracket path. `set` has built-in prototype pollution protection — keys like `__proto__`, `constructor`, and `prototype` are blocked.
|
|
93
|
+
|
|
94
|
+
```ts
|
|
95
|
+
import { set, get } from '@pyreon/ui-core'
|
|
96
|
+
|
|
97
|
+
const obj = {}
|
|
98
|
+
set(obj, 'a.b.c', 42) // => { a: { b: { c: 42 } } }
|
|
99
|
+
get(obj, 'a.b.c') // => 42
|
|
100
|
+
get(obj, 'a.x', 'default') // => 'default'
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
#### merge
|
|
104
|
+
|
|
105
|
+
Deep merge objects left-to-right. Only plain objects are recursed into; arrays are replaced wholesale. Prototype pollution keys are blocked.
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
import { merge } from '@pyreon/ui-core'
|
|
109
|
+
|
|
110
|
+
merge({ a: { x: 1 } }, { a: { y: 2 } }) // => { a: { x: 1, y: 2 } }
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
#### throttle
|
|
114
|
+
|
|
115
|
+
Limits function execution to at most once per wait period. Returns a throttled function with a `.cancel()` method.
|
|
116
|
+
|
|
117
|
+
```ts
|
|
118
|
+
import { throttle } from '@pyreon/ui-core'
|
|
119
|
+
|
|
120
|
+
const throttled = throttle(handleResize, 200)
|
|
121
|
+
window.addEventListener('resize', throttled)
|
|
122
|
+
// cleanup: throttled.cancel()
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### HTML Constants
|
|
126
|
+
|
|
127
|
+
```ts
|
|
128
|
+
import { HTML_TAGS, HTML_TEXT_TAGS } from '@pyreon/ui-core'
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
- **HTML_TAGS** — array of 100+ valid HTML tag names
|
|
132
|
+
- **HTML_TEXT_TAGS** — array of text-content tags (h1–h6, p, span, strong, em, etc.)
|
|
133
|
+
|
|
134
|
+
Both have corresponding TypeScript union types: `HTMLTags` and `HTMLTextTags`.
|
|
135
|
+
|
|
136
|
+
## Peer Dependencies
|
|
137
|
+
|
|
138
|
+
| Package | Version |
|
|
139
|
+
| ------- | ------- |
|
|
140
|
+
| @pyreon/core | >= 0.0.1 |
|
|
141
|
+
| @pyreon/styler | >= 0.0.1 |
|
|
142
|
+
|
|
143
|
+
## License
|
|
144
|
+
|
|
145
|
+
MIT
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
import * as _pyreon_styler0 from "@pyreon/styler";
|
|
2
|
+
import { StyledFunction, css, keyframes, styled } from "@pyreon/styler";
|
|
3
|
+
import * as _pyreon_core0 from "@pyreon/core";
|
|
4
|
+
import { ComponentFn, VNodeChild } from "@pyreon/core";
|
|
5
|
+
|
|
6
|
+
//#region src/compose.d.ts
|
|
7
|
+
type ArityOneFn = (arg: any) => any;
|
|
8
|
+
type PickLastInTuple<T extends any[]> = T extends [...rest: infer _U, argn: infer L] ? L : any;
|
|
9
|
+
type FirstFnParameterType<T extends any[]> = Parameters<PickLastInTuple<T>>[any];
|
|
10
|
+
type LastFnReturnType<T extends any[]> = ReturnType<T[0]>;
|
|
11
|
+
declare const compose: <T extends ArityOneFn[]>(...fns: T) => (p: FirstFnParameterType<T>) => LastFnReturnType<T>;
|
|
12
|
+
//#endregion
|
|
13
|
+
//#region src/html/htmlElementAttrs.d.ts
|
|
14
|
+
type Base = HTMLElement;
|
|
15
|
+
interface HTMLElementAttrs {
|
|
16
|
+
a: HTMLAnchorElement;
|
|
17
|
+
abbr: Base;
|
|
18
|
+
address: Base;
|
|
19
|
+
area: HTMLAreaElement;
|
|
20
|
+
article: Base;
|
|
21
|
+
aside: Base;
|
|
22
|
+
audio: HTMLAudioElement;
|
|
23
|
+
b: Base;
|
|
24
|
+
bdi: Base;
|
|
25
|
+
bdo: Base;
|
|
26
|
+
big: Base;
|
|
27
|
+
blockquote: HTMLQuoteElement;
|
|
28
|
+
body: HTMLBodyElement;
|
|
29
|
+
br: HTMLBRElement;
|
|
30
|
+
button: HTMLButtonElement;
|
|
31
|
+
canvas: HTMLCanvasElement;
|
|
32
|
+
caption: Base;
|
|
33
|
+
cite: HTMLQuoteElement;
|
|
34
|
+
code: Base;
|
|
35
|
+
col: HTMLTableColElement;
|
|
36
|
+
colgroup: HTMLTableColElement;
|
|
37
|
+
data: HTMLDataElement;
|
|
38
|
+
datalist: HTMLDataListElement;
|
|
39
|
+
dd: Base;
|
|
40
|
+
del: HTMLModElement;
|
|
41
|
+
details: HTMLDetailsElement;
|
|
42
|
+
dfn: Base;
|
|
43
|
+
dialog: HTMLDialogElement;
|
|
44
|
+
div: HTMLDivElement;
|
|
45
|
+
dl: HTMLDListElement;
|
|
46
|
+
dt: Base;
|
|
47
|
+
em: Base;
|
|
48
|
+
embed: HTMLEmbedElement;
|
|
49
|
+
fieldset: HTMLFieldSetElement;
|
|
50
|
+
figcaption: Base;
|
|
51
|
+
figure: Base;
|
|
52
|
+
footer: Base;
|
|
53
|
+
form: HTMLFormElement;
|
|
54
|
+
h1: HTMLHeadingElement;
|
|
55
|
+
h2: HTMLHeadingElement;
|
|
56
|
+
h3: HTMLHeadingElement;
|
|
57
|
+
h4: HTMLHeadingElement;
|
|
58
|
+
h5: HTMLHeadingElement;
|
|
59
|
+
h6: HTMLHeadingElement;
|
|
60
|
+
header: Base;
|
|
61
|
+
hr: HTMLHRElement;
|
|
62
|
+
html: HTMLHtmlElement;
|
|
63
|
+
i: Base;
|
|
64
|
+
iframe: HTMLIFrameElement;
|
|
65
|
+
img: HTMLImageElement;
|
|
66
|
+
input: HTMLInputElement;
|
|
67
|
+
ins: HTMLModElement;
|
|
68
|
+
kbd: Base;
|
|
69
|
+
label: HTMLLabelElement;
|
|
70
|
+
legend: HTMLLegendElement;
|
|
71
|
+
li: HTMLLIElement;
|
|
72
|
+
main: Base;
|
|
73
|
+
map: HTMLMapElement;
|
|
74
|
+
mark: Base;
|
|
75
|
+
meter: HTMLMeterElement;
|
|
76
|
+
nav: Base;
|
|
77
|
+
object: HTMLObjectElement;
|
|
78
|
+
ol: HTMLOListElement;
|
|
79
|
+
optgroup: HTMLOptGroupElement;
|
|
80
|
+
option: HTMLOptionElement;
|
|
81
|
+
output: HTMLOutputElement;
|
|
82
|
+
p: HTMLParagraphElement;
|
|
83
|
+
picture: Base;
|
|
84
|
+
pre: HTMLPreElement;
|
|
85
|
+
progress: HTMLProgressElement;
|
|
86
|
+
q: HTMLQuoteElement;
|
|
87
|
+
rp: Base;
|
|
88
|
+
rt: Base;
|
|
89
|
+
ruby: Base;
|
|
90
|
+
s: Base;
|
|
91
|
+
samp: Base;
|
|
92
|
+
section: Base;
|
|
93
|
+
select: HTMLSelectElement;
|
|
94
|
+
small: Base;
|
|
95
|
+
source: HTMLSourceElement;
|
|
96
|
+
span: HTMLSpanElement;
|
|
97
|
+
strong: Base;
|
|
98
|
+
sub: Base;
|
|
99
|
+
summary: Base;
|
|
100
|
+
sup: Base;
|
|
101
|
+
svg: SVGSVGElement;
|
|
102
|
+
table: HTMLTableElement;
|
|
103
|
+
tbody: HTMLTableSectionElement;
|
|
104
|
+
td: HTMLTableCellElement;
|
|
105
|
+
template: HTMLTemplateElement;
|
|
106
|
+
textarea: HTMLTextAreaElement;
|
|
107
|
+
tfoot: HTMLTableSectionElement;
|
|
108
|
+
th: HTMLTableCellElement;
|
|
109
|
+
thead: HTMLTableSectionElement;
|
|
110
|
+
time: HTMLTimeElement;
|
|
111
|
+
tr: HTMLTableRowElement;
|
|
112
|
+
track: HTMLTrackElement;
|
|
113
|
+
u: Base;
|
|
114
|
+
ul: HTMLUListElement;
|
|
115
|
+
var: Base;
|
|
116
|
+
video: HTMLVideoElement;
|
|
117
|
+
wbr: Base;
|
|
118
|
+
}
|
|
119
|
+
//#endregion
|
|
120
|
+
//#region src/html/htmlTags.d.ts
|
|
121
|
+
declare const HTML_TAGS: readonly ["a", "abbr", "address", "area", "article", "aside", "audio", "b", "bdi", "bdo", "big", "blockquote", "body", "br", "button", "canvas", "caption", "cite", "code", "col", "colgroup", "data", "datalist", "dd", "del", "details", "dfn", "dialog", "div", "dl", "dt", "em", "embed", "fieldset", "figcaption", "figure", "footer", "form", "h1", "h2", "h3", "h4", "h5", "h6", "header", "hr", "html", "i", "iframe", "img", "input", "ins", "kbd", "label", "legend", "li", "main", "map", "mark", "meter", "nav", "object", "ol", "optgroup", "option", "output", "p", "picture", "pre", "progress", "q", "rp", "rt", "ruby", "s", "samp", "section", "select", "small", "source", "span", "strong", "sub", "summary", "sup", "svg", "table", "tbody", "td", "template", "textarea", "tfoot", "th", "thead", "time", "tr", "track", "u", "ul", "var", "video", "wbr"];
|
|
122
|
+
declare const HTML_TEXT_TAGS: readonly ["abbr", "b", "bdi", "bdo", "big", "blockquote", "cite", "code", "del", "div", "dl", "dt", "em", "figcaption", "h1", "h2", "h3", "h4", "h5", "h6", "i", "ins", "kbd", "label", "legend", "li", "p", "pre", "q", "rp", "rt", "s", "small", "span", "strong", "sub", "summary", "sup", "time", "u"];
|
|
123
|
+
type HTMLTags = (typeof HTML_TAGS)[number];
|
|
124
|
+
type HTMLTextTags = (typeof HTML_TEXT_TAGS)[number];
|
|
125
|
+
//#endregion
|
|
126
|
+
//#region src/html/index.d.ts
|
|
127
|
+
type HTMLTagAttrsByTag<T extends HTMLTags> = T extends HTMLTags ? HTMLElementAttrs[T] : Record<string, never>;
|
|
128
|
+
//#endregion
|
|
129
|
+
//#region src/config.d.ts
|
|
130
|
+
/**
|
|
131
|
+
* Describes the shape of the CSS-in-JS engine.
|
|
132
|
+
* Pyreon uses @pyreon/styler directly — no connector abstraction needed.
|
|
133
|
+
* This type is kept for API compatibility with downstream packages.
|
|
134
|
+
*/
|
|
135
|
+
interface CSSEngineConnector {
|
|
136
|
+
css: typeof css;
|
|
137
|
+
styled: typeof styled;
|
|
138
|
+
keyframes: typeof keyframes;
|
|
139
|
+
}
|
|
140
|
+
interface PlatformConfig {
|
|
141
|
+
component: string | HTMLTags;
|
|
142
|
+
textComponent: string | HTMLTags;
|
|
143
|
+
createMediaQueries?: (props: {
|
|
144
|
+
breakpoints: Record<string, number>;
|
|
145
|
+
rootSize: number;
|
|
146
|
+
css: CSSEngineConnector["css"];
|
|
147
|
+
}) => Record<string, (...args: any[]) => any>;
|
|
148
|
+
}
|
|
149
|
+
type InitConfig = Partial<CSSEngineConnector & PlatformConfig>;
|
|
150
|
+
/**
|
|
151
|
+
* Configuration singleton that bridges the UI system with the CSS engine.
|
|
152
|
+
* All packages reference config.css, config.styled, etc.
|
|
153
|
+
*
|
|
154
|
+
* In Pyreon, the engine is @pyreon/styler and is available immediately —
|
|
155
|
+
* no lazy initialization or connector pattern needed.
|
|
156
|
+
*/
|
|
157
|
+
declare class Configuration {
|
|
158
|
+
css: (strings: TemplateStringsArray, ...values: _pyreon_styler0.Interpolation[]) => _pyreon_styler0.CSSResult;
|
|
159
|
+
styled: StyledFunction;
|
|
160
|
+
keyframes: (strings: TemplateStringsArray, ...values: _pyreon_styler0.Interpolation[]) => {
|
|
161
|
+
readonly name: string;
|
|
162
|
+
toString(): string;
|
|
163
|
+
};
|
|
164
|
+
component: string | HTMLTags;
|
|
165
|
+
textComponent: string | HTMLTags;
|
|
166
|
+
createMediaQueries: PlatformConfig["createMediaQueries"];
|
|
167
|
+
init: (props: InitConfig) => void;
|
|
168
|
+
}
|
|
169
|
+
declare const config: Configuration;
|
|
170
|
+
declare const init: (props: InitConfig) => void;
|
|
171
|
+
//#endregion
|
|
172
|
+
//#region src/types.d.ts
|
|
173
|
+
interface Breakpoints {
|
|
174
|
+
[key: string]: number;
|
|
175
|
+
}
|
|
176
|
+
type BreakpointKeys = keyof Breakpoints;
|
|
177
|
+
//#endregion
|
|
178
|
+
//#region src/context.d.ts
|
|
179
|
+
/**
|
|
180
|
+
* Internal context shared across all @pyreon packages.
|
|
181
|
+
* Carries the theme object plus any extra provider props.
|
|
182
|
+
*/
|
|
183
|
+
declare const context: _pyreon_core0.Context<any>;
|
|
184
|
+
type Theme = Partial<{
|
|
185
|
+
rootSize: number;
|
|
186
|
+
breakpoints: Breakpoints;
|
|
187
|
+
} & Record<string, any>>;
|
|
188
|
+
type ProviderType = Partial<{
|
|
189
|
+
theme: Theme;
|
|
190
|
+
children: VNodeChild;
|
|
191
|
+
} & Record<string, any>>;
|
|
192
|
+
/**
|
|
193
|
+
* Provider that feeds the internal Pyreon context with the theme.
|
|
194
|
+
* When no theme is supplied, renders children directly.
|
|
195
|
+
*/
|
|
196
|
+
declare function Provider({
|
|
197
|
+
theme,
|
|
198
|
+
children,
|
|
199
|
+
...props
|
|
200
|
+
}: ProviderType): VNodeChild;
|
|
201
|
+
//#endregion
|
|
202
|
+
//#region src/hoistNonReactStatics.d.ts
|
|
203
|
+
/**
|
|
204
|
+
* Copies non-framework static properties from a source component to a target.
|
|
205
|
+
*
|
|
206
|
+
* Pyreon equivalent of hoistNonReactStatics — simplified since Pyreon
|
|
207
|
+
* components are plain functions without React-specific statics like
|
|
208
|
+
* contextType, propTypes, getDerivedStateFromProps, etc.
|
|
209
|
+
*/
|
|
210
|
+
declare const hoistNonReactStatics: <T, S>(target: T, source: S, excludeList?: Record<string, true>) => T;
|
|
211
|
+
//#endregion
|
|
212
|
+
//#region src/isEmpty.d.ts
|
|
213
|
+
type IsEmpty = <T extends Record<number | string, any> | any[] | null | undefined>(param: T) => T extends null | undefined ? true : keyof T extends never ? true : T extends T[] ? T[number] extends never ? true : false : false;
|
|
214
|
+
declare const isEmpty: IsEmpty;
|
|
215
|
+
//#endregion
|
|
216
|
+
//#region src/isEqual.d.ts
|
|
217
|
+
declare const isEqual: (a: unknown, b: unknown) => boolean;
|
|
218
|
+
//#endregion
|
|
219
|
+
//#region src/render.d.ts
|
|
220
|
+
type RenderProps<T extends Record<string, unknown> | undefined> = (props: Partial<T>) => VNodeChild;
|
|
221
|
+
/**
|
|
222
|
+
* Flexible element renderer that handles multiple content types:
|
|
223
|
+
* - Primitives (string, number) — returned as-is
|
|
224
|
+
* - Arrays — returned as-is
|
|
225
|
+
* - Component functions — created via h()
|
|
226
|
+
* - VNode objects — returned as-is (with props merged if provided)
|
|
227
|
+
* - Render props (functions) — called with attachProps
|
|
228
|
+
* - Falsy values — return null
|
|
229
|
+
*/
|
|
230
|
+
type Render = <T extends Record<string, any> | undefined>(content?: ComponentFn | string | VNodeChild | VNodeChild[] | RenderProps<T>, attachProps?: T) => VNodeChild;
|
|
231
|
+
declare const render: Render;
|
|
232
|
+
//#endregion
|
|
233
|
+
//#region src/useStableValue.d.ts
|
|
234
|
+
/**
|
|
235
|
+
* Returns a referentially stable version of `value`. The returned reference
|
|
236
|
+
* only changes when the value is no longer deeply equal to the previous one.
|
|
237
|
+
*
|
|
238
|
+
* Pyreon equivalent of the React useStableValue hook — uses a signal
|
|
239
|
+
* internally to hold the stable reference.
|
|
240
|
+
*/
|
|
241
|
+
declare const useStableValue: <T>(value: T) => T;
|
|
242
|
+
//#endregion
|
|
243
|
+
//#region src/utils.d.ts
|
|
244
|
+
declare const omit: <T extends Record<string, any>>(obj: T | null | undefined, keys?: readonly (string | keyof T)[]) => Partial<T>;
|
|
245
|
+
declare const pick: <T extends Record<string, any>>(obj: T | null | undefined, keys?: readonly (string | keyof T)[]) => Partial<T>;
|
|
246
|
+
declare const get: (obj: any, path: string | string[], defaultValue?: any) => any;
|
|
247
|
+
declare const set: (obj: Record<string, any>, path: string | string[], value: any) => Record<string, any>;
|
|
248
|
+
declare const throttle: <T extends (...args: any[]) => any>(fn: T, wait?: number, options?: {
|
|
249
|
+
leading?: boolean;
|
|
250
|
+
trailing?: boolean;
|
|
251
|
+
}) => T & {
|
|
252
|
+
cancel: () => void;
|
|
253
|
+
};
|
|
254
|
+
declare const merge: <T extends Record<string, any>>(target: T, ...sources: Record<string, any>[]) => T;
|
|
255
|
+
//#endregion
|
|
256
|
+
export { type BreakpointKeys, type Breakpoints, type CSSEngineConnector, type HTMLElementAttrs, type HTMLTagAttrsByTag, type HTMLTags, type HTMLTextTags, HTML_TAGS, HTML_TEXT_TAGS, type IsEmpty, Provider, type Render, compose, config, context, get, hoistNonReactStatics, init, isEmpty, isEqual, merge, omit, pick, render, set, throttle, useStableValue };
|
|
257
|
+
//# sourceMappingURL=index2.d.ts.map
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,560 @@
|
|
|
1
|
+
import { css, keyframes, styled } from "@pyreon/styler";
|
|
2
|
+
import { createContext, h, onUnmount, popContext, pushContext } from "@pyreon/core";
|
|
3
|
+
|
|
4
|
+
//#region src/compose.ts
|
|
5
|
+
const compose = (...fns) => (p) => fns.reduceRight((acc, cur) => cur(acc), p);
|
|
6
|
+
|
|
7
|
+
//#endregion
|
|
8
|
+
//#region src/config.ts
|
|
9
|
+
/**
|
|
10
|
+
* Configuration singleton that bridges the UI system with the CSS engine.
|
|
11
|
+
* All packages reference config.css, config.styled, etc.
|
|
12
|
+
*
|
|
13
|
+
* In Pyreon, the engine is @pyreon/styler and is available immediately —
|
|
14
|
+
* no lazy initialization or connector pattern needed.
|
|
15
|
+
*/
|
|
16
|
+
var Configuration = class {
|
|
17
|
+
css = css;
|
|
18
|
+
styled = styled;
|
|
19
|
+
keyframes = keyframes;
|
|
20
|
+
component = "div";
|
|
21
|
+
textComponent = "span";
|
|
22
|
+
createMediaQueries = void 0;
|
|
23
|
+
init = (props) => {
|
|
24
|
+
if (props.css) this.css = props.css;
|
|
25
|
+
if (props.styled) this.styled = props.styled;
|
|
26
|
+
if (props.keyframes) this.keyframes = props.keyframes;
|
|
27
|
+
if (props.component) this.component = props.component;
|
|
28
|
+
if (props.textComponent) this.textComponent = props.textComponent;
|
|
29
|
+
if (props.createMediaQueries) this.createMediaQueries = props.createMediaQueries;
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
const config = new Configuration();
|
|
33
|
+
const { init } = config;
|
|
34
|
+
|
|
35
|
+
//#endregion
|
|
36
|
+
//#region src/isEmpty.ts
|
|
37
|
+
const isEmpty = ((param) => {
|
|
38
|
+
if (!param) return true;
|
|
39
|
+
if (typeof param !== "object") return true;
|
|
40
|
+
if (Array.isArray(param)) return param.length === 0;
|
|
41
|
+
return Object.keys(param).length === 0;
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
//#endregion
|
|
45
|
+
//#region src/context.tsx
|
|
46
|
+
/**
|
|
47
|
+
* Internal context shared across all @pyreon packages.
|
|
48
|
+
* Carries the theme object plus any extra provider props.
|
|
49
|
+
*/
|
|
50
|
+
const context = createContext({});
|
|
51
|
+
/**
|
|
52
|
+
* Provider that feeds the internal Pyreon context with the theme.
|
|
53
|
+
* When no theme is supplied, renders children directly.
|
|
54
|
+
*/
|
|
55
|
+
function Provider({ theme, children, ...props }) {
|
|
56
|
+
if (isEmpty(theme) || !theme) return children ?? null;
|
|
57
|
+
const contextValue = {
|
|
58
|
+
theme,
|
|
59
|
+
...props
|
|
60
|
+
};
|
|
61
|
+
pushContext(new Map([[context.id, contextValue]]));
|
|
62
|
+
onUnmount(() => popContext());
|
|
63
|
+
return children ?? null;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
//#endregion
|
|
67
|
+
//#region src/hoistNonReactStatics.ts
|
|
68
|
+
const KNOWN_STATICS = {
|
|
69
|
+
name: true,
|
|
70
|
+
length: true,
|
|
71
|
+
prototype: true,
|
|
72
|
+
caller: true,
|
|
73
|
+
callee: true,
|
|
74
|
+
arguments: true,
|
|
75
|
+
arity: true
|
|
76
|
+
};
|
|
77
|
+
const COMPONENT_STATICS = {
|
|
78
|
+
displayName: true,
|
|
79
|
+
defaultProps: true
|
|
80
|
+
};
|
|
81
|
+
/**
|
|
82
|
+
* Copies non-framework static properties from a source component to a target.
|
|
83
|
+
*
|
|
84
|
+
* Pyreon equivalent of hoistNonReactStatics — simplified since Pyreon
|
|
85
|
+
* components are plain functions without React-specific statics like
|
|
86
|
+
* contextType, propTypes, getDerivedStateFromProps, etc.
|
|
87
|
+
*/
|
|
88
|
+
const hoistNonReactStatics = (target, source, excludeList) => {
|
|
89
|
+
if (typeof source === "string") return target;
|
|
90
|
+
const proto = Object.getPrototypeOf(source);
|
|
91
|
+
if (proto && proto !== Object.prototype) hoistNonReactStatics(target, proto, excludeList);
|
|
92
|
+
const keys = [...Object.getOwnPropertyNames(source), ...Object.getOwnPropertySymbols(source)];
|
|
93
|
+
for (const key of keys) {
|
|
94
|
+
const k = key;
|
|
95
|
+
if (KNOWN_STATICS[k] || excludeList?.[k] || COMPONENT_STATICS[k]) continue;
|
|
96
|
+
const descriptor = Object.getOwnPropertyDescriptor(source, key);
|
|
97
|
+
if (descriptor) try {
|
|
98
|
+
Object.defineProperty(target, key, descriptor);
|
|
99
|
+
} catch {}
|
|
100
|
+
}
|
|
101
|
+
return target;
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
//#endregion
|
|
105
|
+
//#region src/html/htmlTags.ts
|
|
106
|
+
const HTML_TAGS = [
|
|
107
|
+
"a",
|
|
108
|
+
"abbr",
|
|
109
|
+
"address",
|
|
110
|
+
"area",
|
|
111
|
+
"article",
|
|
112
|
+
"aside",
|
|
113
|
+
"audio",
|
|
114
|
+
"b",
|
|
115
|
+
"bdi",
|
|
116
|
+
"bdo",
|
|
117
|
+
"big",
|
|
118
|
+
"blockquote",
|
|
119
|
+
"body",
|
|
120
|
+
"br",
|
|
121
|
+
"button",
|
|
122
|
+
"canvas",
|
|
123
|
+
"caption",
|
|
124
|
+
"cite",
|
|
125
|
+
"code",
|
|
126
|
+
"col",
|
|
127
|
+
"colgroup",
|
|
128
|
+
"data",
|
|
129
|
+
"datalist",
|
|
130
|
+
"dd",
|
|
131
|
+
"del",
|
|
132
|
+
"details",
|
|
133
|
+
"dfn",
|
|
134
|
+
"dialog",
|
|
135
|
+
"div",
|
|
136
|
+
"dl",
|
|
137
|
+
"dt",
|
|
138
|
+
"em",
|
|
139
|
+
"embed",
|
|
140
|
+
"fieldset",
|
|
141
|
+
"figcaption",
|
|
142
|
+
"figure",
|
|
143
|
+
"footer",
|
|
144
|
+
"form",
|
|
145
|
+
"h1",
|
|
146
|
+
"h2",
|
|
147
|
+
"h3",
|
|
148
|
+
"h4",
|
|
149
|
+
"h5",
|
|
150
|
+
"h6",
|
|
151
|
+
"header",
|
|
152
|
+
"hr",
|
|
153
|
+
"html",
|
|
154
|
+
"i",
|
|
155
|
+
"iframe",
|
|
156
|
+
"img",
|
|
157
|
+
"input",
|
|
158
|
+
"ins",
|
|
159
|
+
"kbd",
|
|
160
|
+
"label",
|
|
161
|
+
"legend",
|
|
162
|
+
"li",
|
|
163
|
+
"main",
|
|
164
|
+
"map",
|
|
165
|
+
"mark",
|
|
166
|
+
"meter",
|
|
167
|
+
"nav",
|
|
168
|
+
"object",
|
|
169
|
+
"ol",
|
|
170
|
+
"optgroup",
|
|
171
|
+
"option",
|
|
172
|
+
"output",
|
|
173
|
+
"p",
|
|
174
|
+
"picture",
|
|
175
|
+
"pre",
|
|
176
|
+
"progress",
|
|
177
|
+
"q",
|
|
178
|
+
"rp",
|
|
179
|
+
"rt",
|
|
180
|
+
"ruby",
|
|
181
|
+
"s",
|
|
182
|
+
"samp",
|
|
183
|
+
"section",
|
|
184
|
+
"select",
|
|
185
|
+
"small",
|
|
186
|
+
"source",
|
|
187
|
+
"span",
|
|
188
|
+
"strong",
|
|
189
|
+
"sub",
|
|
190
|
+
"summary",
|
|
191
|
+
"sup",
|
|
192
|
+
"svg",
|
|
193
|
+
"table",
|
|
194
|
+
"tbody",
|
|
195
|
+
"td",
|
|
196
|
+
"template",
|
|
197
|
+
"textarea",
|
|
198
|
+
"tfoot",
|
|
199
|
+
"th",
|
|
200
|
+
"thead",
|
|
201
|
+
"time",
|
|
202
|
+
"tr",
|
|
203
|
+
"track",
|
|
204
|
+
"u",
|
|
205
|
+
"ul",
|
|
206
|
+
"var",
|
|
207
|
+
"video",
|
|
208
|
+
"wbr"
|
|
209
|
+
];
|
|
210
|
+
const HTML_TEXT_TAGS = [
|
|
211
|
+
"abbr",
|
|
212
|
+
"b",
|
|
213
|
+
"bdi",
|
|
214
|
+
"bdo",
|
|
215
|
+
"big",
|
|
216
|
+
"blockquote",
|
|
217
|
+
"cite",
|
|
218
|
+
"code",
|
|
219
|
+
"del",
|
|
220
|
+
"div",
|
|
221
|
+
"dl",
|
|
222
|
+
"dt",
|
|
223
|
+
"em",
|
|
224
|
+
"figcaption",
|
|
225
|
+
"h1",
|
|
226
|
+
"h2",
|
|
227
|
+
"h3",
|
|
228
|
+
"h4",
|
|
229
|
+
"h5",
|
|
230
|
+
"h6",
|
|
231
|
+
"i",
|
|
232
|
+
"ins",
|
|
233
|
+
"kbd",
|
|
234
|
+
"label",
|
|
235
|
+
"legend",
|
|
236
|
+
"li",
|
|
237
|
+
"p",
|
|
238
|
+
"pre",
|
|
239
|
+
"q",
|
|
240
|
+
"rp",
|
|
241
|
+
"rt",
|
|
242
|
+
"s",
|
|
243
|
+
"small",
|
|
244
|
+
"span",
|
|
245
|
+
"strong",
|
|
246
|
+
"sub",
|
|
247
|
+
"summary",
|
|
248
|
+
"sup",
|
|
249
|
+
"time",
|
|
250
|
+
"u"
|
|
251
|
+
];
|
|
252
|
+
|
|
253
|
+
//#endregion
|
|
254
|
+
//#region src/isEqual.ts
|
|
255
|
+
const isArrayEqual = (a, b) => {
|
|
256
|
+
if (a.length !== b.length) return false;
|
|
257
|
+
for (let i = 0; i < a.length; i++) if (!isEqual(a[i], b[i])) return false;
|
|
258
|
+
return true;
|
|
259
|
+
};
|
|
260
|
+
const isObjectEqual = (a, b) => {
|
|
261
|
+
const aKeys = Object.keys(a);
|
|
262
|
+
if (aKeys.length !== Object.keys(b).length) return false;
|
|
263
|
+
for (const key of aKeys) {
|
|
264
|
+
if (!Object.hasOwn(b, key)) return false;
|
|
265
|
+
if (!isEqual(a[key], b[key])) return false;
|
|
266
|
+
}
|
|
267
|
+
return true;
|
|
268
|
+
};
|
|
269
|
+
const isEqual = (a, b) => {
|
|
270
|
+
if (Object.is(a, b)) return true;
|
|
271
|
+
if (typeof a !== typeof b || a == null || b == null || typeof a !== "object") return false;
|
|
272
|
+
if (Array.isArray(a)) return Array.isArray(b) && isArrayEqual(a, b);
|
|
273
|
+
if (Array.isArray(b)) return false;
|
|
274
|
+
return isObjectEqual(a, b);
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
//#endregion
|
|
278
|
+
//#region src/render.tsx
|
|
279
|
+
const render = (content, attachProps) => {
|
|
280
|
+
if (!content) return null;
|
|
281
|
+
const t = typeof content;
|
|
282
|
+
if (t === "string" || t === "number" || t === "boolean" || t === "bigint") return content;
|
|
283
|
+
if (Array.isArray(content)) return content;
|
|
284
|
+
if (typeof content === "function") return h(content, attachProps ?? {});
|
|
285
|
+
if (typeof content === "object" && content !== null) return content;
|
|
286
|
+
return content;
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
//#endregion
|
|
290
|
+
//#region ../../node_modules/.bun/@pyreon+reactivity@0.3.1/node_modules/@pyreon/reactivity/lib/index.js
|
|
291
|
+
let batchDepth = 0;
|
|
292
|
+
let setA = /* @__PURE__ */ new Set();
|
|
293
|
+
let pendingNotifications = setA;
|
|
294
|
+
function isBatching() {
|
|
295
|
+
return batchDepth > 0;
|
|
296
|
+
}
|
|
297
|
+
function enqueuePendingNotification(notify) {
|
|
298
|
+
pendingNotifications.add(notify);
|
|
299
|
+
}
|
|
300
|
+
let activeEffect = null;
|
|
301
|
+
const effectDeps = /* @__PURE__ */ new WeakMap();
|
|
302
|
+
let _depsCollector = null;
|
|
303
|
+
/**
|
|
304
|
+
* Register the active effect as a subscriber of the given reactive source.
|
|
305
|
+
* The subscriber Set is created lazily on the host — sources read only outside
|
|
306
|
+
* effects never allocate a Set.
|
|
307
|
+
*/
|
|
308
|
+
function trackSubscriber(host) {
|
|
309
|
+
if (activeEffect) {
|
|
310
|
+
if (!host._s) host._s = /* @__PURE__ */ new Set();
|
|
311
|
+
const subscribers = host._s;
|
|
312
|
+
subscribers.add(activeEffect);
|
|
313
|
+
if (_depsCollector) _depsCollector.push(subscribers);
|
|
314
|
+
else {
|
|
315
|
+
let deps = effectDeps.get(activeEffect);
|
|
316
|
+
if (!deps) {
|
|
317
|
+
deps = /* @__PURE__ */ new Set();
|
|
318
|
+
effectDeps.set(activeEffect, deps);
|
|
319
|
+
}
|
|
320
|
+
deps.add(subscribers);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
function notifySubscribers(subscribers) {
|
|
325
|
+
if (subscribers.size === 0) return;
|
|
326
|
+
if (subscribers.size === 1) {
|
|
327
|
+
const sub = subscribers.values().next().value;
|
|
328
|
+
if (isBatching()) enqueuePendingNotification(sub);
|
|
329
|
+
else sub();
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
if (isBatching()) for (const sub of subscribers) enqueuePendingNotification(sub);
|
|
333
|
+
else {
|
|
334
|
+
const originalSize = subscribers.size;
|
|
335
|
+
let i = 0;
|
|
336
|
+
for (const sub of subscribers) {
|
|
337
|
+
if (i >= originalSize) break;
|
|
338
|
+
sub();
|
|
339
|
+
i++;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
let _traceListeners = null;
|
|
344
|
+
/** @internal — called from signal.set() when tracing is active */
|
|
345
|
+
function _notifyTraceListeners(sig, prev, next) {
|
|
346
|
+
if (!_traceListeners) return;
|
|
347
|
+
const event = {
|
|
348
|
+
signal: sig,
|
|
349
|
+
name: sig.label,
|
|
350
|
+
prev,
|
|
351
|
+
next,
|
|
352
|
+
stack: (/* @__PURE__ */ new Error()).stack ?? "",
|
|
353
|
+
timestamp: performance.now()
|
|
354
|
+
};
|
|
355
|
+
for (const l of _traceListeners) l(event);
|
|
356
|
+
}
|
|
357
|
+
/** Check if any trace listeners are active (fast path for signal.set) */
|
|
358
|
+
function isTracing() {
|
|
359
|
+
return _traceListeners !== null;
|
|
360
|
+
}
|
|
361
|
+
function _peek() {
|
|
362
|
+
return this._v;
|
|
363
|
+
}
|
|
364
|
+
function _set(newValue) {
|
|
365
|
+
if (Object.is(this._v, newValue)) return;
|
|
366
|
+
const prev = this._v;
|
|
367
|
+
this._v = newValue;
|
|
368
|
+
if (isTracing()) _notifyTraceListeners(this, prev, newValue);
|
|
369
|
+
if (this._d) notifyDirect(this._d);
|
|
370
|
+
if (this._s) notifySubscribers(this._s);
|
|
371
|
+
}
|
|
372
|
+
function _update(fn) {
|
|
373
|
+
_set.call(this, fn(this._v));
|
|
374
|
+
}
|
|
375
|
+
function _subscribe(listener) {
|
|
376
|
+
if (!this._s) this._s = /* @__PURE__ */ new Set();
|
|
377
|
+
this._s.add(listener);
|
|
378
|
+
return () => this._s?.delete(listener);
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Register a direct updater — lighter than subscribe().
|
|
382
|
+
* Uses a flat array instead of Set. Disposal nulls the slot (no Set.delete overhead).
|
|
383
|
+
* Used by compiler-emitted _bindText/_bindDirect for zero-overhead DOM bindings.
|
|
384
|
+
*/
|
|
385
|
+
function _directFn(updater) {
|
|
386
|
+
if (!this._d) this._d = [];
|
|
387
|
+
const arr = this._d;
|
|
388
|
+
const idx = arr.length;
|
|
389
|
+
arr.push(updater);
|
|
390
|
+
return () => {
|
|
391
|
+
arr[idx] = null;
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
/**
|
|
395
|
+
* Notify direct updaters — flat array iteration, batch-aware.
|
|
396
|
+
* Null slots (from disposed updaters) are skipped.
|
|
397
|
+
*/
|
|
398
|
+
function notifyDirect(updaters) {
|
|
399
|
+
if (isBatching()) for (let i = 0; i < updaters.length; i++) {
|
|
400
|
+
const fn = updaters[i];
|
|
401
|
+
if (fn) enqueuePendingNotification(fn);
|
|
402
|
+
}
|
|
403
|
+
else for (let i = 0; i < updaters.length; i++) updaters[i]?.();
|
|
404
|
+
}
|
|
405
|
+
function _debug() {
|
|
406
|
+
return {
|
|
407
|
+
name: this.label,
|
|
408
|
+
value: this._v,
|
|
409
|
+
subscriberCount: this._s?.size ?? 0
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* Create a reactive signal.
|
|
414
|
+
*
|
|
415
|
+
* Only 1 closure is allocated (the read function). State is stored as
|
|
416
|
+
* properties on the function object (_v, _s) and methods (peek, set,
|
|
417
|
+
* update, subscribe) are shared across all signals — not per-signal closures.
|
|
418
|
+
*/
|
|
419
|
+
function signal(initialValue, options) {
|
|
420
|
+
const read = (() => {
|
|
421
|
+
trackSubscriber(read);
|
|
422
|
+
return read._v;
|
|
423
|
+
});
|
|
424
|
+
read._v = initialValue;
|
|
425
|
+
read._s = null;
|
|
426
|
+
read._d = null;
|
|
427
|
+
read.peek = _peek;
|
|
428
|
+
read.set = _set;
|
|
429
|
+
read.update = _update;
|
|
430
|
+
read.subscribe = _subscribe;
|
|
431
|
+
read.direct = _directFn;
|
|
432
|
+
read.debug = _debug;
|
|
433
|
+
read.label = options?.name;
|
|
434
|
+
return read;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
//#endregion
|
|
438
|
+
//#region src/useStableValue.ts
|
|
439
|
+
/**
|
|
440
|
+
* Returns a referentially stable version of `value`. The returned reference
|
|
441
|
+
* only changes when the value is no longer deeply equal to the previous one.
|
|
442
|
+
*
|
|
443
|
+
* Pyreon equivalent of the React useStableValue hook — uses a signal
|
|
444
|
+
* internally to hold the stable reference.
|
|
445
|
+
*/
|
|
446
|
+
const useStableValue = (value) => {
|
|
447
|
+
const ref = signal(value);
|
|
448
|
+
if (!isEqual(ref.peek(), value)) ref.set(value);
|
|
449
|
+
return ref.peek();
|
|
450
|
+
};
|
|
451
|
+
|
|
452
|
+
//#endregion
|
|
453
|
+
//#region src/utils.ts
|
|
454
|
+
const omit = (obj, keys) => {
|
|
455
|
+
if (obj == null) return {};
|
|
456
|
+
if (!keys || keys.length === 0) return { ...obj };
|
|
457
|
+
const result = {};
|
|
458
|
+
const keysSet = new Set(keys);
|
|
459
|
+
for (const key in obj) if (Object.hasOwn(obj, key) && !keysSet.has(key)) result[key] = obj[key];
|
|
460
|
+
return result;
|
|
461
|
+
};
|
|
462
|
+
const pick = (obj, keys) => {
|
|
463
|
+
if (obj == null) return {};
|
|
464
|
+
if (!keys || keys.length === 0) return { ...obj };
|
|
465
|
+
const result = {};
|
|
466
|
+
for (const key of keys) {
|
|
467
|
+
const k = key;
|
|
468
|
+
if (Object.hasOwn(obj, k)) result[k] = obj[k];
|
|
469
|
+
}
|
|
470
|
+
return result;
|
|
471
|
+
};
|
|
472
|
+
const PATH_RE = /[^.[\]]+/g;
|
|
473
|
+
const parsePath = (path) => {
|
|
474
|
+
if (Array.isArray(path)) return path;
|
|
475
|
+
return path.match(PATH_RE) ?? [];
|
|
476
|
+
};
|
|
477
|
+
const isUnsafeKey = (key) => key === "__proto__" || key === "prototype" || key === "constructor";
|
|
478
|
+
const get = (obj, path, defaultValue) => {
|
|
479
|
+
const keys = parsePath(path);
|
|
480
|
+
let result = obj;
|
|
481
|
+
for (const key of keys) {
|
|
482
|
+
if (result == null || isUnsafeKey(key)) return defaultValue;
|
|
483
|
+
result = result[key];
|
|
484
|
+
}
|
|
485
|
+
return result === void 0 ? defaultValue : result;
|
|
486
|
+
};
|
|
487
|
+
const set = (obj, path, value) => {
|
|
488
|
+
const keys = parsePath(path);
|
|
489
|
+
let current = obj;
|
|
490
|
+
for (let i = 0; i < keys.length - 1; i++) {
|
|
491
|
+
const key = keys[i];
|
|
492
|
+
if (isUnsafeKey(key)) return obj;
|
|
493
|
+
const nextKey = keys[i + 1];
|
|
494
|
+
if (isUnsafeKey(nextKey)) return obj;
|
|
495
|
+
if (current[key] == null) current[key] = /^\d+$/.test(nextKey) ? [] : {};
|
|
496
|
+
current = current[key];
|
|
497
|
+
}
|
|
498
|
+
const lastKey = keys[keys.length - 1];
|
|
499
|
+
if (lastKey != null && !isUnsafeKey(lastKey)) current[lastKey] = value;
|
|
500
|
+
return obj;
|
|
501
|
+
};
|
|
502
|
+
const throttle = (fn, wait = 0, options) => {
|
|
503
|
+
const leading = options?.leading !== false;
|
|
504
|
+
const trailing = options?.trailing !== false;
|
|
505
|
+
let lastCallTime;
|
|
506
|
+
let timeoutId;
|
|
507
|
+
let lastArgs;
|
|
508
|
+
const invoke = (args) => {
|
|
509
|
+
lastCallTime = Date.now();
|
|
510
|
+
fn(...args);
|
|
511
|
+
};
|
|
512
|
+
const startTrailingTimer = (args, delay) => {
|
|
513
|
+
lastArgs = args;
|
|
514
|
+
if (timeoutId !== void 0) return;
|
|
515
|
+
timeoutId = setTimeout(() => {
|
|
516
|
+
timeoutId = void 0;
|
|
517
|
+
if (lastArgs) {
|
|
518
|
+
invoke(lastArgs);
|
|
519
|
+
lastArgs = void 0;
|
|
520
|
+
}
|
|
521
|
+
}, delay);
|
|
522
|
+
};
|
|
523
|
+
const throttled = (...args) => {
|
|
524
|
+
const now = Date.now();
|
|
525
|
+
const elapsed = lastCallTime === void 0 ? wait : now - lastCallTime;
|
|
526
|
+
if (elapsed >= wait) if (leading) invoke(args);
|
|
527
|
+
else {
|
|
528
|
+
lastCallTime = now;
|
|
529
|
+
if (trailing) startTrailingTimer(args, wait);
|
|
530
|
+
}
|
|
531
|
+
else if (trailing) startTrailingTimer(args, wait - elapsed);
|
|
532
|
+
};
|
|
533
|
+
throttled.cancel = () => {
|
|
534
|
+
if (timeoutId !== void 0) {
|
|
535
|
+
clearTimeout(timeoutId);
|
|
536
|
+
timeoutId = void 0;
|
|
537
|
+
}
|
|
538
|
+
lastArgs = void 0;
|
|
539
|
+
lastCallTime = void 0;
|
|
540
|
+
};
|
|
541
|
+
return throttled;
|
|
542
|
+
};
|
|
543
|
+
const isPlainObject = (value) => value !== null && typeof value === "object" && !Array.isArray(value) && Object.getPrototypeOf(value) === Object.prototype;
|
|
544
|
+
const merge = (target, ...sources) => {
|
|
545
|
+
for (const source of sources) {
|
|
546
|
+
if (source == null) continue;
|
|
547
|
+
for (const key of Object.keys(source)) {
|
|
548
|
+
if (key === "__proto__" || key === "constructor" || key === "prototype") continue;
|
|
549
|
+
const targetVal = target[key];
|
|
550
|
+
const sourceVal = source[key];
|
|
551
|
+
if (isPlainObject(targetVal) && isPlainObject(sourceVal)) target[key] = merge({ ...targetVal }, sourceVal);
|
|
552
|
+
else target[key] = sourceVal;
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
return target;
|
|
556
|
+
};
|
|
557
|
+
|
|
558
|
+
//#endregion
|
|
559
|
+
export { HTML_TAGS, HTML_TEXT_TAGS, Provider, compose, config, context, get, hoistNonReactStatics, init, isEmpty, isEqual, merge, omit, pick, render, set, throttle, useStableValue };
|
|
560
|
+
//# sourceMappingURL=index.js.map
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pyreon/ui-core",
|
|
3
|
+
"version": "0.0.2",
|
|
4
|
+
"description": "Core utilities, config, and context for Pyreon UI System",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"sideEffects": false,
|
|
8
|
+
"exports": {
|
|
9
|
+
"source": "./src/index.ts",
|
|
10
|
+
"import": "./lib/index.js",
|
|
11
|
+
"types": "./lib/index.d.ts"
|
|
12
|
+
},
|
|
13
|
+
"types": "./lib/index.d.ts",
|
|
14
|
+
"main": "./lib/index.js",
|
|
15
|
+
"files": [
|
|
16
|
+
"lib",
|
|
17
|
+
"!lib/**/*.map",
|
|
18
|
+
"!lib/analysis",
|
|
19
|
+
"README.md",
|
|
20
|
+
"LICENSE"
|
|
21
|
+
],
|
|
22
|
+
"engines": {
|
|
23
|
+
"node": ">= 18"
|
|
24
|
+
},
|
|
25
|
+
"publishConfig": {
|
|
26
|
+
"access": "public"
|
|
27
|
+
},
|
|
28
|
+
"scripts": {
|
|
29
|
+
"build": "bun run vl_rolldown_build",
|
|
30
|
+
"lint": "biome check src/",
|
|
31
|
+
"test": "vitest run",
|
|
32
|
+
"typecheck": "tsc --noEmit"
|
|
33
|
+
},
|
|
34
|
+
"peerDependencies": {
|
|
35
|
+
"@pyreon/core": ">=0.3.0",
|
|
36
|
+
"@pyreon/styler": "^0.0.2"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@vitus-labs/tools-rolldown": "^1.15.0",
|
|
40
|
+
"@vitus-labs/tools-typescript": "^1.15.0"
|
|
41
|
+
}
|
|
42
|
+
}
|