@pyreon/elements 0.24.5 → 0.25.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.
Files changed (71) hide show
  1. package/lib/index.js +5 -1
  2. package/package.json +11 -13
  3. package/src/Element/component.tsx +0 -315
  4. package/src/Element/constants.ts +0 -96
  5. package/src/Element/index.ts +0 -6
  6. package/src/Element/types.ts +0 -168
  7. package/src/Element/utils.ts +0 -15
  8. package/src/List/component.tsx +0 -105
  9. package/src/List/index.ts +0 -5
  10. package/src/Overlay/component.tsx +0 -140
  11. package/src/Overlay/context.tsx +0 -36
  12. package/src/Overlay/index.ts +0 -7
  13. package/src/Overlay/positioning.ts +0 -191
  14. package/src/Overlay/useOverlay.tsx +0 -461
  15. package/src/Portal/component.tsx +0 -54
  16. package/src/Portal/index.ts +0 -5
  17. package/src/Text/component.tsx +0 -67
  18. package/src/Text/index.ts +0 -5
  19. package/src/Text/styled.ts +0 -30
  20. package/src/Util/component.tsx +0 -43
  21. package/src/Util/index.ts +0 -5
  22. package/src/__tests__/Content.test.tsx +0 -123
  23. package/src/__tests__/Element-slot-reactivity.browser.test.tsx +0 -152
  24. package/src/__tests__/Element.test.ts +0 -819
  25. package/src/__tests__/Iterator.test.ts +0 -492
  26. package/src/__tests__/Iterator.types.test.ts +0 -237
  27. package/src/__tests__/List.test.ts +0 -199
  28. package/src/__tests__/Overlay.test.ts +0 -492
  29. package/src/__tests__/Portal.test.ts +0 -156
  30. package/src/__tests__/Text.test.ts +0 -274
  31. package/src/__tests__/Util.test.ts +0 -63
  32. package/src/__tests__/Wrapper-innerhtml.test.tsx +0 -178
  33. package/src/__tests__/Wrapper.test.tsx +0 -196
  34. package/src/__tests__/elements.browser.test.tsx +0 -132
  35. package/src/__tests__/equalBeforeAfter.test.ts +0 -122
  36. package/src/__tests__/helpers.test.ts +0 -65
  37. package/src/__tests__/integration.test.tsx +0 -118
  38. package/src/__tests__/internElementBundle.test.ts +0 -102
  39. package/src/__tests__/iterator-function-children.test.tsx +0 -120
  40. package/src/__tests__/native-markers.test.ts +0 -13
  41. package/src/__tests__/overlayContext.test.tsx +0 -78
  42. package/src/__tests__/perf-stress.browser.test.tsx +0 -119
  43. package/src/__tests__/positioning.test.ts +0 -90
  44. package/src/__tests__/responsiveProps.test.ts +0 -328
  45. package/src/__tests__/slot-component-reference.test.tsx +0 -157
  46. package/src/__tests__/useOverlay.test.ts +0 -1336
  47. package/src/__tests__/utils.test.ts +0 -69
  48. package/src/__tests__/wrapper-block-cascade.test.ts +0 -121
  49. package/src/constants.ts +0 -1
  50. package/src/env.d.ts +0 -6
  51. package/src/helpers/Content/component.tsx +0 -75
  52. package/src/helpers/Content/index.ts +0 -3
  53. package/src/helpers/Content/styled.ts +0 -105
  54. package/src/helpers/Content/types.ts +0 -49
  55. package/src/helpers/Iterator/component.tsx +0 -316
  56. package/src/helpers/Iterator/index.ts +0 -30
  57. package/src/helpers/Iterator/types.ts +0 -138
  58. package/src/helpers/Wrapper/component.tsx +0 -180
  59. package/src/helpers/Wrapper/constants.ts +0 -10
  60. package/src/helpers/Wrapper/index.ts +0 -3
  61. package/src/helpers/Wrapper/styled.ts +0 -64
  62. package/src/helpers/Wrapper/types.ts +0 -56
  63. package/src/helpers/Wrapper/utils.ts +0 -7
  64. package/src/helpers/index.ts +0 -4
  65. package/src/helpers/internElementBundle.ts +0 -37
  66. package/src/helpers/isPyreonComponent.ts +0 -46
  67. package/src/index.ts +0 -42
  68. package/src/manifest.ts +0 -190
  69. package/src/tests/manifest-snapshot.test.ts +0 -45
  70. package/src/types.ts +0 -112
  71. package/src/utils.ts +0 -5
