@pyreon/elements 0.11.1 → 0.11.3
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 +8 -7
- package/src/Element/component.tsx +211 -0
- package/src/Element/constants.ts +96 -0
- package/src/Element/index.ts +6 -0
- package/src/Element/types.ts +168 -0
- package/src/Element/utils.ts +15 -0
- package/src/List/component.tsx +57 -0
- package/src/List/index.ts +5 -0
- package/src/Overlay/component.tsx +131 -0
- package/src/Overlay/context.tsx +37 -0
- package/src/Overlay/index.ts +7 -0
- package/src/Overlay/useOverlay.tsx +616 -0
- package/src/Portal/component.tsx +41 -0
- package/src/Portal/index.ts +5 -0
- package/src/Text/component.tsx +65 -0
- package/src/Text/index.ts +5 -0
- package/src/Text/styled.ts +30 -0
- package/src/Util/component.tsx +43 -0
- package/src/Util/index.ts +5 -0
- package/src/__tests__/Content.test.tsx +115 -0
- package/src/__tests__/Element.test.ts +604 -0
- package/src/__tests__/Iterator.test.ts +483 -0
- package/src/__tests__/List.test.ts +199 -0
- package/src/__tests__/Overlay.test.ts +485 -0
- package/src/__tests__/Portal.test.ts +82 -0
- package/src/__tests__/Text.test.ts +274 -0
- package/src/__tests__/Util.test.ts +63 -0
- package/src/__tests__/Wrapper.test.tsx +152 -0
- package/src/__tests__/equalBeforeAfter.test.ts +122 -0
- package/src/__tests__/helpers.test.ts +65 -0
- package/src/__tests__/overlayContext.test.tsx +78 -0
- package/src/__tests__/responsiveProps.test.ts +298 -0
- package/src/__tests__/useOverlay.test.ts +1330 -0
- package/src/__tests__/utils.test.ts +69 -0
- package/src/constants.ts +1 -0
- package/src/helpers/Content/component.tsx +51 -0
- package/src/helpers/Content/index.ts +3 -0
- package/src/helpers/Content/styled.ts +105 -0
- package/src/helpers/Content/types.ts +49 -0
- package/src/helpers/Iterator/component.tsx +252 -0
- package/src/helpers/Iterator/index.ts +13 -0
- package/src/helpers/Iterator/types.ts +79 -0
- package/src/helpers/Wrapper/component.tsx +78 -0
- package/src/helpers/Wrapper/constants.ts +10 -0
- package/src/helpers/Wrapper/index.ts +3 -0
- package/src/helpers/Wrapper/styled.ts +69 -0
- package/src/helpers/Wrapper/types.ts +56 -0
- package/src/helpers/Wrapper/utils.ts +7 -0
- package/src/helpers/index.ts +4 -0
- package/src/index.ts +37 -0
- package/src/types.ts +81 -0
- package/src/utils.ts +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pyreon/elements",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.3",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/pyreon/pyreon",
|
|
@@ -24,7 +24,8 @@
|
|
|
24
24
|
"!lib/**/*.map",
|
|
25
25
|
"!lib/analysis",
|
|
26
26
|
"README.md",
|
|
27
|
-
"LICENSE"
|
|
27
|
+
"LICENSE",
|
|
28
|
+
"src"
|
|
28
29
|
],
|
|
29
30
|
"engines": {
|
|
30
31
|
"node": ">= 22"
|
|
@@ -43,13 +44,13 @@
|
|
|
43
44
|
"typecheck": "tsc --noEmit"
|
|
44
45
|
},
|
|
45
46
|
"peerDependencies": {
|
|
46
|
-
"@pyreon/core": "^0.11.
|
|
47
|
-
"@pyreon/reactivity": "^0.11.
|
|
48
|
-
"@pyreon/ui-core": "^0.11.
|
|
49
|
-
"@pyreon/unistyle": "^0.11.
|
|
47
|
+
"@pyreon/core": "^0.11.3",
|
|
48
|
+
"@pyreon/reactivity": "^0.11.3",
|
|
49
|
+
"@pyreon/ui-core": "^0.11.3",
|
|
50
|
+
"@pyreon/unistyle": "^0.11.3"
|
|
50
51
|
},
|
|
51
52
|
"devDependencies": {
|
|
52
53
|
"@vitus-labs/tools-rolldown": "^1.15.3",
|
|
53
|
-
"@pyreon/typescript": "^0.11.
|
|
54
|
+
"@pyreon/typescript": "^0.11.3"
|
|
54
55
|
}
|
|
55
56
|
}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core building block of the elements package. Renders a three-section layout
|
|
3
|
+
* (beforeContent / content / afterContent) inside a flex Wrapper. When only
|
|
4
|
+
* content is present, the Wrapper inherits content-level alignment directly
|
|
5
|
+
* to avoid an unnecessary nesting layer. Handles HTML-specific edge cases
|
|
6
|
+
* like void elements (input, img) and inline elements (span, a) by
|
|
7
|
+
* skipping children or switching sub-tags accordingly.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { onMount } from "@pyreon/core"
|
|
11
|
+
import { render } from "@pyreon/ui-core"
|
|
12
|
+
import { PKG_NAME } from "../constants"
|
|
13
|
+
import { Content, Wrapper } from "../helpers"
|
|
14
|
+
import type { PyreonElement } from "./types"
|
|
15
|
+
import { getShouldBeEmpty, isInlineElement } from "./utils"
|
|
16
|
+
|
|
17
|
+
const equalize = (el: HTMLElement, direction: unknown) => {
|
|
18
|
+
const beforeEl = el.firstElementChild as HTMLElement | null
|
|
19
|
+
const afterEl = el.lastElementChild as HTMLElement | null
|
|
20
|
+
|
|
21
|
+
if (beforeEl && afterEl && beforeEl !== afterEl) {
|
|
22
|
+
const type: "height" | "width" = direction === "rows" ? "height" : "width"
|
|
23
|
+
const prop = type === "height" ? "offsetHeight" : "offsetWidth"
|
|
24
|
+
const beforeSize = beforeEl[prop]
|
|
25
|
+
const afterSize = afterEl[prop]
|
|
26
|
+
|
|
27
|
+
if (Number.isInteger(beforeSize) && Number.isInteger(afterSize)) {
|
|
28
|
+
const maxSize = `${Math.max(beforeSize, afterSize)}px`
|
|
29
|
+
beforeEl.style[type] = maxSize
|
|
30
|
+
afterEl.style[type] = maxSize
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const defaultDirection = "inline"
|
|
36
|
+
const defaultContentDirection = "rows"
|
|
37
|
+
const defaultAlignX = "left"
|
|
38
|
+
const defaultAlignY = "center"
|
|
39
|
+
|
|
40
|
+
const Component: PyreonElement = ({
|
|
41
|
+
innerRef,
|
|
42
|
+
tag,
|
|
43
|
+
label,
|
|
44
|
+
content,
|
|
45
|
+
children,
|
|
46
|
+
beforeContent,
|
|
47
|
+
afterContent,
|
|
48
|
+
equalBeforeAfter,
|
|
49
|
+
|
|
50
|
+
block,
|
|
51
|
+
equalCols,
|
|
52
|
+
gap,
|
|
53
|
+
|
|
54
|
+
direction,
|
|
55
|
+
alignX = defaultAlignX,
|
|
56
|
+
alignY = defaultAlignY,
|
|
57
|
+
|
|
58
|
+
css,
|
|
59
|
+
contentCss,
|
|
60
|
+
beforeContentCss,
|
|
61
|
+
afterContentCss,
|
|
62
|
+
|
|
63
|
+
contentDirection = defaultContentDirection,
|
|
64
|
+
contentAlignX = defaultAlignX,
|
|
65
|
+
contentAlignY = defaultAlignY,
|
|
66
|
+
|
|
67
|
+
beforeContentDirection = defaultDirection,
|
|
68
|
+
beforeContentAlignX = defaultAlignX,
|
|
69
|
+
beforeContentAlignY = defaultAlignY,
|
|
70
|
+
|
|
71
|
+
afterContentDirection = defaultDirection,
|
|
72
|
+
afterContentAlignX = defaultAlignX,
|
|
73
|
+
afterContentAlignY = defaultAlignY,
|
|
74
|
+
|
|
75
|
+
ref,
|
|
76
|
+
...props
|
|
77
|
+
}) => {
|
|
78
|
+
// --------------------------------------------------------
|
|
79
|
+
// check if should render only single element
|
|
80
|
+
// --------------------------------------------------------
|
|
81
|
+
const shouldBeEmpty = !!props.dangerouslySetInnerHTML || getShouldBeEmpty(tag)
|
|
82
|
+
|
|
83
|
+
// --------------------------------------------------------
|
|
84
|
+
// if not single element, calculate values
|
|
85
|
+
// --------------------------------------------------------
|
|
86
|
+
const isSimpleElement = !beforeContent && !afterContent
|
|
87
|
+
const CHILDREN = children ?? content ?? label
|
|
88
|
+
|
|
89
|
+
const isInline = isInlineElement(tag)
|
|
90
|
+
const SUB_TAG = isInline ? "span" : undefined
|
|
91
|
+
|
|
92
|
+
// --------------------------------------------------------
|
|
93
|
+
// direction & alignX & alignY calculations
|
|
94
|
+
// --------------------------------------------------------
|
|
95
|
+
let wrapperDirection: typeof direction = direction
|
|
96
|
+
let wrapperAlignX: typeof alignX = alignX
|
|
97
|
+
let wrapperAlignY: typeof alignY = alignY
|
|
98
|
+
|
|
99
|
+
if (isSimpleElement) {
|
|
100
|
+
if (contentDirection) wrapperDirection = contentDirection
|
|
101
|
+
if (contentAlignX) wrapperAlignX = contentAlignX
|
|
102
|
+
if (contentAlignY) wrapperAlignY = contentAlignY
|
|
103
|
+
} else if (direction) {
|
|
104
|
+
wrapperDirection = direction
|
|
105
|
+
} else {
|
|
106
|
+
wrapperDirection = defaultDirection
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// --------------------------------------------------------
|
|
110
|
+
// equalBeforeAfter: measure & equalize slot dimensions
|
|
111
|
+
// --------------------------------------------------------
|
|
112
|
+
let equalizeRef: HTMLElement | null = null
|
|
113
|
+
const externalRef = ref ?? innerRef
|
|
114
|
+
|
|
115
|
+
const mergedRef = (node: HTMLElement | null) => {
|
|
116
|
+
equalizeRef = node
|
|
117
|
+
if (typeof externalRef === "function") externalRef(node)
|
|
118
|
+
else if (externalRef != null) {
|
|
119
|
+
;(externalRef as unknown as { current: HTMLElement | null }).current = node
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (equalBeforeAfter && beforeContent && afterContent) {
|
|
124
|
+
onMount(() => {
|
|
125
|
+
if (equalizeRef) equalize(equalizeRef, direction)
|
|
126
|
+
return undefined
|
|
127
|
+
})
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// --------------------------------------------------------
|
|
131
|
+
// common wrapper props
|
|
132
|
+
// --------------------------------------------------------
|
|
133
|
+
const WRAPPER_PROPS = {
|
|
134
|
+
ref: mergedRef,
|
|
135
|
+
extendCss: css,
|
|
136
|
+
tag,
|
|
137
|
+
block,
|
|
138
|
+
direction: wrapperDirection,
|
|
139
|
+
alignX: wrapperAlignX,
|
|
140
|
+
alignY: wrapperAlignY,
|
|
141
|
+
as: undefined, // reset styled-components `as` prop
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// --------------------------------------------------------
|
|
145
|
+
// return simple/empty element like input or image etc.
|
|
146
|
+
// --------------------------------------------------------
|
|
147
|
+
if (shouldBeEmpty) {
|
|
148
|
+
return <Wrapper {...props} {...WRAPPER_PROPS} />
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return (
|
|
152
|
+
<Wrapper {...props} {...WRAPPER_PROPS} isInline={isInline}>
|
|
153
|
+
{beforeContent && (
|
|
154
|
+
<Content
|
|
155
|
+
tag={SUB_TAG}
|
|
156
|
+
contentType="before"
|
|
157
|
+
parentDirection={wrapperDirection}
|
|
158
|
+
extendCss={beforeContentCss}
|
|
159
|
+
direction={beforeContentDirection}
|
|
160
|
+
alignX={beforeContentAlignX}
|
|
161
|
+
alignY={beforeContentAlignY}
|
|
162
|
+
equalCols={equalCols}
|
|
163
|
+
gap={gap}
|
|
164
|
+
>
|
|
165
|
+
{beforeContent}
|
|
166
|
+
</Content>
|
|
167
|
+
)}
|
|
168
|
+
|
|
169
|
+
{isSimpleElement ? (
|
|
170
|
+
render(CHILDREN)
|
|
171
|
+
) : (
|
|
172
|
+
<Content
|
|
173
|
+
tag={SUB_TAG}
|
|
174
|
+
contentType="content"
|
|
175
|
+
parentDirection={wrapperDirection}
|
|
176
|
+
extendCss={contentCss}
|
|
177
|
+
direction={contentDirection}
|
|
178
|
+
alignX={contentAlignX}
|
|
179
|
+
alignY={contentAlignY}
|
|
180
|
+
equalCols={equalCols}
|
|
181
|
+
>
|
|
182
|
+
{CHILDREN}
|
|
183
|
+
</Content>
|
|
184
|
+
)}
|
|
185
|
+
|
|
186
|
+
{afterContent && (
|
|
187
|
+
<Content
|
|
188
|
+
tag={SUB_TAG}
|
|
189
|
+
contentType="after"
|
|
190
|
+
parentDirection={wrapperDirection}
|
|
191
|
+
extendCss={afterContentCss}
|
|
192
|
+
direction={afterContentDirection}
|
|
193
|
+
alignX={afterContentAlignX}
|
|
194
|
+
alignY={afterContentAlignY}
|
|
195
|
+
equalCols={equalCols}
|
|
196
|
+
gap={gap}
|
|
197
|
+
>
|
|
198
|
+
{afterContent}
|
|
199
|
+
</Content>
|
|
200
|
+
)}
|
|
201
|
+
</Wrapper>
|
|
202
|
+
)
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const name = `${PKG_NAME}/Element` as const
|
|
206
|
+
|
|
207
|
+
Component.displayName = name
|
|
208
|
+
Component.pkgName = PKG_NAME
|
|
209
|
+
Component.PYREON__COMPONENT = name
|
|
210
|
+
|
|
211
|
+
export default Component
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/** Props consumed by Element that should not be forwarded to the underlying DOM node. */
|
|
2
|
+
export const RESERVED_PROPS = [
|
|
3
|
+
"innerRef",
|
|
4
|
+
"tag",
|
|
5
|
+
"block",
|
|
6
|
+
"label",
|
|
7
|
+
"children",
|
|
8
|
+
"beforeContent",
|
|
9
|
+
"afterContent",
|
|
10
|
+
|
|
11
|
+
"equalCols",
|
|
12
|
+
"vertical",
|
|
13
|
+
"direction",
|
|
14
|
+
"alignX",
|
|
15
|
+
"alignY",
|
|
16
|
+
|
|
17
|
+
"css",
|
|
18
|
+
"contentCss",
|
|
19
|
+
"beforeContentCss",
|
|
20
|
+
"afterContentCss",
|
|
21
|
+
|
|
22
|
+
"contentDirection",
|
|
23
|
+
"contentAlignX",
|
|
24
|
+
"contentAlignY",
|
|
25
|
+
|
|
26
|
+
"beforeContentDirection",
|
|
27
|
+
"beforeContentAlignX",
|
|
28
|
+
"beforeContentAlignY",
|
|
29
|
+
|
|
30
|
+
"afterContentDirection",
|
|
31
|
+
"afterContentAlignX",
|
|
32
|
+
"afterContentAlignY",
|
|
33
|
+
] as const
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* HTML tags that are inline-level by default. When Element renders one of
|
|
37
|
+
* these tags, child Content wrappers use `span` instead of `div` to
|
|
38
|
+
* preserve valid HTML nesting.
|
|
39
|
+
*/
|
|
40
|
+
export const INLINE_ELEMENTS = {
|
|
41
|
+
span: true,
|
|
42
|
+
a: true,
|
|
43
|
+
button: true,
|
|
44
|
+
input: true,
|
|
45
|
+
label: true,
|
|
46
|
+
select: true,
|
|
47
|
+
textarea: true,
|
|
48
|
+
br: true,
|
|
49
|
+
img: true,
|
|
50
|
+
strong: true,
|
|
51
|
+
small: true,
|
|
52
|
+
code: true,
|
|
53
|
+
b: true,
|
|
54
|
+
big: true,
|
|
55
|
+
i: true,
|
|
56
|
+
tt: true,
|
|
57
|
+
abbr: true,
|
|
58
|
+
acronym: true,
|
|
59
|
+
cite: true,
|
|
60
|
+
dfn: true,
|
|
61
|
+
em: true,
|
|
62
|
+
kbd: true,
|
|
63
|
+
samp: true,
|
|
64
|
+
var: true,
|
|
65
|
+
bdo: true,
|
|
66
|
+
map: true,
|
|
67
|
+
object: true,
|
|
68
|
+
q: true,
|
|
69
|
+
script: true,
|
|
70
|
+
sub: true,
|
|
71
|
+
sup: true,
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* HTML void/self-closing elements that cannot have children. When Element
|
|
76
|
+
* detects one of these tags, it skips rendering beforeContent/content/afterContent
|
|
77
|
+
* and returns the Wrapper alone.
|
|
78
|
+
*/
|
|
79
|
+
export const EMPTY_ELEMENTS = {
|
|
80
|
+
area: true,
|
|
81
|
+
base: true,
|
|
82
|
+
br: true,
|
|
83
|
+
col: true,
|
|
84
|
+
embed: true,
|
|
85
|
+
hr: true,
|
|
86
|
+
img: true,
|
|
87
|
+
input: true,
|
|
88
|
+
keygen: true,
|
|
89
|
+
link: true,
|
|
90
|
+
textarea: true,
|
|
91
|
+
// 'meta': true,
|
|
92
|
+
// 'param': true,
|
|
93
|
+
source: true,
|
|
94
|
+
track: true,
|
|
95
|
+
wbr: true,
|
|
96
|
+
}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import type { ComponentFn, PyreonHTMLAttributes } from "@pyreon/core"
|
|
2
|
+
import type { HTMLTags } from "@pyreon/ui-core"
|
|
3
|
+
import type {
|
|
4
|
+
AlignX,
|
|
5
|
+
AlignY,
|
|
6
|
+
Content,
|
|
7
|
+
Direction,
|
|
8
|
+
ExtendCss,
|
|
9
|
+
InnerRef,
|
|
10
|
+
PyreonStatic,
|
|
11
|
+
Responsive,
|
|
12
|
+
ResponsiveBoolType,
|
|
13
|
+
} from "../types"
|
|
14
|
+
|
|
15
|
+
export type Props = Partial<{
|
|
16
|
+
/**
|
|
17
|
+
* Valid HTML Tag
|
|
18
|
+
*/
|
|
19
|
+
tag: HTMLTags
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Ref prop, alternative to `ref`
|
|
23
|
+
*/
|
|
24
|
+
innerRef: InnerRef
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Valid `children`
|
|
28
|
+
*/
|
|
29
|
+
children: Content
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Alternative prop to `children`
|
|
33
|
+
* It is recommended to pass only one of `children`, `content` or `label` props
|
|
34
|
+
*
|
|
35
|
+
* The prioritization of rendering is following: `children` → `content` → `label`
|
|
36
|
+
*/
|
|
37
|
+
content: Content
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Alternative prop to `children`
|
|
41
|
+
* It is recommended to pass only one of `children`, `content` or `label` props
|
|
42
|
+
*
|
|
43
|
+
* The prioritization of rendering is following: `children` → `content` → `label`
|
|
44
|
+
*/
|
|
45
|
+
label: Content
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Valid `children` to be rendered inside _beforeContent_ wrapper
|
|
49
|
+
*/
|
|
50
|
+
beforeContent: Content
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Valid `children` to be rendered inside _afterContent_ wrapper
|
|
54
|
+
*/
|
|
55
|
+
afterContent: Content
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* A boolean type to define whether **Element** should behave
|
|
59
|
+
* as an inline or block element (`flex` vs. `inline-flex`)
|
|
60
|
+
*/
|
|
61
|
+
block: ResponsiveBoolType
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* A boolean type to define whether inner wrappers should be equal
|
|
65
|
+
* (have the same width or height)
|
|
66
|
+
*/
|
|
67
|
+
equalCols: ResponsiveBoolType
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* When true, measures the `beforeContent` and `afterContent` slot wrappers
|
|
71
|
+
* after render and sets both to the larger dimension so they match.
|
|
72
|
+
*/
|
|
73
|
+
equalBeforeAfter: boolean
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Defines a `gap` spacing between inner wrappers
|
|
77
|
+
*/
|
|
78
|
+
gap: Responsive
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Defines direction of inner wrappers
|
|
82
|
+
*/
|
|
83
|
+
direction: Direction
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Defines flow of `children` elements within its inner wrapper.
|
|
87
|
+
*/
|
|
88
|
+
contentDirection: Direction
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Defines flow of `beforeContent` elements within its inner wrapper.
|
|
92
|
+
*/
|
|
93
|
+
beforeContentDirection: Direction
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Defines flow of `afterContent` elements within its inner wrapper.
|
|
97
|
+
*/
|
|
98
|
+
afterContentDirection: Direction
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Define alignment horizontally.
|
|
102
|
+
*/
|
|
103
|
+
alignX: AlignX
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Defines how `content` children are aligned horizontally.
|
|
107
|
+
*/
|
|
108
|
+
contentAlignX: AlignX
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Defines how `beforeContent` children are aligned horizontally.
|
|
112
|
+
*/
|
|
113
|
+
beforeContentAlignX: AlignX
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Defines how `afterContent` children are aligned horizontally.
|
|
117
|
+
*/
|
|
118
|
+
afterContentAlignX: AlignX
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Define alignment vertically.
|
|
122
|
+
*/
|
|
123
|
+
alignY: AlignY
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Defines how `content` children are aligned vertically.
|
|
127
|
+
*/
|
|
128
|
+
contentAlignY: AlignY
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Defines how `beforeContent` children are aligned vertically.
|
|
132
|
+
*/
|
|
133
|
+
beforeContentAlignY: AlignY
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Defines how `afterContent` children are aligned vertically.
|
|
137
|
+
*/
|
|
138
|
+
afterContentAlignY: AlignY
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* `dangerouslySetInnerHTML` prop
|
|
142
|
+
*/
|
|
143
|
+
dangerouslySetInnerHTML: { __html: string }
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* An additional prop for extending styling of the **root** wrapper element
|
|
147
|
+
*/
|
|
148
|
+
css: ExtendCss
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* An additional prop for extending styling of the **content** wrapper element.
|
|
152
|
+
*/
|
|
153
|
+
contentCss: ExtendCss
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* An additional prop for extending styling of the **beforeContent** wrapper element.
|
|
157
|
+
*/
|
|
158
|
+
beforeContentCss: ExtendCss
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* An additional prop for extending styling of the **afterContent** wrapper element.
|
|
162
|
+
*/
|
|
163
|
+
afterContentCss: ExtendCss
|
|
164
|
+
}> &
|
|
165
|
+
PyreonHTMLAttributes
|
|
166
|
+
|
|
167
|
+
export type PyreonElement<P extends Record<string, unknown> = {}> = ComponentFn<Props & P> &
|
|
168
|
+
PyreonStatic
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { EMPTY_ELEMENTS, INLINE_ELEMENTS } from "./constants"
|
|
2
|
+
|
|
3
|
+
type GetValue = (tag?: string) => boolean
|
|
4
|
+
|
|
5
|
+
/** Checks whether the given HTML tag is an inline-level element, used to determine sub-tag nesting. */
|
|
6
|
+
export const isInlineElement: GetValue = (tag) => {
|
|
7
|
+
if (tag && tag in INLINE_ELEMENTS) return true
|
|
8
|
+
return false
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/** Checks whether the given HTML tag is a void element that cannot have children. */
|
|
12
|
+
export const getShouldBeEmpty: GetValue = (tag) => {
|
|
13
|
+
if (tag && tag in EMPTY_ELEMENTS) return true
|
|
14
|
+
return false
|
|
15
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
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 { omit, pick } from "@pyreon/ui-core"
|
|
9
|
+
import { PKG_NAME } from "../constants"
|
|
10
|
+
import type { ElementProps, PyreonElement } from "../Element"
|
|
11
|
+
import { Element } from "../Element"
|
|
12
|
+
import type { Props as IteratorProps } from "../helpers/Iterator"
|
|
13
|
+
import Iterator from "../helpers/Iterator"
|
|
14
|
+
import type { MergeTypes } from "../types"
|
|
15
|
+
|
|
16
|
+
type ListProps = {
|
|
17
|
+
/**
|
|
18
|
+
* A boolean value. When set to `false`, component returns fragment.
|
|
19
|
+
* When set to `true`, component returns as the **root** element `Element`
|
|
20
|
+
* component.
|
|
21
|
+
*/
|
|
22
|
+
rootElement?: boolean
|
|
23
|
+
/**
|
|
24
|
+
* Label prop from `Element` component is being ignored.
|
|
25
|
+
*/
|
|
26
|
+
label: never
|
|
27
|
+
/**
|
|
28
|
+
* Content prop from `Element` component is being ignored.
|
|
29
|
+
*/
|
|
30
|
+
content: never
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export type Props = MergeTypes<[IteratorProps, ListProps]>
|
|
34
|
+
|
|
35
|
+
const Component: PyreonElement<Props> = (({
|
|
36
|
+
rootElement = false,
|
|
37
|
+
ref,
|
|
38
|
+
...props
|
|
39
|
+
}: Partial<Props & ElementProps>) => {
|
|
40
|
+
const renderedList = <Iterator {...pick(props, Iterator.RESERVED_PROPS)} />
|
|
41
|
+
|
|
42
|
+
if (!rootElement) return renderedList
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<Element {...(ref ? { ref } : {})} {...omit(props, Iterator.RESERVED_PROPS)}>
|
|
46
|
+
{renderedList}
|
|
47
|
+
</Element>
|
|
48
|
+
)
|
|
49
|
+
}) as PyreonElement<Props>
|
|
50
|
+
|
|
51
|
+
const name = `${PKG_NAME}/List` as const
|
|
52
|
+
|
|
53
|
+
Component.displayName = name
|
|
54
|
+
Component.pkgName = PKG_NAME
|
|
55
|
+
Component.PYREON__COMPONENT = name
|
|
56
|
+
|
|
57
|
+
export default Component
|