@pyreon/elements 0.15.0 → 0.18.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/lib/index.d.ts +105 -48
- package/lib/index.js +83 -49
- package/package.json +12 -12
- package/src/Element/component.tsx +13 -2
- package/src/List/component.tsx +65 -15
- package/src/Portal/component.tsx +23 -10
- package/src/__tests__/Element.test.ts +157 -0
- package/src/__tests__/Iterator.test.ts +12 -3
- package/src/__tests__/Iterator.types.test.ts +237 -0
- package/src/__tests__/Portal.test.ts +122 -48
- package/src/__tests__/Wrapper-innerhtml.test.tsx +178 -0
- package/src/__tests__/elements.browser.test.tsx +47 -0
- package/src/__tests__/wrapper-block-cascade.test.ts +121 -0
- package/src/helpers/Iterator/component.tsx +55 -4
- package/src/helpers/Iterator/index.ts +17 -1
- package/src/helpers/Iterator/types.ts +97 -38
- package/src/helpers/Wrapper/component.tsx +104 -19
- package/src/helpers/Wrapper/styled.ts +12 -18
- package/src/index.ts +4 -0
- package/src/types.ts +33 -2
package/lib/index.d.ts
CHANGED
|
@@ -182,54 +182,87 @@ type ExtendedProps = {
|
|
|
182
182
|
even: boolean;
|
|
183
183
|
position: number;
|
|
184
184
|
};
|
|
185
|
-
|
|
186
|
-
|
|
185
|
+
/**
|
|
186
|
+
* Iterator over an array of strings/numbers. Each item is wrapped in
|
|
187
|
+
* `{ [valueName]: item }` and that object is what callbacks see + what's
|
|
188
|
+
* spread onto the rendered component.
|
|
189
|
+
*/
|
|
190
|
+
type SimpleProps<T extends SimpleValue> = {
|
|
191
|
+
data: Array<T | MaybeNull>; /** A component to be rendered per item. */
|
|
192
|
+
component: ElementType;
|
|
187
193
|
/**
|
|
188
|
-
*
|
|
189
|
-
|
|
194
|
+
* Key under which each primitive value is exposed to `component` and
|
|
195
|
+
* callbacks. Defaults to `'children'` at runtime — i.e. the value is
|
|
196
|
+
* passed to the component as its children.
|
|
197
|
+
*/
|
|
198
|
+
valueName?: string; /** Optional wrapper around each item. */
|
|
199
|
+
wrapComponent?: ElementType; /** Stable key per item (defaults to index). */
|
|
200
|
+
itemKey?: (item: T, index: number) => SimpleValue; /** Extra props merged onto the rendered component, optionally per-item. */
|
|
201
|
+
itemProps?: TObj | ((item: {
|
|
202
|
+
[k: string]: T;
|
|
203
|
+
}, ext: ExtendedProps) => TObj); /** Extra props merged onto the wrapper, optionally per-item. */
|
|
204
|
+
wrapProps?: TObj | ((item: {
|
|
205
|
+
[k: string]: T;
|
|
206
|
+
}, ext: ExtendedProps) => TObj);
|
|
207
|
+
children?: never;
|
|
208
|
+
};
|
|
209
|
+
/**
|
|
210
|
+
* Iterator over an array of objects. Each item is spread onto the rendered
|
|
211
|
+
* component as props. Per-item `component` overrides also work — when an
|
|
212
|
+
* item carries its own `component` field, the wrapper is bypassed.
|
|
213
|
+
*/
|
|
214
|
+
type ObjectProps<T extends ObjectValue> = {
|
|
215
|
+
data: Array<T | MaybeNull>; /** Default component to be rendered per item (item-level `component` overrides). */
|
|
216
|
+
component: ElementType; /** `valueName` is meaningless when iterating objects — TS forbids it. */
|
|
217
|
+
valueName?: never; /** Optional wrapper around each item. */
|
|
218
|
+
wrapComponent?: ElementType; /** Stable key per item — pick a key from the item, or compute it. */
|
|
219
|
+
itemKey?: keyof T | ((item: T, index: number) => SimpleValue); /** Extra props merged onto the rendered component, optionally per-item. */
|
|
220
|
+
itemProps?: TObj | ((item: T, ext: ExtendedProps) => TObj); /** Extra props merged onto the wrapper, optionally per-item. */
|
|
221
|
+
wrapProps?: TObj | ((item: T, ext: ExtendedProps) => TObj);
|
|
222
|
+
children?: never;
|
|
223
|
+
};
|
|
224
|
+
/**
|
|
225
|
+
* Iterator over `children` — no `data`/`component`. Each child gets
|
|
226
|
+
* positional metadata via `itemProps` and an optional `wrapComponent`.
|
|
227
|
+
*/
|
|
228
|
+
type ChildrenProps = {
|
|
229
|
+
children: VNodeChild;
|
|
230
|
+
data?: never;
|
|
231
|
+
component?: never;
|
|
232
|
+
valueName?: never;
|
|
233
|
+
itemKey?: never;
|
|
234
|
+
wrapComponent?: ElementType;
|
|
235
|
+
itemProps?: TObj | ((_: Record<string, never>, ext: ExtendedProps) => TObj);
|
|
236
|
+
wrapProps?: TObj | ((_: Record<string, never>, ext: ExtendedProps) => TObj);
|
|
237
|
+
};
|
|
238
|
+
type PropsCallback = TObj | ((itemProps: Record<string, never> | Record<string, SimpleValue> | ObjectValue, extendedProps: ExtendedProps) => TObj);
|
|
239
|
+
type LooseProps = Partial<{
|
|
190
240
|
children: VNodeChild;
|
|
191
|
-
/**
|
|
192
|
-
* Array of data passed to `component` prop
|
|
193
|
-
*/
|
|
194
241
|
data: Array<SimpleValue | ObjectValue | MaybeNull>;
|
|
195
|
-
/**
|
|
196
|
-
* A component to be rendered within list
|
|
197
|
-
*/
|
|
198
242
|
component: ElementType;
|
|
199
|
-
/**
|
|
200
|
-
* Defines name of the prop to be passed to the iteration component
|
|
201
|
-
* when **data** prop is type of `string[]`, `number[]` or combination
|
|
202
|
-
* of both. Otherwise ignored.
|
|
203
|
-
*/
|
|
204
243
|
valueName: string;
|
|
205
|
-
/**
|
|
206
|
-
* A component to be rendered within list. `wrapComponent`
|
|
207
|
-
* wraps `component`. Therefore it can be used to enhance the behavior
|
|
208
|
-
* of the list component
|
|
209
|
-
*/
|
|
210
244
|
wrapComponent: ElementType;
|
|
211
|
-
/**
|
|
212
|
-
* Extension of **item** `component` props to be passed
|
|
213
|
-
*/
|
|
214
245
|
itemProps: PropsCallback;
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
*/
|
|
218
|
-
wrapProps?: PropsCallback;
|
|
219
|
-
/**
|
|
220
|
-
* Extension of **item** `wrapComponent` props to be passed
|
|
221
|
-
*/
|
|
222
|
-
itemKey?: keyof ObjectValue | ((item: SimpleValue | Omit<ObjectValue, 'component'>, index: number) => SimpleValue);
|
|
246
|
+
wrapProps: PropsCallback;
|
|
247
|
+
itemKey: keyof ObjectValue | ((item: SimpleValue | Omit<ObjectValue, 'component'>, index: number) => SimpleValue);
|
|
223
248
|
}>;
|
|
249
|
+
type Props$1<T = unknown> = unknown extends T ? LooseProps : T extends SimpleValue ? SimpleProps<T> : T extends ObjectValue ? ObjectProps<T> : ChildrenProps;
|
|
224
250
|
//#endregion
|
|
225
251
|
//#region src/helpers/Iterator/component.d.ts
|
|
226
|
-
declare const
|
|
252
|
+
declare const RESERVED_PROPS: readonly ["children", "component", "wrapComponent", "data", "itemKey", "valueName", "itemProps", "wrapProps"];
|
|
253
|
+
interface IteratorComponent {
|
|
254
|
+
<T extends SimpleValue>(props: SimpleProps<T>): VNodeChild;
|
|
255
|
+
<T extends ObjectValue>(props: ObjectProps<T>): VNodeChild;
|
|
256
|
+
(props: ChildrenProps): VNodeChild;
|
|
257
|
+
(props: LooseProps): VNodeChild;
|
|
227
258
|
isIterator: true;
|
|
228
|
-
RESERVED_PROPS:
|
|
229
|
-
|
|
259
|
+
RESERVED_PROPS: typeof RESERVED_PROPS;
|
|
260
|
+
displayName?: string;
|
|
261
|
+
}
|
|
262
|
+
declare const Iterator: IteratorComponent;
|
|
230
263
|
//#endregion
|
|
231
264
|
//#region src/List/component.d.ts
|
|
232
|
-
type
|
|
265
|
+
type ListOnly = {
|
|
233
266
|
/**
|
|
234
267
|
* A boolean value. When set to `false`, component returns fragment.
|
|
235
268
|
* When set to `true`, component returns as the **root** element `Element`
|
|
@@ -239,14 +272,38 @@ type ListProps = {
|
|
|
239
272
|
/**
|
|
240
273
|
* Label prop from `Element` component is being ignored.
|
|
241
274
|
*/
|
|
242
|
-
label
|
|
275
|
+
label?: never;
|
|
243
276
|
/**
|
|
244
277
|
* Content prop from `Element` component is being ignored.
|
|
245
278
|
*/
|
|
246
|
-
content
|
|
279
|
+
content?: never;
|
|
247
280
|
};
|
|
248
|
-
|
|
249
|
-
|
|
281
|
+
/**
|
|
282
|
+
* Props that List accepts on top of the Iterator branch — the Element prop
|
|
283
|
+
* surface (so `tag`, `direction`, `alignX`, etc. forward when
|
|
284
|
+
* `rootElement` is true) plus the List-only toggle.
|
|
285
|
+
*/
|
|
286
|
+
type ListExtras = Partial<Omit<Props, 'children' | 'content' | 'label'>> & ListOnly;
|
|
287
|
+
/**
|
|
288
|
+
* Public Props — generic over the data element type so callers get the same
|
|
289
|
+
* inference Iterator does, plus the List-specific `rootElement` toggle and
|
|
290
|
+
* Element prop forwarding.
|
|
291
|
+
*
|
|
292
|
+
* Props<string> → SimpleProps & ListExtras (valueName REQUIRED)
|
|
293
|
+
* Props<{ id; name }> → ObjectProps & ListExtras (valueName FORBIDDEN)
|
|
294
|
+
* Props<unknown> / Props → LooseProps & ListExtras (today's behavior)
|
|
295
|
+
*/
|
|
296
|
+
type Props$2<T = unknown> = MergeTypes<[Props$1<T>, ListExtras]>;
|
|
297
|
+
interface ListComponent {
|
|
298
|
+
<T extends SimpleValue>(props: SimpleProps<T> & ListExtras): VNodeChild;
|
|
299
|
+
<T extends ObjectValue>(props: ObjectProps<T> & ListExtras): VNodeChild;
|
|
300
|
+
(props: ChildrenProps & ListExtras): VNodeChild;
|
|
301
|
+
(props: LooseProps & ListExtras): VNodeChild;
|
|
302
|
+
displayName?: string;
|
|
303
|
+
pkgName?: string;
|
|
304
|
+
PYREON__COMPONENT?: string;
|
|
305
|
+
}
|
|
306
|
+
declare const _default: ListComponent;
|
|
250
307
|
//#endregion
|
|
251
308
|
//#region src/Overlay/context.d.ts
|
|
252
309
|
interface OverlayContext {
|
|
@@ -254,7 +311,7 @@ interface OverlayContext {
|
|
|
254
311
|
setBlocked: () => void;
|
|
255
312
|
setUnblocked: () => void;
|
|
256
313
|
}
|
|
257
|
-
declare const Component$
|
|
314
|
+
declare const Component$2: (props: OverlayContext & {
|
|
258
315
|
children: VNodeChild;
|
|
259
316
|
}) => _$_pyreon_core0.VNode;
|
|
260
317
|
//#endregion
|
|
@@ -343,24 +400,24 @@ type Props$3 = {
|
|
|
343
400
|
triggerRefName?: string;
|
|
344
401
|
contentRefName?: string;
|
|
345
402
|
} & UseOverlayProps;
|
|
346
|
-
declare const Component$
|
|
403
|
+
declare const Component$1: PyreonComponent<Props$3>;
|
|
347
404
|
//#endregion
|
|
348
405
|
//#region src/Portal/component.d.ts
|
|
349
406
|
interface Props$4 {
|
|
350
407
|
/**
|
|
351
|
-
*
|
|
408
|
+
* DOM element to mount the wrapper into. Defaults to `document.body`.
|
|
352
409
|
*/
|
|
353
410
|
DOMLocation?: HTMLElement;
|
|
354
411
|
/**
|
|
355
|
-
* Children
|
|
412
|
+
* Children rendered inside the wrapper.
|
|
356
413
|
*/
|
|
357
414
|
children: VNodeChild;
|
|
358
415
|
/**
|
|
359
|
-
*
|
|
416
|
+
* HTML tag for the per-instance wrapper element. Defaults to `'div'`.
|
|
360
417
|
*/
|
|
361
418
|
tag?: string;
|
|
362
419
|
}
|
|
363
|
-
declare const Component$
|
|
420
|
+
declare const Component$3: PyreonComponent<Props$4>;
|
|
364
421
|
//#endregion
|
|
365
422
|
//#region src/Text/component.d.ts
|
|
366
423
|
type Props$5 = Partial<{
|
|
@@ -385,7 +442,7 @@ type Props$5 = Partial<{
|
|
|
385
442
|
*/
|
|
386
443
|
css: ExtendCss;
|
|
387
444
|
}> & PyreonHTMLAttributes;
|
|
388
|
-
declare const Component$
|
|
445
|
+
declare const Component$4: PyreonComponent<Props$5> & {
|
|
389
446
|
isText?: true;
|
|
390
447
|
};
|
|
391
448
|
//#endregion
|
|
@@ -404,7 +461,7 @@ interface Props$6 {
|
|
|
404
461
|
*/
|
|
405
462
|
style?: Record<string, unknown> | undefined;
|
|
406
463
|
}
|
|
407
|
-
declare const Component$
|
|
464
|
+
declare const Component$5: PyreonComponent<Props$6>;
|
|
408
465
|
//#endregion
|
|
409
|
-
export { type AlignX, type AlignY, type Content, type ContentBoolean, type Direction, Component as Element, type Props as ElementProps, type ElementType, type ExtendCss, type ExtendedProps, type InnerRef,
|
|
466
|
+
export { type AlignX, type AlignY, type Content, type ContentBoolean, type Direction, Component as Element, type Props as ElementProps, type ElementType, type ExtendCss, type ExtendedProps, type InnerRef, Iterator, type ChildrenProps as IteratorChildrenProps, type LooseProps as IteratorLooseProps, type ObjectProps as IteratorObjectProps, type Props$1 as IteratorProps, type SimpleProps as IteratorSimpleProps, _default as List, type Props$2 as ListProps, type MaybeNull, type ObjectValue, Component$1 as Overlay, type Props$3 as OverlayProps, Component$2 as OverlayProvider, Component$3 as Portal, type Props$4 as PortalProps, type PropsCallback, Provider, type PyreonElement, type PyreonStatic, type Responsive, type ResponsiveBoolType, type SimpleValue, Component$4 as Text, type Props$5 as TextProps, type UseOverlayProps, Component$5 as Util, type Props$6 as UtilProps, useOverlay };
|
|
410
467
|
//# sourceMappingURL=index2.d.ts.map
|
package/lib/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Provider, alignContent, extendCss, makeItResponsive, value } from "@pyreon/unistyle";
|
|
2
|
-
import { Fragment, Portal, createContext, nativeCompat, onMount, provide, splitProps, useContext } from "@pyreon/core";
|
|
2
|
+
import { Fragment, Portal, createContext, h, nativeCompat, onMount, onUnmount, provide, splitProps, useContext } from "@pyreon/core";
|
|
3
3
|
import { config, isEmpty, omit, pick, render, throttle } from "@pyreon/ui-core";
|
|
4
4
|
import { Fragment as Fragment$1, jsx, jsxs } from "@pyreon/core/jsx-runtime";
|
|
5
5
|
import { signal } from "@pyreon/reactivity";
|
|
@@ -264,28 +264,25 @@ const childFixCSS = `
|
|
|
264
264
|
const parentFixCSS = `
|
|
265
265
|
flex-direction: column;
|
|
266
266
|
`;
|
|
267
|
-
const fullHeightCSS = `
|
|
268
|
-
height: 100%;
|
|
269
|
-
`;
|
|
270
|
-
const blockCSS = `
|
|
271
|
-
align-self: stretch;
|
|
272
|
-
flex: 1;
|
|
273
|
-
min-width: 0;
|
|
274
|
-
`;
|
|
275
|
-
const childFixPosition = (isBlock) => `display: ${isBlock ? "flex" : "inline-flex"};`;
|
|
276
267
|
const styles$1 = ({ theme: t, css: cssFn }) => cssFn`
|
|
277
|
-
${t.alignY === "block" && fullHeightCSS};
|
|
278
|
-
|
|
279
268
|
${alignContent({
|
|
280
269
|
direction: t.direction,
|
|
281
270
|
alignX: t.alignX,
|
|
282
271
|
alignY: t.alignY
|
|
283
272
|
})};
|
|
284
273
|
|
|
285
|
-
|
|
286
|
-
|
|
274
|
+
/*
|
|
275
|
+
* Always emit a value for the block-related properties so a responsive
|
|
276
|
+
* theme that flips from \`block: true\` at one breakpoint to \`block: false\`
|
|
277
|
+
* at another resets cleanly. Previously \`align-self\` / \`width\` / \`height\`
|
|
278
|
+
* were only set when the truthy branch matched, which left the prior
|
|
279
|
+
* breakpoint's values cascading through.
|
|
280
|
+
*/
|
|
281
|
+
${`align-self: ${t.block ? "stretch" : "auto"};
|
|
282
|
+
width: ${t.block ? "100%" : "auto"};
|
|
283
|
+
height: ${t.alignY === "block" ? "100%" : "auto"};`};
|
|
287
284
|
|
|
288
|
-
${!t.childFix &&
|
|
285
|
+
${!t.childFix && `display: ${t.block ? "flex" : "inline-flex"};`};
|
|
289
286
|
${t.parentFix && parentFixCSS};
|
|
290
287
|
|
|
291
288
|
${t.extraStyles && extendCss(t.extraStyles)};
|
|
@@ -334,6 +331,34 @@ const isWebFixNeeded = (tag) => {
|
|
|
334
331
|
* support `display: flex` consistently across browsers.
|
|
335
332
|
*/
|
|
336
333
|
const DEV_PROPS = IS_DEVELOPMENT ? { "data-pyr-element": "Element" } : {};
|
|
334
|
+
/**
|
|
335
|
+
* Build a props object for `h(Styled, ...)` by copying own property
|
|
336
|
+
* DESCRIPTORS from `rest`, then layering the additional fields. Compiler-
|
|
337
|
+
* emitted reactive props (`_rp(() => signal())` converted to getters by
|
|
338
|
+
* `makeReactiveProps`) survive end-to-end with their getter intact.
|
|
339
|
+
*
|
|
340
|
+
* Why we bypass JSX spread here: the standard JSX automatic-runtime
|
|
341
|
+
* compilation lowers `<Styled {...rest} foo={x}>` to roughly
|
|
342
|
+
* `jsx(Styled, { ...rest, foo: x })`. That `{...rest, foo: x}` object
|
|
343
|
+
* literal is evaluated at JS level — it fires every getter on `rest` and
|
|
344
|
+
* stores the resolved value before `jsx()` ever sees the object. No
|
|
345
|
+
* amount of in-runtime descriptor preservation can recover the getters
|
|
346
|
+
* once they've been collapsed by the surface-level spread. The fix is
|
|
347
|
+
* structural: don't use JSX spread for reactive-prop forwarding. Build
|
|
348
|
+
* the props object with descriptor preservation and pass it to `h()`
|
|
349
|
+
* directly — `h()` stores props as-is on the vnode, no copy, getters
|
|
350
|
+
* survive into mount.
|
|
351
|
+
*/
|
|
352
|
+
const buildStyledProps = (rest, refValue, asTag, extras) => {
|
|
353
|
+
const result = {};
|
|
354
|
+
const descriptors = Object.getOwnPropertyDescriptors(rest);
|
|
355
|
+
for (const key in descriptors) Object.defineProperty(result, key, descriptors[key]);
|
|
356
|
+
for (const key in DEV_PROPS) result[key] = DEV_PROPS[key];
|
|
357
|
+
result.ref = refValue;
|
|
358
|
+
result.as = asTag;
|
|
359
|
+
for (const key in extras) result[key] = extras[key];
|
|
360
|
+
return result;
|
|
361
|
+
};
|
|
337
362
|
const OWN_KEYS = [
|
|
338
363
|
"children",
|
|
339
364
|
"tag",
|
|
@@ -349,14 +374,9 @@ const OWN_KEYS = [
|
|
|
349
374
|
];
|
|
350
375
|
const Component$8 = (props) => {
|
|
351
376
|
const [own, rest] = splitProps(props, OWN_KEYS);
|
|
352
|
-
const commonProps = {
|
|
353
|
-
...rest,
|
|
354
|
-
...DEV_PROPS,
|
|
355
|
-
ref: own.ref,
|
|
356
|
-
as: own.tag
|
|
357
|
-
};
|
|
358
377
|
const needsFix = !own.dangerouslySetInnerHTML && isWebFixNeeded(own.tag);
|
|
359
378
|
const isVoidTag = !own.dangerouslySetInnerHTML && getShouldBeEmpty(own.tag);
|
|
379
|
+
const innerHTML = own.dangerouslySetInnerHTML;
|
|
360
380
|
if (!needsFix) {
|
|
361
381
|
const bundle = internElementBundle({
|
|
362
382
|
block: own.block,
|
|
@@ -366,15 +386,15 @@ const Component$8 = (props) => {
|
|
|
366
386
|
equalCols: own.equalCols,
|
|
367
387
|
extraStyles: own.extendCss
|
|
368
388
|
});
|
|
369
|
-
if (isVoidTag) return
|
|
370
|
-
|
|
371
|
-
$element: bundle
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
389
|
+
if (isVoidTag) return h(styled_default$1, buildStyledProps(rest, own.ref, own.tag, { $element: bundle }));
|
|
390
|
+
if (innerHTML) return h(styled_default$1, buildStyledProps(rest, own.ref, own.tag, {
|
|
391
|
+
$element: bundle,
|
|
392
|
+
dangerouslySetInnerHTML: innerHTML
|
|
393
|
+
}));
|
|
394
|
+
return h(styled_default$1, buildStyledProps(rest, own.ref, own.tag, {
|
|
375
395
|
$element: bundle,
|
|
376
396
|
children: own.children
|
|
377
|
-
});
|
|
397
|
+
}));
|
|
378
398
|
}
|
|
379
399
|
const asTag = own.isInline ? "span" : "div";
|
|
380
400
|
const parentBundle = internElementBundle({
|
|
@@ -389,16 +409,24 @@ const Component$8 = (props) => {
|
|
|
389
409
|
alignY: own.alignY,
|
|
390
410
|
equalCols: own.equalCols
|
|
391
411
|
});
|
|
392
|
-
|
|
393
|
-
|
|
412
|
+
if (innerHTML) return h(styled_default$1, buildStyledProps(rest, own.ref, own.tag, {
|
|
413
|
+
$element: parentBundle,
|
|
414
|
+
children: h(styled_default$1, {
|
|
415
|
+
as: asTag,
|
|
416
|
+
$childFix: true,
|
|
417
|
+
$element: childBundle,
|
|
418
|
+
dangerouslySetInnerHTML: innerHTML
|
|
419
|
+
})
|
|
420
|
+
}));
|
|
421
|
+
return h(styled_default$1, buildStyledProps(rest, own.ref, own.tag, {
|
|
394
422
|
$element: parentBundle,
|
|
395
|
-
children:
|
|
423
|
+
children: h(styled_default$1, {
|
|
396
424
|
as: asTag,
|
|
397
425
|
$childFix: true,
|
|
398
426
|
$element: childBundle,
|
|
399
427
|
children: own.children
|
|
400
428
|
})
|
|
401
|
-
});
|
|
429
|
+
}));
|
|
402
430
|
};
|
|
403
431
|
|
|
404
432
|
//#endregion
|
|
@@ -499,7 +527,13 @@ const Component = (props) => {
|
|
|
499
527
|
else if (externalRef != null) externalRef.current = node;
|
|
500
528
|
};
|
|
501
529
|
if (own.equalBeforeAfter && own.beforeContent && own.afterContent) onMount(() => {
|
|
502
|
-
|
|
530
|
+
const node = equalizeRef;
|
|
531
|
+
if (!node) return void 0;
|
|
532
|
+
equalize(node, own.direction);
|
|
533
|
+
if (typeof ResizeObserver === "undefined") return void 0;
|
|
534
|
+
const observer = new ResizeObserver(() => equalize(node, own.direction));
|
|
535
|
+
observer.observe(node);
|
|
536
|
+
return () => observer.disconnect();
|
|
503
537
|
});
|
|
504
538
|
const WRAPPER_PROPS = {
|
|
505
539
|
ref: mergedRef,
|
|
@@ -730,34 +764,28 @@ const Component$7 = (props) => {
|
|
|
730
764
|
};
|
|
731
765
|
return renderItems();
|
|
732
766
|
};
|
|
733
|
-
|
|
767
|
+
const Iterator = Object.assign(Component$7, {
|
|
734
768
|
isIterator: true,
|
|
735
769
|
RESERVED_PROPS
|
|
736
770
|
});
|
|
737
771
|
|
|
738
772
|
//#endregion
|
|
739
773
|
//#region src/helpers/Iterator/index.ts
|
|
740
|
-
var Iterator_default =
|
|
774
|
+
var Iterator_default = Iterator;
|
|
741
775
|
|
|
742
776
|
//#endregion
|
|
743
777
|
//#region src/List/component.tsx
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
* optional Element root wrapper. When `rootElement` is false (default),
|
|
747
|
-
* it renders a bare Iterator as a fragment. When true, the Iterator output
|
|
748
|
-
* is wrapped in an Element that receives all non-iterator props (e.g.,
|
|
749
|
-
* layout, alignment, css), allowing the list to be styled as a single block.
|
|
750
|
-
*/
|
|
751
|
-
const Component$1 = ((allProps) => {
|
|
778
|
+
const LooseIterator = Iterator_default;
|
|
779
|
+
const Component$1 = (allProps) => {
|
|
752
780
|
const [own, props] = splitProps(allProps, ["rootElement", "ref"]);
|
|
753
|
-
const renderedList = /* @__PURE__ */ jsx(
|
|
781
|
+
const renderedList = /* @__PURE__ */ jsx(LooseIterator, { ...pick(props, Iterator_default.RESERVED_PROPS) });
|
|
754
782
|
if (!own.rootElement) return renderedList;
|
|
755
783
|
return /* @__PURE__ */ jsx(Component, {
|
|
756
|
-
|
|
784
|
+
ref: own.ref,
|
|
757
785
|
...omit(props, Iterator_default.RESERVED_PROPS),
|
|
758
786
|
children: renderedList
|
|
759
787
|
});
|
|
760
|
-
}
|
|
788
|
+
};
|
|
761
789
|
const name$4 = `${PKG_NAME}/List`;
|
|
762
790
|
Component$1.displayName = name$4;
|
|
763
791
|
Component$1.pkgName = PKG_NAME;
|
|
@@ -1237,10 +1265,16 @@ nativeCompat(Component$2);
|
|
|
1237
1265
|
//#endregion
|
|
1238
1266
|
//#region src/Portal/component.tsx
|
|
1239
1267
|
const Component$4 = (props) => {
|
|
1240
|
-
|
|
1241
|
-
|
|
1268
|
+
if (typeof document === "undefined") return null;
|
|
1269
|
+
const tag = props.tag ?? "div";
|
|
1270
|
+
const target = props.DOMLocation ?? document.body;
|
|
1271
|
+
const wrapper = document.createElement(tag);
|
|
1272
|
+
target.appendChild(wrapper);
|
|
1273
|
+
onUnmount(() => {
|
|
1274
|
+
wrapper.remove();
|
|
1275
|
+
});
|
|
1242
1276
|
return /* @__PURE__ */ jsx(Portal, {
|
|
1243
|
-
target,
|
|
1277
|
+
target: wrapper,
|
|
1244
1278
|
children: props.children
|
|
1245
1279
|
});
|
|
1246
1280
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pyreon/elements",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"description": "Foundational UI components for Pyreon",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -42,21 +42,21 @@
|
|
|
42
42
|
"typecheck": "tsc --noEmit"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
|
-
"@pyreon/core": "^0.
|
|
46
|
-
"@pyreon/reactivity": "^0.
|
|
47
|
-
"@pyreon/runtime-dom": "^0.
|
|
48
|
-
"@pyreon/test-utils": "^0.13.
|
|
49
|
-
"@pyreon/typescript": "^0.
|
|
45
|
+
"@pyreon/core": "^0.18.0",
|
|
46
|
+
"@pyreon/reactivity": "^0.18.0",
|
|
47
|
+
"@pyreon/runtime-dom": "^0.18.0",
|
|
48
|
+
"@pyreon/test-utils": "^0.13.5",
|
|
49
|
+
"@pyreon/typescript": "^0.18.0",
|
|
50
50
|
"@vitest/browser-playwright": "^4.1.4",
|
|
51
51
|
"@vitus-labs/tools-rolldown": "^2.3.0"
|
|
52
52
|
},
|
|
53
|
-
"peerDependencies": {
|
|
54
|
-
"@pyreon/core": "^0.15.0",
|
|
55
|
-
"@pyreon/reactivity": "^0.15.0",
|
|
56
|
-
"@pyreon/ui-core": "^0.15.0",
|
|
57
|
-
"@pyreon/unistyle": "^0.15.0"
|
|
58
|
-
},
|
|
59
53
|
"engines": {
|
|
60
54
|
"node": ">= 22"
|
|
55
|
+
},
|
|
56
|
+
"dependencies": {
|
|
57
|
+
"@pyreon/core": "^0.18.0",
|
|
58
|
+
"@pyreon/reactivity": "^0.18.0",
|
|
59
|
+
"@pyreon/ui-core": "^0.18.0",
|
|
60
|
+
"@pyreon/unistyle": "^0.18.0"
|
|
61
61
|
}
|
|
62
62
|
}
|
|
@@ -137,9 +137,20 @@ const Component: PyreonElement = (props) => {
|
|
|
137
137
|
}
|
|
138
138
|
|
|
139
139
|
if (own.equalBeforeAfter && own.beforeContent && own.afterContent) {
|
|
140
|
+
// Run once on mount AND continue equalizing as the element resizes —
|
|
141
|
+
// catches async slot content (font swaps, lazy text, viewport resize)
|
|
142
|
+
// that a one-shot measurement would miss. Mirrors vitus-labs's Element
|
|
143
|
+
// useLayoutEffect + ResizeObserver pattern.
|
|
140
144
|
onMount(() => {
|
|
141
|
-
|
|
142
|
-
return undefined
|
|
145
|
+
const node = equalizeRef
|
|
146
|
+
if (!node) return undefined
|
|
147
|
+
|
|
148
|
+
equalize(node, own.direction)
|
|
149
|
+
|
|
150
|
+
if (typeof ResizeObserver === 'undefined') return undefined
|
|
151
|
+
const observer = new ResizeObserver(() => equalize(node, own.direction))
|
|
152
|
+
observer.observe(node)
|
|
153
|
+
return () => observer.disconnect()
|
|
143
154
|
})
|
|
144
155
|
}
|
|
145
156
|
|
package/src/List/component.tsx
CHANGED
|
@@ -5,16 +5,25 @@
|
|
|
5
5
|
* is wrapped in an Element that receives all non-iterator props (e.g.,
|
|
6
6
|
* layout, alignment, css), allowing the list to be styled as a single block.
|
|
7
7
|
*/
|
|
8
|
+
import type { VNodeChild } from '@pyreon/core'
|
|
8
9
|
import { splitProps } from '@pyreon/core'
|
|
9
10
|
import { omit, pick } from '@pyreon/ui-core'
|
|
10
11
|
import { PKG_NAME } from '../constants'
|
|
11
|
-
import type { ElementProps
|
|
12
|
+
import type { ElementProps } from '../Element'
|
|
12
13
|
import { Element } from '../Element'
|
|
13
|
-
import type {
|
|
14
|
+
import type {
|
|
15
|
+
ChildrenProps as IteratorChildrenProps,
|
|
16
|
+
LooseProps as IteratorLooseProps,
|
|
17
|
+
ObjectProps as IteratorObjectProps,
|
|
18
|
+
Props as IteratorProps,
|
|
19
|
+
SimpleProps as IteratorSimpleProps,
|
|
20
|
+
ObjectValue,
|
|
21
|
+
SimpleValue,
|
|
22
|
+
} from '../helpers/Iterator'
|
|
14
23
|
import Iterator from '../helpers/Iterator'
|
|
15
24
|
import type { MergeTypes } from '../types'
|
|
16
25
|
|
|
17
|
-
type
|
|
26
|
+
type ListOnly = {
|
|
18
27
|
/**
|
|
19
28
|
* A boolean value. When set to `false`, component returns fragment.
|
|
20
29
|
* When set to `true`, component returns as the **root** element `Element`
|
|
@@ -24,32 +33,73 @@ type ListProps = {
|
|
|
24
33
|
/**
|
|
25
34
|
* Label prop from `Element` component is being ignored.
|
|
26
35
|
*/
|
|
27
|
-
label
|
|
36
|
+
label?: never
|
|
28
37
|
/**
|
|
29
38
|
* Content prop from `Element` component is being ignored.
|
|
30
39
|
*/
|
|
31
|
-
content
|
|
40
|
+
content?: never
|
|
32
41
|
}
|
|
33
42
|
|
|
34
|
-
|
|
43
|
+
/**
|
|
44
|
+
* Props that List accepts on top of the Iterator branch — the Element prop
|
|
45
|
+
* surface (so `tag`, `direction`, `alignX`, etc. forward when
|
|
46
|
+
* `rootElement` is true) plus the List-only toggle.
|
|
47
|
+
*/
|
|
48
|
+
type ListExtras = Partial<Omit<ElementProps, 'children' | 'content' | 'label'>> & ListOnly
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Public Props — generic over the data element type so callers get the same
|
|
52
|
+
* inference Iterator does, plus the List-specific `rootElement` toggle and
|
|
53
|
+
* Element prop forwarding.
|
|
54
|
+
*
|
|
55
|
+
* Props<string> → SimpleProps & ListExtras (valueName REQUIRED)
|
|
56
|
+
* Props<{ id; name }> → ObjectProps & ListExtras (valueName FORBIDDEN)
|
|
57
|
+
* Props<unknown> / Props → LooseProps & ListExtras (today's behavior)
|
|
58
|
+
*/
|
|
59
|
+
export type Props<T = unknown> = MergeTypes<[IteratorProps<T>, ListExtras]>
|
|
35
60
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
61
|
+
// Internal spread — runtime is correct, but the picked subset can't satisfy
|
|
62
|
+
// any specific Iterator overload statically (which is good — the public
|
|
63
|
+
// overloads enforce constraints). Cast Iterator to a loose callable for
|
|
64
|
+
// the internal forwarding only; public consumers still see the strict
|
|
65
|
+
// overloaded interface.
|
|
66
|
+
const LooseIterator = Iterator as unknown as (props: IteratorLooseProps) => VNodeChild
|
|
67
|
+
|
|
68
|
+
const Component = (allProps: IteratorLooseProps & ListExtras) => {
|
|
69
|
+
const [own, props] = splitProps(allProps as Record<string, unknown>, ['rootElement', 'ref'])
|
|
70
|
+
const renderedList = <LooseIterator {...pick(props, Iterator.RESERVED_PROPS)} />
|
|
39
71
|
|
|
40
72
|
if (!own.rootElement) return renderedList
|
|
41
73
|
|
|
42
74
|
return (
|
|
43
|
-
<Element {
|
|
75
|
+
<Element ref={own.ref as ElementProps['ref']} {...omit(props, Iterator.RESERVED_PROPS)}>
|
|
44
76
|
{renderedList}
|
|
45
77
|
</Element>
|
|
46
78
|
)
|
|
47
|
-
}
|
|
79
|
+
}
|
|
48
80
|
|
|
49
81
|
const name = `${PKG_NAME}/List` as const
|
|
50
82
|
|
|
51
|
-
Component.displayName = name
|
|
52
|
-
Component.pkgName = PKG_NAME
|
|
53
|
-
Component.PYREON__COMPONENT = name
|
|
83
|
+
;(Component as { displayName?: string }).displayName = name
|
|
84
|
+
;(Component as { pkgName?: string }).pkgName = PKG_NAME
|
|
85
|
+
;(Component as { PYREON__COMPONENT?: string }).PYREON__COMPONENT = name
|
|
86
|
+
|
|
87
|
+
// ---------------------------------------------------------------------------
|
|
88
|
+
// Public callable type — same overload pattern as Iterator so JSX-site
|
|
89
|
+
// inference flows through without callers having to spell out `<T>`.
|
|
90
|
+
// ---------------------------------------------------------------------------
|
|
91
|
+
export interface ListComponent {
|
|
92
|
+
// T inferred from `data`. Order: SimpleProps, ObjectProps, ChildrenProps,
|
|
93
|
+
// then a LooseProps fallback for forwarding patterns where derived
|
|
94
|
+
// `$$types['data']` is a wide union that doesn't bind to any narrow
|
|
95
|
+
// overload. See Iterator's IteratorComponent for the full rationale.
|
|
96
|
+
<T extends SimpleValue>(props: IteratorSimpleProps<T> & ListExtras): VNodeChild
|
|
97
|
+
<T extends ObjectValue>(props: IteratorObjectProps<T> & ListExtras): VNodeChild
|
|
98
|
+
(props: IteratorChildrenProps & ListExtras): VNodeChild
|
|
99
|
+
(props: IteratorLooseProps & ListExtras): VNodeChild
|
|
100
|
+
displayName?: string
|
|
101
|
+
pkgName?: string
|
|
102
|
+
PYREON__COMPONENT?: string
|
|
103
|
+
}
|
|
54
104
|
|
|
55
|
-
export default Component
|
|
105
|
+
export default Component as unknown as ListComponent
|