@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.
Files changed (70) hide show
  1. package/package.json +10 -12
  2. package/src/Element/component.tsx +0 -315
  3. package/src/Element/constants.ts +0 -96
  4. package/src/Element/index.ts +0 -6
  5. package/src/Element/types.ts +0 -168
  6. package/src/Element/utils.ts +0 -15
  7. package/src/List/component.tsx +0 -105
  8. package/src/List/index.ts +0 -5
  9. package/src/Overlay/component.tsx +0 -140
  10. package/src/Overlay/context.tsx +0 -36
  11. package/src/Overlay/index.ts +0 -7
  12. package/src/Overlay/positioning.ts +0 -191
  13. package/src/Overlay/useOverlay.tsx +0 -461
  14. package/src/Portal/component.tsx +0 -54
  15. package/src/Portal/index.ts +0 -5
  16. package/src/Text/component.tsx +0 -67
  17. package/src/Text/index.ts +0 -5
  18. package/src/Text/styled.ts +0 -30
  19. package/src/Util/component.tsx +0 -43
  20. package/src/Util/index.ts +0 -5
  21. package/src/__tests__/Content.test.tsx +0 -123
  22. package/src/__tests__/Element-slot-reactivity.browser.test.tsx +0 -152
  23. package/src/__tests__/Element.test.ts +0 -819
  24. package/src/__tests__/Iterator.test.ts +0 -492
  25. package/src/__tests__/Iterator.types.test.ts +0 -237
  26. package/src/__tests__/List.test.ts +0 -199
  27. package/src/__tests__/Overlay.test.ts +0 -492
  28. package/src/__tests__/Portal.test.ts +0 -156
  29. package/src/__tests__/Text.test.ts +0 -274
  30. package/src/__tests__/Util.test.ts +0 -63
  31. package/src/__tests__/Wrapper-innerhtml.test.tsx +0 -178
  32. package/src/__tests__/Wrapper.test.tsx +0 -196
  33. package/src/__tests__/elements.browser.test.tsx +0 -132
  34. package/src/__tests__/equalBeforeAfter.test.ts +0 -122
  35. package/src/__tests__/helpers.test.ts +0 -65
  36. package/src/__tests__/integration.test.tsx +0 -118
  37. package/src/__tests__/internElementBundle.test.ts +0 -102
  38. package/src/__tests__/iterator-function-children.test.tsx +0 -120
  39. package/src/__tests__/native-markers.test.ts +0 -13
  40. package/src/__tests__/overlayContext.test.tsx +0 -78
  41. package/src/__tests__/perf-stress.browser.test.tsx +0 -119
  42. package/src/__tests__/positioning.test.ts +0 -90
  43. package/src/__tests__/responsiveProps.test.ts +0 -328
  44. package/src/__tests__/slot-component-reference.test.tsx +0 -157
  45. package/src/__tests__/useOverlay.test.ts +0 -1336
  46. package/src/__tests__/utils.test.ts +0 -69
  47. package/src/__tests__/wrapper-block-cascade.test.ts +0 -121
  48. package/src/constants.ts +0 -1
  49. package/src/env.d.ts +0 -6
  50. package/src/helpers/Content/component.tsx +0 -75
  51. package/src/helpers/Content/index.ts +0 -3
  52. package/src/helpers/Content/styled.ts +0 -105
  53. package/src/helpers/Content/types.ts +0 -49
  54. package/src/helpers/Iterator/component.tsx +0 -316
  55. package/src/helpers/Iterator/index.ts +0 -30
  56. package/src/helpers/Iterator/types.ts +0 -138
  57. package/src/helpers/Wrapper/component.tsx +0 -180
  58. package/src/helpers/Wrapper/constants.ts +0 -10
  59. package/src/helpers/Wrapper/index.ts +0 -3
  60. package/src/helpers/Wrapper/styled.ts +0 -64
  61. package/src/helpers/Wrapper/types.ts +0 -56
  62. package/src/helpers/Wrapper/utils.ts +0 -7
  63. package/src/helpers/index.ts +0 -4
  64. package/src/helpers/internElementBundle.ts +0 -37
  65. package/src/helpers/isPyreonComponent.ts +0 -46
  66. package/src/index.ts +0 -42
  67. package/src/manifest.ts +0 -190
  68. package/src/tests/manifest-snapshot.test.ts +0 -45
  69. package/src/types.ts +0 -112
  70. 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
- }>
@@ -1,7 +0,0 @@
1
- import { INLINE_ELEMENTS_FLEX_FIX } from './constants'
2
-
3
- type IsWebFixNeeded = (tag?: string) => boolean
4
- export const isWebFixNeeded: IsWebFixNeeded = (tag) => {
5
- if (tag && tag in INLINE_ELEMENTS_FLEX_FIX) return true
6
- return false
7
- }
@@ -1,4 +0,0 @@
1
- import Content from './Content'
2
- import Wrapper from './Wrapper'
3
-
4
- export { Content, Wrapper }
@@ -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'