@pyreon/elements 0.24.4 → 0.24.6
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/package.json +10 -12
- package/src/Element/component.tsx +0 -315
- package/src/Element/constants.ts +0 -96
- package/src/Element/index.ts +0 -6
- package/src/Element/types.ts +0 -168
- package/src/Element/utils.ts +0 -15
- package/src/List/component.tsx +0 -105
- package/src/List/index.ts +0 -5
- package/src/Overlay/component.tsx +0 -140
- package/src/Overlay/context.tsx +0 -36
- package/src/Overlay/index.ts +0 -7
- package/src/Overlay/positioning.ts +0 -191
- package/src/Overlay/useOverlay.tsx +0 -461
- package/src/Portal/component.tsx +0 -54
- package/src/Portal/index.ts +0 -5
- package/src/Text/component.tsx +0 -67
- package/src/Text/index.ts +0 -5
- package/src/Text/styled.ts +0 -30
- package/src/Util/component.tsx +0 -43
- package/src/Util/index.ts +0 -5
- package/src/__tests__/Content.test.tsx +0 -123
- package/src/__tests__/Element-slot-reactivity.browser.test.tsx +0 -152
- package/src/__tests__/Element.test.ts +0 -819
- package/src/__tests__/Iterator.test.ts +0 -492
- package/src/__tests__/Iterator.types.test.ts +0 -237
- package/src/__tests__/List.test.ts +0 -199
- package/src/__tests__/Overlay.test.ts +0 -492
- package/src/__tests__/Portal.test.ts +0 -156
- package/src/__tests__/Text.test.ts +0 -274
- package/src/__tests__/Util.test.ts +0 -63
- package/src/__tests__/Wrapper-innerhtml.test.tsx +0 -178
- package/src/__tests__/Wrapper.test.tsx +0 -196
- package/src/__tests__/elements.browser.test.tsx +0 -132
- package/src/__tests__/equalBeforeAfter.test.ts +0 -122
- package/src/__tests__/helpers.test.ts +0 -65
- package/src/__tests__/integration.test.tsx +0 -118
- package/src/__tests__/internElementBundle.test.ts +0 -102
- package/src/__tests__/iterator-function-children.test.tsx +0 -120
- package/src/__tests__/native-markers.test.ts +0 -13
- package/src/__tests__/overlayContext.test.tsx +0 -78
- package/src/__tests__/perf-stress.browser.test.tsx +0 -119
- package/src/__tests__/positioning.test.ts +0 -90
- package/src/__tests__/responsiveProps.test.ts +0 -328
- package/src/__tests__/slot-component-reference.test.tsx +0 -157
- package/src/__tests__/useOverlay.test.ts +0 -1336
- package/src/__tests__/utils.test.ts +0 -69
- package/src/__tests__/wrapper-block-cascade.test.ts +0 -121
- package/src/constants.ts +0 -1
- package/src/env.d.ts +0 -6
- package/src/helpers/Content/component.tsx +0 -75
- package/src/helpers/Content/index.ts +0 -3
- package/src/helpers/Content/styled.ts +0 -105
- package/src/helpers/Content/types.ts +0 -49
- package/src/helpers/Iterator/component.tsx +0 -316
- package/src/helpers/Iterator/index.ts +0 -30
- package/src/helpers/Iterator/types.ts +0 -138
- package/src/helpers/Wrapper/component.tsx +0 -180
- package/src/helpers/Wrapper/constants.ts +0 -10
- package/src/helpers/Wrapper/index.ts +0 -3
- package/src/helpers/Wrapper/styled.ts +0 -64
- package/src/helpers/Wrapper/types.ts +0 -56
- package/src/helpers/Wrapper/utils.ts +0 -7
- package/src/helpers/index.ts +0 -4
- package/src/helpers/internElementBundle.ts +0 -37
- package/src/helpers/isPyreonComponent.ts +0 -46
- package/src/index.ts +0 -42
- package/src/manifest.ts +0 -190
- package/src/tests/manifest-snapshot.test.ts +0 -45
- package/src/types.ts +0 -112
- package/src/utils.ts +0 -5
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Styled component for the Element wrapper layer. Handles responsive
|
|
3
|
-
* block/inline-flex display, direction, alignment, and custom CSS injection.
|
|
4
|
-
* Includes special handling for the `parentFix` / `childFix` flags that
|
|
5
|
-
* split flex behavior across two DOM nodes for button/fieldset/legend
|
|
6
|
-
* elements where a single flex container is insufficient.
|
|
7
|
-
*/
|
|
8
|
-
import { config } from '@pyreon/ui-core'
|
|
9
|
-
import { alignContent, extendCss, makeItResponsive } from '@pyreon/unistyle'
|
|
10
|
-
import type { ResponsiveStylesCallback } from '../../types'
|
|
11
|
-
import type { StyledProps } from './types'
|
|
12
|
-
|
|
13
|
-
const { styled, css, component } = config
|
|
14
|
-
|
|
15
|
-
const childFixCSS = `
|
|
16
|
-
display: flex;
|
|
17
|
-
flex: 1;
|
|
18
|
-
width: 100%;
|
|
19
|
-
height: 100%;
|
|
20
|
-
`
|
|
21
|
-
|
|
22
|
-
const parentFixCSS = `
|
|
23
|
-
flex-direction: column;
|
|
24
|
-
`
|
|
25
|
-
|
|
26
|
-
export const styles: ResponsiveStylesCallback = ({ theme: t, css: cssFn }) => cssFn`
|
|
27
|
-
${alignContent({
|
|
28
|
-
direction: t.direction,
|
|
29
|
-
alignX: t.alignX,
|
|
30
|
-
alignY: t.alignY,
|
|
31
|
-
})};
|
|
32
|
-
|
|
33
|
-
/*
|
|
34
|
-
* Always emit a value for the block-related properties so a responsive
|
|
35
|
-
* theme that flips from \`block: true\` at one breakpoint to \`block: false\`
|
|
36
|
-
* at another resets cleanly. Previously \`align-self\` / \`width\` / \`height\`
|
|
37
|
-
* were only set when the truthy branch matched, which left the prior
|
|
38
|
-
* breakpoint's values cascading through.
|
|
39
|
-
*/
|
|
40
|
-
${`align-self: ${t.block ? 'stretch' : 'auto'};
|
|
41
|
-
width: ${t.block ? '100%' : 'auto'};
|
|
42
|
-
height: ${t.alignY === 'block' ? '100%' : 'auto'};`};
|
|
43
|
-
|
|
44
|
-
${!t.childFix && `display: ${t.block ? 'flex' : 'inline-flex'};`};
|
|
45
|
-
${t.parentFix && parentFixCSS};
|
|
46
|
-
|
|
47
|
-
${t.extraStyles && extendCss(t.extraStyles as Parameters<typeof extendCss>[0])};
|
|
48
|
-
`
|
|
49
|
-
|
|
50
|
-
const platformCSS = `box-sizing: border-box;`
|
|
51
|
-
|
|
52
|
-
export default styled(component, { layer: 'elements' })`
|
|
53
|
-
position: relative;
|
|
54
|
-
${platformCSS};
|
|
55
|
-
|
|
56
|
-
${(({ $childFix }: StyledProps) => $childFix && childFixCSS) as any};
|
|
57
|
-
|
|
58
|
-
${makeItResponsive({
|
|
59
|
-
key: '$element',
|
|
60
|
-
styles,
|
|
61
|
-
css,
|
|
62
|
-
normalize: true,
|
|
63
|
-
})};
|
|
64
|
-
`
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import type { VNodeChild } from '@pyreon/core'
|
|
2
|
-
import type { HTMLTags } from '@pyreon/ui-core'
|
|
3
|
-
import type {
|
|
4
|
-
AlignX,
|
|
5
|
-
AlignY,
|
|
6
|
-
ContentAlignX,
|
|
7
|
-
ContentAlignY,
|
|
8
|
-
ContentBoolean,
|
|
9
|
-
ContentDirection,
|
|
10
|
-
Css,
|
|
11
|
-
Direction,
|
|
12
|
-
ExtendCss,
|
|
13
|
-
ResponsiveBoolType,
|
|
14
|
-
} from '../../types'
|
|
15
|
-
|
|
16
|
-
export type Reference = unknown
|
|
17
|
-
|
|
18
|
-
export interface Props {
|
|
19
|
-
children: VNodeChild | VNodeChild[]
|
|
20
|
-
tag: HTMLTags | undefined
|
|
21
|
-
block: ResponsiveBoolType | undefined
|
|
22
|
-
isInline: boolean | undefined
|
|
23
|
-
direction: Direction | undefined
|
|
24
|
-
alignX: AlignX | undefined
|
|
25
|
-
alignY: AlignY | undefined
|
|
26
|
-
equalCols: ResponsiveBoolType | undefined
|
|
27
|
-
extendCss: ExtendCss | undefined
|
|
28
|
-
dangerouslySetInnerHTML: { __html: string } | undefined
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export interface StyledProps {
|
|
32
|
-
$element: {
|
|
33
|
-
direction: Direction
|
|
34
|
-
alignX: AlignX
|
|
35
|
-
alignY: AlignY
|
|
36
|
-
equalCols: ResponsiveBoolType
|
|
37
|
-
} & Partial<{
|
|
38
|
-
block: ResponsiveBoolType
|
|
39
|
-
extraStyles: ExtendCss
|
|
40
|
-
childFix: true
|
|
41
|
-
parentFix: true
|
|
42
|
-
}>
|
|
43
|
-
$childFix?: true
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export type ThemeProps = {
|
|
47
|
-
direction: ContentDirection
|
|
48
|
-
alignX: ContentAlignX
|
|
49
|
-
alignY: ContentAlignY
|
|
50
|
-
equalCols: ContentBoolean
|
|
51
|
-
} & Partial<{
|
|
52
|
-
block: ContentBoolean
|
|
53
|
-
extraStyles: Css
|
|
54
|
-
childFix: true
|
|
55
|
-
parentFix: true
|
|
56
|
-
}>
|
package/src/helpers/index.ts
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Module-scope intern cache for `$element` bundles passed to Wrapper's styled
|
|
3
|
-
* component. Same primitive prop tuple → same object identity, so the styler's
|
|
4
|
-
* `elClassCache` (added 2026-Q2 alongside this) hits and skips the resolve
|
|
5
|
-
* pipeline. Analogous to `@pyreon/rocketstyle`'s dimension-prop memo (PR #344)
|
|
6
|
-
* but at the layer below — covers non-rocketstyle Element / Wrapper / Text usage
|
|
7
|
-
* AND the residual styled wrappers under any rocketstyle component.
|
|
8
|
-
*
|
|
9
|
-
* Cache key is a JSON-stringified shallow snapshot of the bundle. LRU-bound at
|
|
10
|
-
* 256 entries; oldest-first eviction. Bail (return the input as-is, no cache)
|
|
11
|
-
* when any value is a function (signal accessor) or a non-string object (CSS
|
|
12
|
-
* callback / CSSResult / nested object) — those cannot be safely round-tripped
|
|
13
|
-
* through JSON without losing identity guarantees.
|
|
14
|
-
*/
|
|
15
|
-
const _bundleCache = new Map<string, Record<string, unknown>>()
|
|
16
|
-
const BUNDLE_CAP = 256
|
|
17
|
-
|
|
18
|
-
export const internElementBundle = <T extends Record<string, unknown>>(bundle: T): T => {
|
|
19
|
-
for (const k in bundle) {
|
|
20
|
-
const v = bundle[k]
|
|
21
|
-
if (typeof v === 'function') return bundle
|
|
22
|
-
if (v != null && typeof v === 'object') return bundle
|
|
23
|
-
}
|
|
24
|
-
const key = JSON.stringify(bundle)
|
|
25
|
-
const existing = _bundleCache.get(key)
|
|
26
|
-
if (existing) {
|
|
27
|
-
_bundleCache.delete(key)
|
|
28
|
-
_bundleCache.set(key, existing)
|
|
29
|
-
return existing as T
|
|
30
|
-
}
|
|
31
|
-
if (_bundleCache.size >= BUNDLE_CAP) {
|
|
32
|
-
const oldest = _bundleCache.keys().next().value
|
|
33
|
-
if (oldest !== undefined) _bundleCache.delete(oldest)
|
|
34
|
-
}
|
|
35
|
-
_bundleCache.set(key, bundle)
|
|
36
|
-
return bundle
|
|
37
|
-
}
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Detect whether a function value is a framework component (created via
|
|
3
|
-
* `rocketstyle()` or one of `@pyreon/elements`' component factories), as
|
|
4
|
-
* opposed to a plain reactive-accessor function.
|
|
5
|
-
*
|
|
6
|
-
* Used by `Element` / `Content` `resolveSlot` to discriminate between
|
|
7
|
-
* `beforeContent={Header}` — component-reference shorthand, MUST mount
|
|
8
|
-
* as `h(Component, null)` so the framework HOC's
|
|
9
|
-
* `removeUndefinedProps(props)` / `splitProps(props)` get the
|
|
10
|
-
* default-filled props object, not bare `undefined`.
|
|
11
|
-
* `beforeContent={() => <Header />}` — reactive accessor, MUST be called
|
|
12
|
-
* so its body's signal reads land inside the enclosing
|
|
13
|
-
* `mountReactive` effect.
|
|
14
|
-
*
|
|
15
|
-
* Both are `typeof === 'function'`. The discriminator is the marker
|
|
16
|
-
* attached by each factory:
|
|
17
|
-
* - `IS_ROCKETSTYLE` — set by `@pyreon/rocketstyle` (`rocketstyle.ts:527`,
|
|
18
|
-
* `542`) on every `rocketstyle(...).config(...)` chain end-point.
|
|
19
|
-
* - `PYREON__COMPONENT` — set by every `@pyreon/elements` component
|
|
20
|
-
* factory (Element, Text, List, Portal, Overlay, Util, Content,
|
|
21
|
-
* Wrapper, …).
|
|
22
|
-
* - `pkgName` — same components also carry this; checked as a fallback
|
|
23
|
-
* in case a third-party package mirrors the elements convention.
|
|
24
|
-
*
|
|
25
|
-
* Plain bare-function components without any marker (e.g.
|
|
26
|
-
* `const MyComp = () => <div />`) intentionally take the accessor path
|
|
27
|
-
* — they don't access props, so calling them with no args is safe AND
|
|
28
|
-
* returns the VNode the renderer expects. The marker check ONLY rescues
|
|
29
|
-
* components whose HOC pipelines REQUIRE props to be defined.
|
|
30
|
-
*
|
|
31
|
-
* Reference: regression report on 0.24.3 / PR #839 — `resolveSlot` called
|
|
32
|
-
* any function-valued slot bare, crashing real consumers (bokisch.com
|
|
33
|
-
* SSG build: `Prerendered 0 page(s) + 404.html`) that used the
|
|
34
|
-
* `beforeContent={Component}` shorthand documented since the original
|
|
35
|
-
* Element API.
|
|
36
|
-
*/
|
|
37
|
-
export function isPyreonComponent(value: unknown): boolean {
|
|
38
|
-
if (typeof value !== 'function') return false
|
|
39
|
-
// `Object.hasOwn` (not `in`) so a marker on a parent prototype doesn't
|
|
40
|
-
// count — the marker is always an own-property in the factories.
|
|
41
|
-
return (
|
|
42
|
-
Object.hasOwn(value, 'IS_ROCKETSTYLE') ||
|
|
43
|
-
Object.hasOwn(value, 'PYREON__COMPONENT') ||
|
|
44
|
-
Object.hasOwn(value, 'pkgName')
|
|
45
|
-
)
|
|
46
|
-
}
|
package/src/index.ts
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { Provider } from '@pyreon/unistyle'
|
|
2
|
-
|
|
3
|
-
export type { ElementProps, PyreonElement } from './Element'
|
|
4
|
-
export { Element } from './Element'
|
|
5
|
-
export type {
|
|
6
|
-
ChildrenProps as IteratorChildrenProps,
|
|
7
|
-
ElementType,
|
|
8
|
-
ExtendedProps,
|
|
9
|
-
LooseProps as IteratorLooseProps,
|
|
10
|
-
MaybeNull,
|
|
11
|
-
ObjectProps as IteratorObjectProps,
|
|
12
|
-
ObjectValue,
|
|
13
|
-
Props as IteratorProps,
|
|
14
|
-
PropsCallback,
|
|
15
|
-
SimpleProps as IteratorSimpleProps,
|
|
16
|
-
SimpleValue,
|
|
17
|
-
} from './helpers/Iterator'
|
|
18
|
-
export { default as Iterator } from './helpers/Iterator'
|
|
19
|
-
export type { ListProps } from './List'
|
|
20
|
-
export { List } from './List'
|
|
21
|
-
export type { OverlayProps, UseOverlayProps } from './Overlay'
|
|
22
|
-
export { Overlay, OverlayProvider, useOverlay } from './Overlay'
|
|
23
|
-
export type { PortalProps } from './Portal'
|
|
24
|
-
export { Portal } from './Portal'
|
|
25
|
-
export type { TextProps } from './Text'
|
|
26
|
-
export { Text } from './Text'
|
|
27
|
-
export type {
|
|
28
|
-
AlignX,
|
|
29
|
-
AlignY,
|
|
30
|
-
Content,
|
|
31
|
-
ContentBoolean,
|
|
32
|
-
Direction,
|
|
33
|
-
ExtendCss,
|
|
34
|
-
InnerRef,
|
|
35
|
-
PyreonStatic,
|
|
36
|
-
Responsive,
|
|
37
|
-
ResponsiveBoolType,
|
|
38
|
-
} from './types'
|
|
39
|
-
export type { UtilProps } from './Util'
|
|
40
|
-
export { Util } from './Util'
|
|
41
|
-
|
|
42
|
-
export { Provider }
|
package/src/manifest.ts
DELETED
|
@@ -1,190 +0,0 @@
|
|
|
1
|
-
import { defineManifest } from '@pyreon/manifest'
|
|
2
|
-
|
|
3
|
-
export default defineManifest({
|
|
4
|
-
name: '@pyreon/elements',
|
|
5
|
-
title: 'Base Primitives',
|
|
6
|
-
tagline:
|
|
7
|
-
'Five foundational layout primitives — Element, Text, List, Overlay, Portal — plus the useOverlay positioning hook and the Iterator data helper',
|
|
8
|
-
description:
|
|
9
|
-
"The structural layer every styled / rocketstyle component renders through. `Element` is the responsive flexbox block (direction / alignX / alignY / gap / block + before/after content slots); `Text` is inline typography; `List` is a flowing-children container with the four-overload Iterator data API; `Overlay` is a positioned layer with backdrop driven by `useOverlay` (viewport flip, ESC, click-outside, scroll tracking, hover delay — never reimplement this); `Portal` renders children outside the DOM hierarchy into a per-instance wrapper. Element has a 2026-Q2 simple-path fast path that inlines the Wrapper helper for non-compound, non-needsFix tags (31-45% faster mount) — its rendered VNode then exposes the HTML tag as `props.as` and layout under `props.$element.{...}` instead of flat props.",
|
|
10
|
-
category: 'browser',
|
|
11
|
-
features: [
|
|
12
|
-
'Element — responsive flexbox block: direction / alignX / alignY / gap / block, beforeContent / afterContent slots, equalBeforeAfter (ResizeObserver-tracked)',
|
|
13
|
-
'Text — inline typography primitive',
|
|
14
|
-
'List — flowing-children container; data via the four-overload Iterator API',
|
|
15
|
-
'Overlay + useOverlay — positioned layer (dropdown/modal/tooltip) with viewport flip, ESC, click-outside, scroll tracking, hover delay',
|
|
16
|
-
'Portal — renders children into a per-instance wrapper inside a DOMLocation (default document.body)',
|
|
17
|
-
'Iterator — Simple / Object / Children / Loose overloads keep primitive-vs-object iteration modes type-safe',
|
|
18
|
-
'Util — bare utility primitive; Provider re-exported from @pyreon/unistyle',
|
|
19
|
-
'Simple-path fast path: non-compound Elements skip a component invocation + splitProps + mountChild',
|
|
20
|
-
],
|
|
21
|
-
api: [
|
|
22
|
-
{
|
|
23
|
-
name: 'Element',
|
|
24
|
-
kind: 'component',
|
|
25
|
-
signature: 'Element(props: ElementProps): VNodeChild',
|
|
26
|
-
summary:
|
|
27
|
-
"The responsive flexbox block primitive every layout-bearing component renders through. Layout props live here (NOT in a styler `.theme()`): `direction` (`inline` | `rows` | `reverseInline` | `reverseRows` — note `row` is INVALID), `alignX`, `alignY`, `gap`, `block`, plus `beforeContent` / `afterContent` slot wrappers and `equalBeforeAfter` (equalizes the slot widths on mount AND keeps them equal via ResizeObserver). The 2026-Q2 simple-path fast path inlines the Wrapper for non-compound, non-needsFix tags: the rendered VNode then exposes the HTML tag as `props.as` and layout under `props.$element.{direction,alignX,alignY,block,equalCols,extraStyles}` rather than flat props (styled-components consumers see no change since `as` is the canonical tag selector).",
|
|
28
|
-
example: `import { Element } from "@pyreon/elements"
|
|
29
|
-
|
|
30
|
-
<Element tag="section" direction="rows" gap="md" alignX="center">
|
|
31
|
-
<Header />
|
|
32
|
-
<Body />
|
|
33
|
-
</Element>`,
|
|
34
|
-
mistakes: [
|
|
35
|
-
'Using `direction="row"` — invalid; the values are `inline` / `rows` / `reverseInline` / `reverseRows`',
|
|
36
|
-
'Putting layout props in a styler `.theme()` callback — `direction` / `alignX` / `alignY` / `gap` / `block` are Element ATTRS, not CSS; theme is for colors / spacing / borders',
|
|
37
|
-
'Reading flat `props.direction` on a simple-path Element in a test or styled consumer — the fast path moves layout to `props.$element.*` and the tag to `props.as`; read both shapes via a helper',
|
|
38
|
-
'Passing children to a void `tag` (`hr` / `img` / `br` / `input`) — Element correctly drops them; do not rely on a children slot for void tags',
|
|
39
|
-
'Relying on `equalBeforeAfter` measuring async slot content where `ResizeObserver` is undefined (older runtimes / SSR) — it falls back to the one-shot mount measurement there',
|
|
40
|
-
],
|
|
41
|
-
seeAlso: ['Text', 'List', 'Portal'],
|
|
42
|
-
},
|
|
43
|
-
{
|
|
44
|
-
name: 'Text',
|
|
45
|
-
kind: 'component',
|
|
46
|
-
signature: 'Text(props: TextProps): VNodeChild',
|
|
47
|
-
summary:
|
|
48
|
-
'Inline typography primitive — the text counterpart to `Element`. Carries typography props and renders an inline element; use it for runs of text that need the design-system typography contract rather than a raw `<span>`. Like `Element`, visual styling belongs in the styler/rocketstyle layer; `Text` owns the inline-flow structure.',
|
|
49
|
-
example: `import { Text } from "@pyreon/elements"
|
|
50
|
-
|
|
51
|
-
<Text tag="span">Inline label</Text>`,
|
|
52
|
-
seeAlso: ['Element'],
|
|
53
|
-
},
|
|
54
|
-
{
|
|
55
|
-
name: 'List',
|
|
56
|
-
kind: 'component',
|
|
57
|
-
signature: 'List(props: ListProps): VNodeChild',
|
|
58
|
-
summary:
|
|
59
|
-
'A flowing-children container (`ul` / `ol` / `dl` / custom) built on the Iterator data API. Render children directly OR drive it with `data` + a `component` renderer. Inherits Iterator’s four typed overloads (Simple / Object / Children / Loose) and additionally blocks Element-only `label` / `content` props at the type level.',
|
|
60
|
-
example: `import { List } from "@pyreon/elements"
|
|
61
|
-
|
|
62
|
-
<List tag="ul" data={items()} component={(item) => <li>{item.name}</li>} />`,
|
|
63
|
-
mistakes: [
|
|
64
|
-
'Mixing primitive and object entries in `data` (`[1, {id:1}, null]`) — primitive arrays and object arrays are mutually exclusive iteration modes; the typed overloads reject the mix for direct callers',
|
|
65
|
-
'Passing `valueName` with an object-array `data` — `valueName` is a Simple-mode (primitive) prop only',
|
|
66
|
-
'Passing `children` AND `data`/`component` — Children mode and Object mode are distinct overloads; pick one',
|
|
67
|
-
],
|
|
68
|
-
seeAlso: ['Iterator', 'Element'],
|
|
69
|
-
},
|
|
70
|
-
{
|
|
71
|
-
name: 'Overlay',
|
|
72
|
-
kind: 'component',
|
|
73
|
-
signature: 'Overlay(props: OverlayProps): VNodeChild',
|
|
74
|
-
summary:
|
|
75
|
-
'A positioned layer (dropdown / modal / tooltip / popover) with an optional backdrop, driven internally by `useOverlay`. It handles viewport flipping, ESC-to-close, click-outside, scroll tracking, and hover delay — do NOT reimplement any of that in a primitive; compose `Overlay` (or `useOverlay`) instead. Renders through `Portal` so the layer escapes overflow/stacking contexts.',
|
|
76
|
-
example: `import { Overlay } from "@pyreon/elements"
|
|
77
|
-
|
|
78
|
-
<Overlay isOpen={open()} type="dropdown" align="bottom" onClose={() => open.set(false)}>
|
|
79
|
-
<Menu />
|
|
80
|
-
</Overlay>`,
|
|
81
|
-
mistakes: [
|
|
82
|
-
'Hand-rolling positioning / flip / click-outside / ESC logic in a tooltip or dropdown primitive — `useOverlay` already owns all of it; reimplementing drifts from the shared behavior',
|
|
83
|
-
'Reading the rendered overlay as `document.body.firstChild` — it renders through `Portal` into a per-instance wrapper; traverse the wrapper, not body’s direct child',
|
|
84
|
-
],
|
|
85
|
-
seeAlso: ['useOverlay', 'OverlayProvider', 'Portal'],
|
|
86
|
-
},
|
|
87
|
-
{
|
|
88
|
-
name: 'useOverlay',
|
|
89
|
-
kind: 'hook',
|
|
90
|
-
signature:
|
|
91
|
-
'useOverlay(props?: Partial<UseOverlayProps>): { isOpen, open, close, toggle, triggerProps, overlayProps, /* … */ }',
|
|
92
|
-
summary:
|
|
93
|
-
'The positioning + interaction engine `Overlay` is built on, exposed for headless consumers. Options: `openOn` / `closeOn` (`click` | `hover` | …), `type` (`dropdown` | `modal` | …), `position` (`fixed` | …), `align` + `alignX` / `alignY` + `offsetX` / `offsetY`, `closeOnEsc`, `hoverDelay`, `throttleDelay`, `parentContainer`, `disabled`, `onOpen` / `onClose`. SSR-safe: the internal positioning helpers early-return under no-`window` so the contract is documented at the call site rather than crashing on the server.',
|
|
94
|
-
example: `import { useOverlay } from "@pyreon/elements"
|
|
95
|
-
|
|
96
|
-
const o = useOverlay({ openOn: "hover", type: "tooltip", hoverDelay: 150 })
|
|
97
|
-
// spread o.triggerProps on the anchor, o.overlayProps on the floating layer`,
|
|
98
|
-
mistakes: [
|
|
99
|
-
'Passing `align` as a function accessor — it is a value option, not a signal accessor; let the compiler wrap reactive values',
|
|
100
|
-
'Expecting positioning to run during SSR — the helpers are guarded and no-op without `window`; positioning happens post-mount on the client',
|
|
101
|
-
'Reaching for `addEventListener` for outside-click / scroll instead of letting `useOverlay` own the listener lifecycle — it self-cleans on unmount',
|
|
102
|
-
],
|
|
103
|
-
seeAlso: ['Overlay', 'OverlayProvider'],
|
|
104
|
-
},
|
|
105
|
-
{
|
|
106
|
-
name: 'OverlayProvider',
|
|
107
|
-
kind: 'component',
|
|
108
|
-
signature: 'OverlayProvider(props: { children?: VNodeChild }): VNodeChild',
|
|
109
|
-
summary:
|
|
110
|
-
'Context provider that lets nested overlays coordinate (shared root, stacking, outside-click scoping). `useOverlay` reads it via `useOverlayContext`. Marked `nativeCompat` so it works correctly inside `@pyreon/{react,preact,vue,solid}-compat` apps (its `provide()` runs in Pyreon’s setup frame, not the compat wrapper accessor).',
|
|
111
|
-
example: `import { OverlayProvider } from "@pyreon/elements"
|
|
112
|
-
|
|
113
|
-
<OverlayProvider>
|
|
114
|
-
<App />
|
|
115
|
-
</OverlayProvider>`,
|
|
116
|
-
seeAlso: ['useOverlay', 'Overlay'],
|
|
117
|
-
},
|
|
118
|
-
{
|
|
119
|
-
name: 'Portal',
|
|
120
|
-
kind: 'component',
|
|
121
|
-
signature: 'Portal(props: PortalProps): VNodeChild',
|
|
122
|
-
summary:
|
|
123
|
-
'Renders children OUTSIDE the parent DOM hierarchy — into a PER-INSTANCE wrapper element (default `<div>`, configurable via `tag`) created inside a `DOMLocation` (default `document.body`). Multiple Portals sharing a location each get their OWN wrapper so children never intermingle, which gives cleanup isolation when several modals / tooltips share a portal root.',
|
|
124
|
-
example: `import { Portal } from "@pyreon/elements"
|
|
125
|
-
|
|
126
|
-
<Portal>
|
|
127
|
-
<Modal />
|
|
128
|
-
</Portal>`,
|
|
129
|
-
mistakes: [
|
|
130
|
-
'Asserting `document.body.firstChild === modalRoot` in a test — the Portal nests one level deeper; query the per-instance wrapper (`document.body.querySelector("[data-…]").parentElement`) instead',
|
|
131
|
-
'Assuming all Portals share one container — each instance gets its own wrapper inside the DOMLocation; they do not merge',
|
|
132
|
-
],
|
|
133
|
-
seeAlso: ['Overlay', 'Element'],
|
|
134
|
-
},
|
|
135
|
-
{
|
|
136
|
-
name: 'Iterator',
|
|
137
|
-
kind: 'component',
|
|
138
|
-
signature: 'Iterator<T>(props: IteratorProps<T>): VNodeChild',
|
|
139
|
-
summary:
|
|
140
|
-
'The data-iteration helper backing `List` (default export of `helpers/Iterator`). FOUR typed overloads keep iteration modes honest: `SimpleProps<T>` (primitive arrays — `valueName` allowed), `ObjectProps<T>` (object arrays — `valueName` and `children` FORBIDDEN), `ChildrenProps` (no data/component, only children), and a `LooseProps` fallback that exists so rocketstyle/attrs forwarding patterns (`<Iterator {...wrapperProps} />`) bind without a per-call-site overload error. The discriminator picks the overload via `unknown extends T ? Loose : T extends SimpleValue ? Simple : T extends ObjectValue ? Object : Children`.',
|
|
141
|
-
example: `import Iterator from "@pyreon/elements/helpers/Iterator"
|
|
142
|
-
|
|
143
|
-
<Iterator data={users()} component={(u) => <Row user={u} />} />`,
|
|
144
|
-
mistakes: [
|
|
145
|
-
'Mixed-shape `data` (`[1, {id:1}, null]`) — primitive and object iteration are mutually exclusive; the narrow overloads reject it (the Loose fallback only catches forwarding-pattern shapes)',
|
|
146
|
-
'`valueName` with object-array `data` — Simple-mode only; ObjectProps forbids it',
|
|
147
|
-
'`children` together with `data`/`component` — Children and Object are distinct overloads; the runtime picks the mode by which props are populated, but the types steer you to one',
|
|
148
|
-
],
|
|
149
|
-
seeAlso: ['List'],
|
|
150
|
-
},
|
|
151
|
-
{
|
|
152
|
-
name: 'Util',
|
|
153
|
-
kind: 'component',
|
|
154
|
-
signature: 'Util(props: UtilProps): VNodeChild',
|
|
155
|
-
summary:
|
|
156
|
-
'A bare utility primitive — the minimal structural wrapper when you need an Element-family node without layout semantics (no flex direction / align). Use it for thin passthrough containers where `Element` would impose unwanted flex defaults.',
|
|
157
|
-
example: `import { Util } from "@pyreon/elements"
|
|
158
|
-
|
|
159
|
-
<Util>{children}</Util>`,
|
|
160
|
-
seeAlso: ['Element'],
|
|
161
|
-
},
|
|
162
|
-
{
|
|
163
|
-
name: 'Provider',
|
|
164
|
-
kind: 'component',
|
|
165
|
-
signature: 'Provider(props: { children?: VNodeChild }): VNodeChild',
|
|
166
|
-
summary:
|
|
167
|
-
'Re-exported from `@pyreon/unistyle` for convenience (responsive/breakpoint context). Most apps mount the unified `<PyreonUI>` from `@pyreon/ui-core` instead, which wires this internally — reach for the bare `Provider` only outside the `ui-core` provider tree.',
|
|
168
|
-
example: `import { Provider } from "@pyreon/elements"`,
|
|
169
|
-
seeAlso: ['Element'],
|
|
170
|
-
},
|
|
171
|
-
],
|
|
172
|
-
gotchas: [
|
|
173
|
-
{
|
|
174
|
-
label: 'Layout in attrs, not theme',
|
|
175
|
-
note: 'Element/Text/List layout props (`direction`, `alignX`, `alignY`, `gap`, `block`, `tag`) are primitive ATTRS — they target the inner flex wrapper. Colors / spacing / borders / shadows belong in the styler or rocketstyle `.theme()` layer. `direction` accepts `inline` / `rows` / `reverseInline` / `reverseRows` — `row` is invalid.',
|
|
176
|
-
},
|
|
177
|
-
{
|
|
178
|
-
label: 'Simple-path fast path changed the VNode shape',
|
|
179
|
-
note: 'A non-compound, non-needsFix Element renders a VNode exposing the HTML tag as `props.as` and layout under `props.$element.{direction,alignX,alignY,block,equalCols,extraStyles}` — NOT flat `props.{tag,direction,…}`. Production styled-components consumers are unaffected (`as` is canonical); test/introspection code must read both shapes.',
|
|
180
|
-
},
|
|
181
|
-
{
|
|
182
|
-
label: 'Overlay positioning is centralised',
|
|
183
|
-
note: 'Viewport flip, ESC, click-outside, scroll tracking, hover delay all live in `useOverlay`. Never reimplement them in a tooltip / dropdown / popover primitive — compose `Overlay` or `useOverlay`. The positioning helpers are SSR-guarded (no-op without `window`).',
|
|
184
|
-
},
|
|
185
|
-
{
|
|
186
|
-
label: 'Portal nests a per-instance wrapper',
|
|
187
|
-
note: '`Portal` renders into its OWN wrapper element inside the DOMLocation (default `document.body`), not directly as a body child. DOM assertions must traverse one extra level; multiple Portals do not share a container.',
|
|
188
|
-
},
|
|
189
|
-
],
|
|
190
|
-
})
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
renderApiReferenceEntries,
|
|
3
|
-
renderLlmsFullSection,
|
|
4
|
-
renderLlmsTxtLine,
|
|
5
|
-
} from '@pyreon/manifest'
|
|
6
|
-
import manifest from '../manifest'
|
|
7
|
-
|
|
8
|
-
describe('gen-docs — elements snapshot', () => {
|
|
9
|
-
it('renders a llms.txt bullet starting with the package prefix', () => {
|
|
10
|
-
const line = renderLlmsTxtLine(manifest)
|
|
11
|
-
expect(line.startsWith('- @pyreon/elements —')).toBe(true)
|
|
12
|
-
})
|
|
13
|
-
|
|
14
|
-
it('renders a llms-full.txt section with the right header', () => {
|
|
15
|
-
const section = renderLlmsFullSection(manifest)
|
|
16
|
-
expect(section.startsWith('## @pyreon/elements —')).toBe(true)
|
|
17
|
-
expect(section).toContain('```typescript')
|
|
18
|
-
})
|
|
19
|
-
|
|
20
|
-
it('renders MCP api-reference entries for every api[] item', () => {
|
|
21
|
-
const record = renderApiReferenceEntries(manifest)
|
|
22
|
-
expect(Object.keys(record).sort()).toEqual([
|
|
23
|
-
'elements/Element',
|
|
24
|
-
'elements/Iterator',
|
|
25
|
-
'elements/List',
|
|
26
|
-
'elements/Overlay',
|
|
27
|
-
'elements/OverlayProvider',
|
|
28
|
-
'elements/Portal',
|
|
29
|
-
'elements/Provider',
|
|
30
|
-
'elements/Text',
|
|
31
|
-
'elements/Util',
|
|
32
|
-
'elements/useOverlay',
|
|
33
|
-
])
|
|
34
|
-
})
|
|
35
|
-
|
|
36
|
-
it('carries the layout-in-attrs foot-gun on the Element entry', () => {
|
|
37
|
-
const record = renderApiReferenceEntries(manifest)
|
|
38
|
-
expect(record['elements/Element']?.mistakes).toContain('Using `direction="row"` — invalid')
|
|
39
|
-
})
|
|
40
|
-
|
|
41
|
-
it('flags the Portal per-instance-wrapper assertion trap', () => {
|
|
42
|
-
const record = renderApiReferenceEntries(manifest)
|
|
43
|
-
expect(record['elements/Portal']?.mistakes).toContain('document.body.firstChild')
|
|
44
|
-
})
|
|
45
|
-
})
|
package/src/types.ts
DELETED
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Shared type definitions for the elements package. Provides responsive
|
|
3
|
-
* value types (single | array | breakpoint-map) for layout props like
|
|
4
|
-
* alignment and direction, plus utility types for merging prop objects.
|
|
5
|
-
*/
|
|
6
|
-
import type { ComponentFn } from '@pyreon/core'
|
|
7
|
-
import type { BreakpointKeys, config, render } from '@pyreon/ui-core'
|
|
8
|
-
import type { MakeItResponsiveStyles } from '@pyreon/unistyle'
|
|
9
|
-
|
|
10
|
-
export type ResponsiveStylesCallback = MakeItResponsiveStyles
|
|
11
|
-
|
|
12
|
-
type ExtractNullableKeys<T> = {
|
|
13
|
-
[P in keyof T as T[P] extends null | undefined ? never : P]: T[P]
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
type Id<T> = T extends infer U ? { [K in keyof U]: U[K] } : never
|
|
17
|
-
|
|
18
|
-
type SpreadTwo<L, R> = Id<Pick<L, Exclude<keyof L, keyof R>> & R>
|
|
19
|
-
|
|
20
|
-
type Spread<A extends readonly [...any]> = A extends [infer L, ...infer R]
|
|
21
|
-
? SpreadTwo<L, Spread<R>>
|
|
22
|
-
: unknown
|
|
23
|
-
|
|
24
|
-
export type MergeTypes<A extends readonly [...any]> = ExtractNullableKeys<Spread<A>>
|
|
25
|
-
|
|
26
|
-
export type InnerRef = HTMLElement | ((el: HTMLElement | null) => void) | null
|
|
27
|
-
|
|
28
|
-
export type CssCallback = (css: typeof config.css) => ReturnType<typeof css>
|
|
29
|
-
|
|
30
|
-
export type Css = CssCallback | ReturnType<typeof config.css> | string
|
|
31
|
-
|
|
32
|
-
export type Content = Parameters<typeof render>['0']
|
|
33
|
-
|
|
34
|
-
export type ContentAlignX = 'left' | 'center' | 'right' | 'spaceBetween' | 'spaceAround' | 'block'
|
|
35
|
-
|
|
36
|
-
export type ContentAlignY = 'top' | 'center' | 'bottom' | 'spaceBetween' | 'spaceAround' | 'block'
|
|
37
|
-
|
|
38
|
-
export type ContentDirection = 'inline' | 'rows' | 'reverseInline' | 'reverseRows'
|
|
39
|
-
|
|
40
|
-
export type ContentBoolean = boolean
|
|
41
|
-
export type ContentSimpleValue = string | number
|
|
42
|
-
|
|
43
|
-
export type Ref = HTMLElement
|
|
44
|
-
|
|
45
|
-
export type AlignY =
|
|
46
|
-
| ContentAlignY
|
|
47
|
-
| ContentAlignY[]
|
|
48
|
-
| Partial<Record<BreakpointKeys, ContentAlignY>>
|
|
49
|
-
|
|
50
|
-
export type AlignX =
|
|
51
|
-
| ContentAlignX
|
|
52
|
-
| ContentAlignX[]
|
|
53
|
-
| Partial<Record<BreakpointKeys, ContentAlignX>>
|
|
54
|
-
|
|
55
|
-
export type Direction =
|
|
56
|
-
| ContentDirection
|
|
57
|
-
| ContentDirection[]
|
|
58
|
-
| Partial<Record<BreakpointKeys, ContentDirection>>
|
|
59
|
-
|
|
60
|
-
export type ResponsiveBoolType =
|
|
61
|
-
| ContentBoolean
|
|
62
|
-
| ContentBoolean[]
|
|
63
|
-
| Partial<Record<BreakpointKeys, ContentBoolean>>
|
|
64
|
-
|
|
65
|
-
export type Responsive =
|
|
66
|
-
| ContentSimpleValue
|
|
67
|
-
| ContentSimpleValue[]
|
|
68
|
-
| Partial<Record<BreakpointKeys, number | string>>
|
|
69
|
-
|
|
70
|
-
export type ExtendCss = Css | Css[] | Partial<Record<BreakpointKeys, Css>>
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Extracts the props type from a Pyreon component function — multi-overload
|
|
74
|
-
* aware. Matches up to 4 call signatures and produces the UNION of their
|
|
75
|
-
* first-argument types. Iterator / List ship 3-overload primitives whose
|
|
76
|
-
* LAST overload is `ChildrenProps` (the loosest); without overload-aware
|
|
77
|
-
* extraction, `ExtractProps<Iterator>` returned just `ChildrenProps` and
|
|
78
|
-
* lost `SimpleProps<T>` / `ObjectProps<T>` shapes. Mirrors vitus-labs
|
|
79
|
-
* PR #222.
|
|
80
|
-
*
|
|
81
|
-
* Kept in sync with the copies in `@pyreon/core` / `@pyreon/attrs` /
|
|
82
|
-
* `@pyreon/rocketstyle`.
|
|
83
|
-
*/
|
|
84
|
-
export type ExtractProps<TComponentOrTProps> = TComponentOrTProps extends {
|
|
85
|
-
(props: infer P1, ...args: any): any
|
|
86
|
-
(props: infer P2, ...args: any): any
|
|
87
|
-
(props: infer P3, ...args: any): any
|
|
88
|
-
(props: infer P4, ...args: any): any
|
|
89
|
-
}
|
|
90
|
-
? P1 | P2 | P3 | P4
|
|
91
|
-
: TComponentOrTProps extends {
|
|
92
|
-
(props: infer P1, ...args: any): any
|
|
93
|
-
(props: infer P2, ...args: any): any
|
|
94
|
-
(props: infer P3, ...args: any): any
|
|
95
|
-
}
|
|
96
|
-
? P1 | P2 | P3
|
|
97
|
-
: TComponentOrTProps extends {
|
|
98
|
-
(props: infer P1, ...args: any): any
|
|
99
|
-
(props: infer P2, ...args: any): any
|
|
100
|
-
}
|
|
101
|
-
? P1 | P2
|
|
102
|
-
: TComponentOrTProps extends ComponentFn<infer TProps>
|
|
103
|
-
? TProps
|
|
104
|
-
: TComponentOrTProps
|
|
105
|
-
|
|
106
|
-
export type PyreonComponent<P extends Record<string, any> = {}> = ComponentFn<P> & PyreonStatic
|
|
107
|
-
|
|
108
|
-
export interface PyreonStatic {
|
|
109
|
-
displayName?: string | undefined
|
|
110
|
-
pkgName?: string
|
|
111
|
-
PYREON__COMPONENT?: `@pyreon/${string}`
|
|
112
|
-
}
|
package/src/utils.ts
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
// `import.meta.env.DEV` is provided by Vite/Rolldown at build time and
|
|
2
|
-
// literal-replaced so prod bundles tree-shake the dev branch to zero bytes.
|
|
3
|
-
// Typed through a narrowing interface so downstream packages don't need
|
|
4
|
-
// `vite/client` in their tsconfigs to type-check this file transitively.
|
|
5
|
-
export const IS_DEVELOPMENT: boolean = process.env.NODE_ENV !== 'production'
|