@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.
Files changed (52) hide show
  1. package/package.json +8 -7
  2. package/src/Element/component.tsx +211 -0
  3. package/src/Element/constants.ts +96 -0
  4. package/src/Element/index.ts +6 -0
  5. package/src/Element/types.ts +168 -0
  6. package/src/Element/utils.ts +15 -0
  7. package/src/List/component.tsx +57 -0
  8. package/src/List/index.ts +5 -0
  9. package/src/Overlay/component.tsx +131 -0
  10. package/src/Overlay/context.tsx +37 -0
  11. package/src/Overlay/index.ts +7 -0
  12. package/src/Overlay/useOverlay.tsx +616 -0
  13. package/src/Portal/component.tsx +41 -0
  14. package/src/Portal/index.ts +5 -0
  15. package/src/Text/component.tsx +65 -0
  16. package/src/Text/index.ts +5 -0
  17. package/src/Text/styled.ts +30 -0
  18. package/src/Util/component.tsx +43 -0
  19. package/src/Util/index.ts +5 -0
  20. package/src/__tests__/Content.test.tsx +115 -0
  21. package/src/__tests__/Element.test.ts +604 -0
  22. package/src/__tests__/Iterator.test.ts +483 -0
  23. package/src/__tests__/List.test.ts +199 -0
  24. package/src/__tests__/Overlay.test.ts +485 -0
  25. package/src/__tests__/Portal.test.ts +82 -0
  26. package/src/__tests__/Text.test.ts +274 -0
  27. package/src/__tests__/Util.test.ts +63 -0
  28. package/src/__tests__/Wrapper.test.tsx +152 -0
  29. package/src/__tests__/equalBeforeAfter.test.ts +122 -0
  30. package/src/__tests__/helpers.test.ts +65 -0
  31. package/src/__tests__/overlayContext.test.tsx +78 -0
  32. package/src/__tests__/responsiveProps.test.ts +298 -0
  33. package/src/__tests__/useOverlay.test.ts +1330 -0
  34. package/src/__tests__/utils.test.ts +69 -0
  35. package/src/constants.ts +1 -0
  36. package/src/helpers/Content/component.tsx +51 -0
  37. package/src/helpers/Content/index.ts +3 -0
  38. package/src/helpers/Content/styled.ts +105 -0
  39. package/src/helpers/Content/types.ts +49 -0
  40. package/src/helpers/Iterator/component.tsx +252 -0
  41. package/src/helpers/Iterator/index.ts +13 -0
  42. package/src/helpers/Iterator/types.ts +79 -0
  43. package/src/helpers/Wrapper/component.tsx +78 -0
  44. package/src/helpers/Wrapper/constants.ts +10 -0
  45. package/src/helpers/Wrapper/index.ts +3 -0
  46. package/src/helpers/Wrapper/styled.ts +69 -0
  47. package/src/helpers/Wrapper/types.ts +56 -0
  48. package/src/helpers/Wrapper/utils.ts +7 -0
  49. package/src/helpers/index.ts +4 -0
  50. package/src/index.ts +37 -0
  51. package/src/types.ts +81 -0
  52. package/src/utils.ts +1 -0