@@ -1,105 +0,0 @@
1
- /**
2
- * List component that combines Iterator (data-driven rendering) with an
3
- * optional Element root wrapper. When `rootElement` is false (default),
4
- * it renders a bare Iterator as a fragment. When true, the Iterator output
5
- * is wrapped in an Element that receives all non-iterator props (e.g.,
6
- * layout, alignment, css), allowing the list to be styled as a single block.
7
- */
8
- import type { VNodeChild } from '@pyreon/core'
9
- import { splitProps } from '@pyreon/core'
10
- import { omit, pick } from '@pyreon/ui-core'
11
- import { PKG_NAME } from '../constants'
12
- import type { ElementProps } from '../Element'
13
- import { Element } from '../Element'
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'
23
- import Iterator from '../helpers/Iterator'
24
- import type { MergeTypes } from '../types'
25
-
26
- type ListOnly = {
27
- /**
28
- * A boolean value. When set to `false`, component returns fragment.
29
- * When set to `true`, component returns as the **root** element `Element`
30
- * component.
31
- */
32
- rootElement?: boolean
33
- /**
34
- * Label prop from `Element` component is being ignored.
35
- */
36
- label?: never
37
- /**
38
- * Content prop from `Element` component is being ignored.
39
- */
40
- content?: never
41
- }
42
-
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]>
60
-
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)} />
71
-
72
- if (!own.rootElement) return renderedList
73
-
74
- return (
75
- <Element ref={own.ref as ElementProps['ref']} {...omit(props, Iterator.RESERVED_PROPS)}>
76
- {renderedList}
77
- </Element>
78
- )
79
- }
80
-
81
- const name = `${PKG_NAME}/List` as const
82
-
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
- }
104
-
105
- export default Component as unknown as ListComponent
package/src/List/index.ts DELETED
@@ -1,5 +0,0 @@
1
- import type { Props } from './component'
2
- import component from './component'
3
-
4
- export type { Props as ListProps }
5
- export { component as List }
@@ -1,140 +0,0 @@
1
- /**
2
- * Overlay component that renders a trigger element and conditionally shows
3
- * content via a Portal. The trigger receives a ref and optional show/hide
4
- * callbacks; the content is positioned and managed by the useOverlay hook.
5
- * A context Provider wraps the content to support nested overlays (e.g.,
6
- * a dropdown inside another dropdown) via blocked-state propagation.
7
- */
8
-
9
- import type { VNodeChild } from '@pyreon/core'
10
- import { nativeCompat, onMount, Portal, splitProps } from '@pyreon/core'
11
- import { render } from '@pyreon/ui-core'
12
- import { PKG_NAME } from '../constants'
13
- import type { Content, PyreonComponent } from '../types'
14
- import useOverlay, { type UseOverlayProps } from './useOverlay'
15
-
16
- const IS_BROWSER = typeof window !== 'undefined'
17
-
18
- type Align = 'bottom' | 'top' | 'left' | 'right'
19
- type AlignX = 'left' | 'center' | 'right'
20
- type AlignY = 'bottom' | 'top' | 'center'
21
-
22
- type TriggerRenderer = (
23
- props: Partial<{
24
- active: boolean
25
- showContent: () => void
26
- hideContent: () => void
27
- }>,
28
- ) => VNodeChild
29
-
30
- type ContentRenderer = (
31
- props: Partial<{
32
- active: boolean
33
- showContent: () => void
34
- hideContent: () => void
35
- align: Align
36
- alignX: AlignX
37
- alignY: AlignY
38
- }>,
39
- ) => VNodeChild
40
-
41
- export type Props = {
42
- children: ContentRenderer | Content
43
- trigger: TriggerRenderer | Content
44
- DOMLocation?: HTMLElement
45
- triggerRefName?: string
46
- contentRefName?: string
47
- } & UseOverlayProps
48
-
49
- const Component: PyreonComponent<Props> = (props) => {
50
- const [own, overlayProps] = splitProps(props, [
51
- 'children',
52
- 'trigger',
53
- 'DOMLocation',
54
- 'triggerRefName',
55
- 'contentRefName',
56
- ])
57
-
58
- const triggerRefName = own.triggerRefName ?? 'ref'
59
- const contentRefName = own.contentRefName ?? 'ref'
60
-
61
- const {
62
- active,
63
- triggerRef,
64
- contentRef,
65
- showContent,
66
- hideContent,
67
- align,
68
- alignX,
69
- alignY,
70
- setupListeners,
71
- Provider,
72
- ...ctx
73
- } = useOverlay(overlayProps)
74
-
75
- const { openOn, closeOn, type } = overlayProps
76
-
77
- const passHandlers =
78
- openOn === 'manual' || closeOn === 'manual' || closeOn === 'clickOutsideContent'
79
-
80
- const ariaHasPopup = (() => {
81
- switch (type) {
82
- case 'modal':
83
- return 'dialog' as const
84
- case 'tooltip':
85
- return 'true' as const
86
- default:
87
- return 'menu' as const
88
- }
89
- })()
90
-
91
- // Set up event listeners on mount
92
- onMount(() => {
93
- const cleanup = setupListeners()
94
- return cleanup
95
- })
96
-
97
- return (
98
- <>
99
- {render(own.trigger, {
100
- [triggerRefName]: triggerRef,
101
- active: active(),
102
- 'aria-expanded': active(),
103
- 'aria-haspopup': ariaHasPopup,
104
- ...(passHandlers ? { showContent, hideContent } : {}),
105
- })}
106
-
107
- {() =>
108
- IS_BROWSER && active() ? (
109
- <Portal target={own.DOMLocation ?? document.body}>
110
- <Provider {...ctx}>
111
- {render(own.children, {
112
- [contentRefName]: contentRef,
113
- role: type === 'modal' ? 'dialog' : undefined,
114
- 'aria-modal': type === 'modal' ? true : undefined,
115
- active: active(),
116
- align,
117
- alignX: alignX(),
118
- alignY: alignY(),
119
- ...(passHandlers ? { showContent, hideContent } : {}),
120
- })}
121
- </Provider>
122
- </Portal>
123
- ) : null
124
- }
125
- </>
126
- )
127
- }
128
-
129
- const name = `${PKG_NAME}/Overlay` as const
130
-
131
- Component.displayName = name
132
- Component.pkgName = PKG_NAME
133
- Component.PYREON__COMPONENT = name
134
-
135
- // Mark as native — compat-mode jsx() runtimes skip wrapCompatComponent so
136
- // Overlay's onMount + Portal + useOverlay hook setup run inside Pyreon's
137
- // setup frame.
138
- nativeCompat(Component)
139
-
140
- export default Component
@@ -1,36 +0,0 @@
1
- /**
2
- * Context for nested overlay coordination. When a child overlay opens, it
3
- * sets the parent's blocked state to true, preventing the parent from
4
- * closing in response to click/hover events that belong to the child.
5
- */
6
-
7
- import type { VNodeChild } from '@pyreon/core'
8
- import { createContext, nativeCompat, provide, useContext } from '@pyreon/core'
9
-
10
- export interface OverlayContext {
11
- blocked: boolean | (() => boolean)
12
- setBlocked: () => void
13
- setUnblocked: () => void
14
- }
15
-
16
- const context = createContext<OverlayContext>({} as OverlayContext)
17
-
18
- export const useOverlayContext = () => useContext(context)
19
-
20
- const Component = (props: OverlayContext & { children: VNodeChild }) => {
21
- const ctx = {
22
- blocked: props.blocked,
23
- setBlocked: props.setBlocked,
24
- setUnblocked: props.setUnblocked,
25
- }
26
-
27
- provide(context, ctx)
28
-
29
- return <>{props.children}</>
30
- }
31
-
32
- // Mark as native — invoked by Overlay internally; needs Pyreon's setup
33
- // frame for provide(context, ...) to reach descendant overlays.
34
- nativeCompat(Component)
35
-
36
- export default Component
@@ -1,7 +0,0 @@
1
- import component, { type Props } from './component'
2
- import OverlayProvider from './context'
3
- import useOverlay, { type UseOverlayProps } from './useOverlay'
4
-
5
- export type { Props as OverlayProps, UseOverlayProps }
6
-
7
- export { component as Overlay, OverlayProvider, useOverlay }
@@ -1,191 +0,0 @@
1
- /**
2
- * Pure positioning helpers for the Overlay component. Split out from
3
- * `useOverlay.tsx` so the SSR-fallback branches (`typeof window === 'undefined'`)
4
- * can be exercised directly by tests that stub `globalThis.window` — the
5
- * `useOverlay` hook itself runs these via event handlers registered inside
6
- * `onMount`, which are unreachable during module-level test imports in
7
- * happy-dom (where `window` is always defined).
8
- */
9
-
10
- export type OverlayPosition = Partial<{
11
- top: number | string
12
- bottom: number | string
13
- left: number | string
14
- right: number | string
15
- }>
16
-
17
- export type Align = 'bottom' | 'top' | 'left' | 'right'
18
- export type AlignX = 'left' | 'center' | 'right'
19
- export type AlignY = 'bottom' | 'top' | 'center'
20
-
21
- export type PositionResult = {
22
- pos: OverlayPosition
23
- resolvedAlignX: AlignX
24
- resolvedAlignY: AlignY
25
- }
26
-
27
- const sel = <T,>(cond: boolean, a: T, b: T): T => (cond ? a : b)
28
-
29
- export const calcDropdownVertical = (
30
- c: DOMRect,
31
- t: DOMRect,
32
- align: 'top' | 'bottom',
33
- alignX: AlignX,
34
- offsetX: number,
35
- offsetY: number,
36
- ): PositionResult => {
37
- // SSR-fallback: positioning only runs in the mounted browser context, but
38
- // the explicit guard documents the SSR-safety contract at the callsite
39
- // and lets `no-window-in-ssr` prove it locally. Return shape mirrors the
40
- // "no element" path below (empty `pos`, alignment preserved).
41
- if (typeof window === 'undefined') return { pos: {}, resolvedAlignX: alignX, resolvedAlignY: align }
42
- const pos: OverlayPosition = {}
43
-
44
- const topPos = t.top - offsetY - c.height
45
- const bottomPos = t.bottom + offsetY
46
- const leftPos = t.left + offsetX
47
- const rightPos = t.right - offsetX - c.width
48
-
49
- const fitsTop = topPos >= 0
50
- const fitsBottom = bottomPos + c.height <= window.innerHeight
51
- const fitsLeft = leftPos + c.width <= window.innerWidth
52
- const fitsRight = rightPos >= 0
53
-
54
- const useTop = sel(align === 'top', fitsTop, !fitsBottom)
55
- pos.top = sel(useTop, topPos, bottomPos)
56
- const resolvedAlignY: AlignY = sel(useTop, 'top', 'bottom')
57
-
58
- let resolvedAlignX: AlignX = alignX
59
- if (alignX === 'left') {
60
- pos.left = sel(fitsLeft, leftPos, rightPos)
61
- resolvedAlignX = sel(fitsLeft, 'left', 'right')
62
- } else if (alignX === 'right') {
63
- pos.left = sel(fitsRight, rightPos, leftPos)
64
- resolvedAlignX = sel(fitsRight, 'right', 'left')
65
- } else {
66
- const center = t.left + (t.right - t.left) / 2 - c.width / 2
67
- const fitsCL = center >= 0
68
- const fitsCR = center + c.width <= window.innerWidth
69
-
70
- if (fitsCL && fitsCR) {
71
- resolvedAlignX = 'center'
72
- pos.left = center
73
- } else if (fitsCL) {
74
- resolvedAlignX = 'left'
75
- pos.left = leftPos
76
- } else if (fitsCR) {
77
- resolvedAlignX = 'right'
78
- pos.left = rightPos
79
- }
80
- }
81
-
82
- return { pos, resolvedAlignX, resolvedAlignY }
83
- }
84
-
85
- export const calcDropdownHorizontal = (
86
- c: DOMRect,
87
- t: DOMRect,
88
- align: 'left' | 'right',
89
- alignY: AlignY,
90
- offsetX: number,
91
- offsetY: number,
92
- ): PositionResult => {
93
- if (typeof window === 'undefined') return { pos: {}, resolvedAlignX: align, resolvedAlignY: alignY }
94
- const pos: OverlayPosition = {}
95
-
96
- const leftPos = t.left - offsetX - c.width
97
- const rightPos = t.right + offsetX
98
- const topPos = t.top + offsetY
99
- const bottomPos = t.bottom - offsetY - c.height
100
-
101
- const fitsLeft = leftPos >= 0
102
- const fitsRight = rightPos + c.width <= window.innerWidth
103
- const fitsTop = topPos + c.height <= window.innerHeight
104
- const fitsBottom = bottomPos >= 0
105
-
106
- const useLeft = sel(align === 'left', fitsLeft, !fitsRight)
107
- pos.left = sel(useLeft, leftPos, rightPos)
108
- const resolvedAlignX: AlignX = sel(useLeft, 'left', 'right')
109
-
110
- let resolvedAlignY: AlignY = alignY
111
- if (alignY === 'top') {
112
- pos.top = sel(fitsTop, topPos, bottomPos)
113
- resolvedAlignY = sel(fitsTop, 'top', 'bottom')
114
- } else if (alignY === 'bottom') {
115
- pos.top = sel(fitsBottom, bottomPos, topPos)
116
- resolvedAlignY = sel(fitsBottom, 'bottom', 'top')
117
- } else {
118
- const center = t.top + (t.bottom - t.top) / 2 - c.height / 2
119
- const fitsCT = center >= 0
120
- const fitsCB = center + c.height <= window.innerHeight
121
-
122
- if (fitsCT && fitsCB) {
123
- resolvedAlignY = 'center'
124
- pos.top = center
125
- } else if (fitsCT) {
126
- resolvedAlignY = 'top'
127
- pos.top = topPos
128
- } else if (fitsCB) {
129
- resolvedAlignY = 'bottom'
130
- pos.top = bottomPos
131
- }
132
- }
133
-
134
- return { pos, resolvedAlignX, resolvedAlignY }
135
- }
136
-
137
- export const calcModalPos = (
138
- c: DOMRect,
139
- alignX: AlignX,
140
- alignY: AlignY,
141
- offsetX: number,
142
- offsetY: number,
143
- ): OverlayPosition => {
144
- if (typeof window === 'undefined') return {}
145
- const pos: OverlayPosition = {}
146
-
147
- switch (alignX) {
148
- case 'right':
149
- pos.right = offsetX
150
- break
151
- case 'left':
152
- pos.left = offsetX
153
- break
154
- case 'center':
155
- pos.left = window.innerWidth / 2 - c.width / 2
156
- break
157
- default:
158
- pos.right = offsetX
159
- }
160
-
161
- switch (alignY) {
162
- case 'top':
163
- pos.top = offsetY
164
- break
165
- case 'center':
166
- pos.top = window.innerHeight / 2 - c.height / 2
167
- break
168
- case 'bottom':
169
- pos.bottom = offsetY
170
- break
171
- default:
172
- pos.top = offsetY
173
- }
174
-
175
- return pos
176
- }
177
-
178
- export const adjustForAncestor = (
179
- pos: OverlayPosition,
180
- ancestor: { top: number; left: number },
181
- ): OverlayPosition => {
182
- if (ancestor.top === 0 && ancestor.left === 0) return pos
183
-
184
- const result = { ...pos }
185
- if (typeof result.top === 'number') result.top -= ancestor.top
186
- if (typeof result.bottom === 'number') result.bottom += ancestor.top
187
- if (typeof result.left === 'number') result.left -= ancestor.left
188
- if (typeof result.right === 'number') result.right += ancestor.left
189
-
190
- return result
191
- }