@@ -0,0 +1,69 @@
1
+ import { describe, expect, it } from "vitest"
2
+ import { getShouldBeEmpty, isInlineElement } from "../Element/utils"
3
+ import { isWebFixNeeded } from "../helpers/Wrapper/utils"
4
+
5
+ describe("isInlineElement", () => {
6
+ it("returns true for inline elements", () => {
7
+ expect(isInlineElement("span")).toBe(true)
8
+ expect(isInlineElement("a")).toBe(true)
9
+ expect(isInlineElement("button")).toBe(true)
10
+ expect(isInlineElement("input")).toBe(true)
11
+ expect(isInlineElement("label")).toBe(true)
12
+ expect(isInlineElement("strong")).toBe(true)
13
+ expect(isInlineElement("em")).toBe(true)
14
+ expect(isInlineElement("img")).toBe(true)
15
+ })
16
+
17
+ it("returns false for block elements", () => {
18
+ expect(isInlineElement("div")).toBe(false)
19
+ expect(isInlineElement("p")).toBe(false)
20
+ expect(isInlineElement("section")).toBe(false)
21
+ expect(isInlineElement("header")).toBe(false)
22
+ })
23
+
24
+ it("returns false for undefined", () => {
25
+ expect(isInlineElement(undefined)).toBe(false)
26
+ })
27
+
28
+ it("returns false for empty string", () => {
29
+ expect(isInlineElement("")).toBe(false)
30
+ })
31
+ })
32
+
33
+ describe("getShouldBeEmpty", () => {
34
+ it("returns true for void elements", () => {
35
+ expect(getShouldBeEmpty("br")).toBe(true)
36
+ expect(getShouldBeEmpty("img")).toBe(true)
37
+ expect(getShouldBeEmpty("input")).toBe(true)
38
+ expect(getShouldBeEmpty("hr")).toBe(true)
39
+ expect(getShouldBeEmpty("embed")).toBe(true)
40
+ })
41
+
42
+ it("returns false for non-void elements", () => {
43
+ expect(getShouldBeEmpty("div")).toBe(false)
44
+ expect(getShouldBeEmpty("span")).toBe(false)
45
+ expect(getShouldBeEmpty("p")).toBe(false)
46
+ })
47
+
48
+ it("returns false for undefined", () => {
49
+ expect(getShouldBeEmpty(undefined)).toBe(false)
50
+ })
51
+ })
52
+
53
+ describe("isWebFixNeeded", () => {
54
+ it("returns true for button, fieldset, legend", () => {
55
+ expect(isWebFixNeeded("button")).toBe(true)
56
+ expect(isWebFixNeeded("fieldset")).toBe(true)
57
+ expect(isWebFixNeeded("legend")).toBe(true)
58
+ })
59
+
60
+ it("returns false for other elements", () => {
61
+ expect(isWebFixNeeded("div")).toBe(false)
62
+ expect(isWebFixNeeded("span")).toBe(false)
63
+ expect(isWebFixNeeded("input")).toBe(false)
64
+ })
65
+
66
+ it("returns false for undefined", () => {
67
+ expect(isWebFixNeeded(undefined)).toBe(false)
68
+ })
69
+ })
@@ -0,0 +1 @@
1
+ export const PKG_NAME = "@pyreon/elements" as const
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Content area used inside Element to render one of the three
3
+ * layout slots (before, content, after). Passes alignment, direction,
4
+ * gap, and equalCols styling props to the underlying styled component.
5
+ * Adds a `data-pyr-element` attribute in development for debugging.
6
+ *
7
+ * Children are rendered via core `render()`.
8
+ */
9
+ import { render } from "@pyreon/ui-core"
10
+ import { IS_DEVELOPMENT } from "../../utils"
11
+ import Styled from "./styled"
12
+ import type { Props } from "./types"
13
+
14
+ const Component = ({
15
+ contentType,
16
+ tag,
17
+ parentDirection,
18
+ direction,
19
+ alignX,
20
+ alignY,
21
+ equalCols,
22
+ gap,
23
+ extendCss,
24
+ children,
25
+ ...props
26
+ }: Partial<Props>) => {
27
+ const debugProps = IS_DEVELOPMENT
28
+ ? {
29
+ "data-pyr-element": contentType,
30
+ }
31
+ : {}
32
+
33
+ const stylingProps = {
34
+ contentType,
35
+ parentDirection,
36
+ direction,
37
+ alignX,
38
+ alignY,
39
+ equalCols,
40
+ gap,
41
+ extraStyles: extendCss,
42
+ }
43
+
44
+ return (
45
+ <Styled as={tag} $contentType={contentType} $element={stylingProps} {...debugProps} {...props}>
46
+ {render(children)}
47
+ </Styled>
48
+ )
49
+ }
50
+
51
+ export default Component
@@ -0,0 +1,3 @@
1
+ import component from "./component"
2
+
3
+ export default component
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Styled component for content areas (before/content/after). Applies
3
+ * responsive flex alignment, gap spacing between slots based on parent
4
+ * direction (margin-right for inline, margin-bottom for rows), and
5
+ * equalCols flex distribution. The "content" slot gets `flex: 1` to
6
+ * fill remaining space between before and after.
7
+ */
8
+ import { config } from "@pyreon/ui-core"
9
+ import { alignContent, extendCss, makeItResponsive, value } from "@pyreon/unistyle"
10
+ import type { ResponsiveStylesCallback } from "../../types"
11
+ import type { StyledProps, ThemeProps } from "./types"
12
+
13
+ const { styled, css, component } = config
14
+
15
+ const equalColsCSS = `
16
+ flex: 1;
17
+ `
18
+
19
+ const typeContentCSS = `
20
+ flex: 1;
21
+ `
22
+
23
+ // --------------------------------------------------------
24
+ // calculate spacing between before / content / after
25
+ // --------------------------------------------------------
26
+ const gapDimensions = {
27
+ inline: {
28
+ before: "margin-right",
29
+ after: "margin-left",
30
+ },
31
+ reverseInline: {
32
+ before: "margin-right",
33
+ after: "margin-left",
34
+ },
35
+ rows: {
36
+ before: "margin-bottom",
37
+ after: "margin-top",
38
+ },
39
+ reverseRows: {
40
+ before: "margin-bottom",
41
+ after: "margin-top",
42
+ },
43
+ } as const
44
+
45
+ const calculateGap = ({
46
+ direction,
47
+ type,
48
+ value: gapValue,
49
+ }: {
50
+ direction: keyof typeof gapDimensions
51
+ type: ThemeProps["contentType"]
52
+ value: string | number | null | undefined
53
+ }) => {
54
+ if (!direction || !type || type === "content") return undefined
55
+
56
+ const finalStyles = `${gapDimensions[direction][type]}: ${gapValue};`
57
+
58
+ return finalStyles
59
+ }
60
+
61
+ // --------------------------------------------------------
62
+ // calculations of styles to be rendered
63
+ // --------------------------------------------------------
64
+ const styles: ResponsiveStylesCallback = ({ css: cssFn, theme: t, rootSize }) => cssFn`
65
+ ${alignContent({
66
+ direction: t.direction,
67
+ alignX: t.alignX,
68
+ alignY: t.alignY,
69
+ })};
70
+
71
+ ${t.equalCols && equalColsCSS};
72
+
73
+ ${
74
+ t.gap &&
75
+ t.contentType &&
76
+ calculateGap({
77
+ direction: t.parentDirection,
78
+ type: t.contentType,
79
+ value: value(t.gap, rootSize),
80
+ })
81
+ };
82
+
83
+ ${t.extraStyles && extendCss(t.extraStyles as Parameters<typeof extendCss>[0])};
84
+ `
85
+
86
+ const platformCSS = `box-sizing: border-box;`
87
+
88
+ const StyledComponent = styled(component)`
89
+ ${platformCSS};
90
+
91
+ display: flex;
92
+ align-self: stretch;
93
+ flex-wrap: wrap;
94
+
95
+ ${(({ $contentType }: StyledProps) => $contentType === "content" && typeContentCSS) as any};
96
+
97
+ ${makeItResponsive({
98
+ key: "$element",
99
+ styles,
100
+ css,
101
+ normalize: true,
102
+ })};
103
+ `
104
+
105
+ export default StyledComponent
@@ -0,0 +1,49 @@
1
+ import type { HTMLTags } from "@pyreon/ui-core"
2
+ import type {
3
+ AlignX,
4
+ AlignY,
5
+ Content,
6
+ ContentAlignX,
7
+ ContentAlignY,
8
+ ContentBoolean,
9
+ ContentDirection,
10
+ ContentSimpleValue,
11
+ Css,
12
+ Direction,
13
+ ExtendCss,
14
+ Responsive,
15
+ ResponsiveBoolType,
16
+ } from "../../types"
17
+
18
+ export interface Props {
19
+ parentDirection: Direction | undefined
20
+ gap: Responsive | undefined
21
+ contentType: "before" | "content" | "after" | undefined
22
+ children: Content
23
+ tag: HTMLTags | undefined
24
+ direction: Direction | undefined
25
+ alignX: AlignX | undefined
26
+ alignY: AlignY | undefined
27
+ equalCols: ResponsiveBoolType | undefined
28
+ extendCss: ExtendCss | undefined
29
+ }
30
+
31
+ export interface StyledProps {
32
+ $element: Pick<
33
+ Props,
34
+ "contentType" | "parentDirection" | "direction" | "alignX" | "alignY" | "equalCols" | "gap"
35
+ > & {
36
+ extraStyles: Props["extendCss"]
37
+ }
38
+ $contentType: Props["contentType"]
39
+ }
40
+
41
+ export type ThemeProps = Pick<Props, "contentType"> & {
42
+ parentDirection: ContentDirection
43
+ direction: ContentDirection
44
+ alignX: ContentAlignX
45
+ alignY: ContentAlignY
46
+ equalCols?: ContentBoolean
47
+ gap?: ContentSimpleValue
48
+ extraStyles?: Css
49
+ }
@@ -0,0 +1,252 @@
1
+ /**
2
+ * Data-driven list renderer that supports three input modes: children,
3
+ * an array of primitives, or an array of objects.
4
+ * Each item receives positional metadata (first, last, odd, even, position)
5
+ * and optional injected props via `itemProps`. Items can be individually
6
+ * wrapped with `wrapComponent`. Children always take priority over the
7
+ * component+data prop pattern.
8
+ */
9
+
10
+ import type { VNode, VNodeChild } from "@pyreon/core"
11
+ import { Fragment } from "@pyreon/core"
12
+ import { isEmpty, render } from "@pyreon/ui-core"
13
+ import type { ExtendedProps, ObjectValue, Props, SimpleValue } from "./types"
14
+
15
+ type ClassifiedData =
16
+ | { type: "simple"; data: SimpleValue[] }
17
+ | { type: "complex"; data: ObjectValue[] }
18
+ | null
19
+
20
+ const classifyData = (data: unknown[]): ClassifiedData => {
21
+ const items = data.filter(
22
+ (item) =>
23
+ item != null && !(typeof item === "object" && isEmpty(item as Record<string, unknown>)),
24
+ )
25
+
26
+ if (items.length === 0) return null
27
+
28
+ let isSimple = true
29
+ let isComplex = true
30
+
31
+ for (const item of items) {
32
+ if (typeof item === "string" || typeof item === "number") {
33
+ isComplex = false
34
+ } else if (typeof item === "object") {
35
+ isSimple = false
36
+ } else {
37
+ isSimple = false
38
+ isComplex = false
39
+ }
40
+ }
41
+
42
+ if (isSimple) return { type: "simple", data: items as SimpleValue[] }
43
+ if (isComplex) return { type: "complex", data: items as ObjectValue[] }
44
+ return null
45
+ }
46
+
47
+ const RESERVED_PROPS = [
48
+ "children",
49
+ "component",
50
+ "wrapComponent",
51
+ "data",
52
+ "itemKey",
53
+ "valueName",
54
+ "itemProps",
55
+ "wrapProps",
56
+ ] as const
57
+
58
+ type AttachItemProps = ({ i, length }: { i: number; length: number }) => ExtendedProps
59
+
60
+ const attachItemProps: AttachItemProps = ({ i, length }: { i: number; length: number }) => {
61
+ const position = i + 1
62
+
63
+ return {
64
+ index: i,
65
+ first: position === 1,
66
+ last: position === length,
67
+ odd: position % 2 === 1,
68
+ even: position % 2 === 0,
69
+ position,
70
+ }
71
+ }
72
+
73
+ const Component = (props: Props) => {
74
+ const {
75
+ itemKey,
76
+ valueName,
77
+ children,
78
+ component,
79
+ data,
80
+ wrapComponent: Wrapper,
81
+ wrapProps,
82
+ itemProps,
83
+ } = props
84
+
85
+ const injectItemProps = typeof itemProps === "function" ? itemProps : () => itemProps
86
+
87
+ const injectWrapItemProps = typeof wrapProps === "function" ? wrapProps : () => wrapProps
88
+
89
+ const getKey = (item: string | number, index: number) => {
90
+ if (typeof itemKey === "function") return itemKey(item, index)
91
+ return index
92
+ }
93
+
94
+ const renderChild = (child: VNodeChild, total = 1, i = 0) => {
95
+ if (!itemProps && !Wrapper) return child
96
+
97
+ const extendedProps = attachItemProps({
98
+ i,
99
+ length: total,
100
+ })
101
+
102
+ const finalItemProps = itemProps ? injectItemProps({}, extendedProps) : {}
103
+
104
+ if (Wrapper) {
105
+ const finalWrapProps = wrapProps ? injectWrapItemProps({}, extendedProps) : {}
106
+
107
+ return (
108
+ <Wrapper key={i} {...finalWrapProps}>
109
+ {render(child, finalItemProps)}
110
+ </Wrapper>
111
+ )
112
+ }
113
+
114
+ return render(child, {
115
+ key: i,
116
+ ...finalItemProps,
117
+ })
118
+ }
119
+
120
+ // --------------------------------------------------------
121
+ // render children
122
+ // --------------------------------------------------------
123
+ const renderChildren = () => {
124
+ if (!children) return null
125
+
126
+ // if children is Array
127
+ if (Array.isArray(children)) {
128
+ return children.map((item, i) => renderChild(item, children.length, i))
129
+ }
130
+
131
+ // if children is Fragment — check VNode type
132
+ if (
133
+ typeof children === "object" &&
134
+ "type" in (children as VNode) &&
135
+ (children as VNode).type === Fragment
136
+ ) {
137
+ const fragmentChildren = (children as VNode).children as VNodeChild[]
138
+ const childrenLength = fragmentChildren.length
139
+
140
+ return fragmentChildren.map((item, i) => renderChild(item, childrenLength, i))
141
+ }
142
+
143
+ // if single child
144
+ return renderChild(children)
145
+ }
146
+
147
+ // --------------------------------------------------------
148
+ // render array of strings or numbers
149
+ // --------------------------------------------------------
150
+ const renderSimpleArray = (simpleData: SimpleValue[]) => {
151
+ const { length } = simpleData
152
+
153
+ if (length === 0) return null
154
+
155
+ return simpleData.map((item, i) => {
156
+ const key = getKey(item, i)
157
+ const keyName = valueName ?? "children"
158
+ const extendedProps = attachItemProps({
159
+ i,
160
+ length,
161
+ })
162
+
163
+ const finalItemProps = {
164
+ ...(itemProps ? injectItemProps({ [keyName]: item }, extendedProps) : {}),
165
+ [keyName]: item,
166
+ }
167
+
168
+ if (Wrapper) {
169
+ const finalWrapProps = wrapProps
170
+ ? injectWrapItemProps({ [keyName]: item }, extendedProps)
171
+ : {}
172
+
173
+ return (
174
+ <Wrapper key={key} {...finalWrapProps}>
175
+ {render(component, finalItemProps)}
176
+ </Wrapper>
177
+ )
178
+ }
179
+
180
+ return render(component, { key, ...finalItemProps })
181
+ })
182
+ }
183
+
184
+ // --------------------------------------------------------
185
+ // render array of objects
186
+ // --------------------------------------------------------
187
+ const getObjectKey = (item: ObjectValue, index: number) => {
188
+ if (!itemKey) return item.key ?? item.id ?? item.itemId ?? index
189
+ if (typeof itemKey === "function") return itemKey(item, index)
190
+ if (typeof itemKey === "string") return item[itemKey]
191
+
192
+ return index
193
+ }
194
+
195
+ const renderComplexArray = (complexData: ObjectValue[]) => {
196
+ const { length } = complexData
197
+
198
+ if (length === 0) return null
199
+
200
+ return complexData.map((item, i) => {
201
+ const { component: itemComponent, ...restItem } = item
202
+ const renderItem = itemComponent ?? component
203
+ const key = getObjectKey(restItem, i)
204
+ const extendedProps = attachItemProps({
205
+ i,
206
+ length,
207
+ })
208
+
209
+ const finalItemProps = {
210
+ ...(itemProps ? injectItemProps(item, extendedProps) : {}),
211
+ ...restItem,
212
+ }
213
+
214
+ if (Wrapper && !itemComponent) {
215
+ const finalWrapProps = wrapProps ? injectWrapItemProps(item, extendedProps) : {}
216
+
217
+ return (
218
+ <Wrapper key={key} {...finalWrapProps}>
219
+ {render(renderItem, finalItemProps)}
220
+ </Wrapper>
221
+ )
222
+ }
223
+
224
+ return render(renderItem, { key, ...finalItemProps })
225
+ })
226
+ }
227
+
228
+ // --------------------------------------------------------
229
+ // render list items
230
+ // --------------------------------------------------------
231
+ const renderItems = (): VNodeChild => {
232
+ // children have priority over props component + data
233
+ if (children) return renderChildren() as VNodeChild
234
+
235
+ // render props component + data
236
+ if (component && Array.isArray(data)) {
237
+ const classified = classifyData(data)
238
+ if (!classified) return null
239
+ if (classified.type === "simple") return renderSimpleArray(classified.data) as VNodeChild
240
+ return renderComplexArray(classified.data) as VNodeChild
241
+ }
242
+
243
+ return null
244
+ }
245
+
246
+ return renderItems()
247
+ }
248
+
249
+ export default Object.assign(Component, {
250
+ isIterator: true as const,
251
+ RESERVED_PROPS,
252
+ })
@@ -0,0 +1,13 @@
1
+ import component from "./component"
2
+ import type {
3
+ ElementType,
4
+ ExtendedProps,
5
+ ObjectValue,
6
+ Props,
7
+ PropsCallback,
8
+ SimpleValue,
9
+ } from "./types"
10
+
11
+ export type { ElementType, ExtendedProps, ObjectValue, Props, PropsCallback, SimpleValue }
12
+
13
+ export default component
@@ -0,0 +1,79 @@
1
+ import type { ComponentFn, VNodeChild } from "@pyreon/core"
2
+ import type { HTMLTags } from "@pyreon/ui-core"
3
+
4
+ export type MaybeNull = undefined | null
5
+ export type TObj = Record<string, unknown>
6
+ export type SimpleValue = string | number
7
+ export type ObjectValue = Partial<{
8
+ id: SimpleValue
9
+ key: SimpleValue
10
+ itemId: SimpleValue
11
+ component: ElementType
12
+ }> &
13
+ Record<string, unknown>
14
+
15
+ export type ElementType<T extends Record<string, unknown> = any> = ComponentFn<T> | HTMLTags
16
+
17
+ export type ExtendedProps = {
18
+ index: number
19
+ first: boolean
20
+ last: boolean
21
+ odd: boolean
22
+ even: boolean
23
+ position: number
24
+ }
25
+
26
+ export type PropsCallback =
27
+ | TObj
28
+ | ((
29
+ itemProps: Record<string, never> | Record<string, SimpleValue> | ObjectValue,
30
+ extendedProps: ExtendedProps,
31
+ ) => TObj)
32
+
33
+ export type Props = Partial<{
34
+ /**
35
+ * Valid children
36
+ */
37
+ children: VNodeChild
38
+
39
+ /**
40
+ * Array of data passed to `component` prop
41
+ */
42
+ data: Array<SimpleValue | ObjectValue | MaybeNull>
43
+
44
+ /**
45
+ * A component to be rendered within list
46
+ */
47
+ component: ElementType
48
+
49
+ /**
50
+ * Defines name of the prop to be passed to the iteration component
51
+ * when **data** prop is type of `string[]`, `number[]` or combination
52
+ * of both. Otherwise ignored.
53
+ */
54
+ valueName: string
55
+
56
+ /**
57
+ * A component to be rendered within list. `wrapComponent`
58
+ * wraps `component`. Therefore it can be used to enhance the behavior
59
+ * of the list component
60
+ */
61
+ wrapComponent: ElementType
62
+
63
+ /**
64
+ * Extension of **item** `component` props to be passed
65
+ */
66
+ itemProps: PropsCallback
67
+
68
+ /**
69
+ * Extension of **item** `wrapComponent` props to be passed
70
+ */
71
+ wrapProps?: PropsCallback
72
+
73
+ /**
74
+ * Extension of **item** `wrapComponent` props to be passed
75
+ */
76
+ itemKey?:
77
+ | keyof ObjectValue
78
+ | ((item: SimpleValue | Omit<ObjectValue, "component">, index: number) => SimpleValue)
79
+ }>
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Wrapper component that serves as the outermost styled container for Element.
3
+ * On web, it detects button/fieldset/legend tags and applies a two-layer flex
4
+ * fix (parent + child Styled) because these HTML elements do not natively
5
+ * support `display: flex` consistently across browsers.
6
+ */
7
+ import { IS_DEVELOPMENT } from "../../utils"
8
+ import Styled from "./styled"
9
+ import type { Props } from "./types"
10
+ import { isWebFixNeeded } from "./utils"
11
+
12
+ const DEV_PROPS: Record<string, string> = IS_DEVELOPMENT ? { "data-pyr-element": "Element" } : {}
13
+
14
+ const Component = ({
15
+ children,
16
+ tag,
17
+ block,
18
+ extendCss,
19
+ direction,
20
+ alignX,
21
+ alignY,
22
+ equalCols,
23
+ isInline,
24
+ ref,
25
+ ...props
26
+ }: Partial<Props> & { ref?: any }) => {
27
+ const COMMON_PROPS = {
28
+ ...props,
29
+ ...DEV_PROPS,
30
+ ref,
31
+ as: tag,
32
+ }
33
+
34
+ const needsFix = !props.dangerouslySetInnerHTML && isWebFixNeeded(tag)
35
+
36
+ const normalElement = {
37
+ block,
38
+ direction,
39
+ alignX,
40
+ alignY,
41
+ equalCols,
42
+ extraStyles: extendCss,
43
+ }
44
+
45
+ const parentFixElement = {
46
+ parentFix: true as const,
47
+ block,
48
+ extraStyles: extendCss,
49
+ }
50
+
51
+ const childFixElement = {
52
+ childFix: true as const,
53
+ direction,
54
+ alignX,
55
+ alignY,
56
+ equalCols,
57
+ }
58
+
59
+ if (!needsFix) {
60
+ return (
61
+ <Styled {...COMMON_PROPS} $element={normalElement}>
62
+ {children}
63
+ </Styled>
64
+ )
65
+ }
66
+
67
+ const asTag = isInline ? "span" : "div"
68
+
69
+ return (
70
+ <Styled {...COMMON_PROPS} $element={parentFixElement}>
71
+ <Styled as={asTag} $childFix $element={childFixElement}>
72
+ {children}
73
+ </Styled>
74
+ </Styled>
75
+ )
76
+ }
77
+
78
+ export default Component