@lattice-ui/accordion 0.5.0-next.1 → 0.5.0-next.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.
@@ -6,53 +6,106 @@ local Slot = _core.Slot
6
6
  local Presence = TS.import(script, TS.getModule(script, "@lattice-ui", "layer").out).Presence
7
7
  local _motion = TS.import(script, TS.getModule(script, "@lattice-ui", "motion").out)
8
8
  local createSurfaceRevealRecipe = _motion.createSurfaceRevealRecipe
9
- local usePresenceMotion = _motion.usePresenceMotion
9
+ local usePresenceMotionController = _motion.usePresenceMotionController
10
10
  local useAccordionItemContext = TS.import(script, script.Parent, "context").useAccordionItemContext
11
+ local function getImplRestProps(props)
12
+ local restProps = {}
13
+ for rawKey, value in pairs(props) do
14
+ if not (type(rawKey) == "string") then
15
+ continue
16
+ end
17
+ if rawKey == "present" or rawKey == "forceMount" or rawKey == "transition" or rawKey == "onExitComplete" or rawKey == "asChild" or rawKey == "children" then
18
+ continue
19
+ end
20
+ restProps[rawKey] = value
21
+ end
22
+ return restProps
23
+ end
24
+ local function getContentRestProps(props)
25
+ local restProps = {}
26
+ for rawKey, value in pairs(props) do
27
+ if not (type(rawKey) == "string") then
28
+ continue
29
+ end
30
+ if rawKey == "asChild" or rawKey == "forceMount" or rawKey == "transition" or rawKey == "children" then
31
+ continue
32
+ end
33
+ restProps[rawKey] = value
34
+ end
35
+ return restProps
36
+ end
11
37
  local function AccordionContentImpl(props)
38
+ local present = props.present
39
+ local forceMount = props.forceMount
40
+ local transition = props.transition
41
+ local onExitComplete = props.onExitComplete
42
+ local asChild = props.asChild
43
+ local children = props.children
44
+ local restProps = getImplRestProps(props)
12
45
  local defaultTransition = React.useMemo(function()
13
46
  return createSurfaceRevealRecipe()
14
47
  end, {})
15
- local config = props.transition or defaultTransition
16
- local motionRef = usePresenceMotion(props.motionPresent, config, props.onExitComplete)
17
- if props.asChild then
18
- local child = props.children
19
- if not React.isValidElement(child) then
20
- error("[AccordionContent] `asChild` requires a child element.")
48
+ local config = transition or defaultTransition
49
+ local motion = usePresenceMotionController({
50
+ present = present,
51
+ forceMount = forceMount,
52
+ config = config,
53
+ onExitComplete = onExitComplete,
54
+ })
55
+ local mounted = motion.mounted
56
+ local visible = mounted and (motion.present or motion.phase ~= "exited")
57
+ if not mounted then
58
+ return nil
59
+ end
60
+ if asChild then
61
+ local child = children
62
+ if React.Children.count(children) ~= 1 or not React.isValidElement(child) then
63
+ error("[AccordionContent] `asChild` requires a single child element.")
21
64
  end
22
- return React.createElement(Slot, {
23
- Visible = props.visible,
24
- ref = motionRef,
25
- }, child)
65
+ local _attributes = table.clone(restProps)
66
+ setmetatable(_attributes, nil)
67
+ _attributes.Visible = visible
68
+ _attributes.ref = motion.ref
69
+ return React.createElement(Slot, _attributes, child)
26
70
  end
27
- return React.createElement("frame", {
71
+ local _attributes = {
28
72
  BackgroundColor3 = Color3.fromRGB(35, 41, 54),
29
73
  BorderSizePixel = 0,
30
- Size = UDim2.fromOffset(260, 44),
31
- Visible = props.visible,
32
- ref = motionRef,
33
- }, props.children)
74
+ }
75
+ for _k, _v in restProps do
76
+ _attributes[_k] = _v
77
+ end
78
+ _attributes.Visible = visible
79
+ _attributes.ref = motion.ref
80
+ return React.createElement("frame", _attributes, children)
34
81
  end
35
82
  local function AccordionContent(props)
36
83
  local itemContext = useAccordionItemContext()
37
- local forceMount = props.forceMount == true
38
- if forceMount then
39
- return React.createElement(AccordionContentImpl, {
40
- asChild = props.asChild,
41
- motionPresent = itemContext.open,
42
- transition = props.transition,
43
- visible = itemContext.open,
44
- }, props.children)
84
+ local asChild = props.asChild
85
+ local forceMount = props.forceMount
86
+ local transition = props.transition
87
+ local children = props.children
88
+ local restProps = getContentRestProps(props)
89
+ local shouldForceMount = forceMount == true
90
+ if shouldForceMount then
91
+ local _attributes = table.clone(restProps)
92
+ setmetatable(_attributes, nil)
93
+ _attributes.asChild = asChild
94
+ _attributes.forceMount = true
95
+ _attributes.present = itemContext.open
96
+ _attributes.transition = transition
97
+ return React.createElement(AccordionContentImpl, _attributes, children)
45
98
  end
46
99
  return React.createElement(Presence, {
47
100
  present = itemContext.open,
48
101
  render = function(state)
49
- return React.createElement(AccordionContentImpl, {
50
- asChild = props.asChild,
51
- motionPresent = state.isPresent,
52
- onExitComplete = state.onExitComplete,
53
- transition = props.transition,
54
- visible = true,
55
- }, props.children)
102
+ local _attributes = table.clone(restProps)
103
+ setmetatable(_attributes, nil)
104
+ _attributes.asChild = asChild
105
+ _attributes.onExitComplete = state.onExitComplete
106
+ _attributes.present = state.isPresent
107
+ _attributes.transition = transition
108
+ return React.createElement(AccordionContentImpl, _attributes, children)
56
109
  end,
57
110
  })
58
111
  end
@@ -33,9 +33,11 @@ export type AccordionTriggerProps = {
33
33
  asChild?: boolean;
34
34
  children?: React.ReactElement;
35
35
  };
36
+ type AccordionContentGuiProps = React.Attributes & Record<string, unknown>;
36
37
  export type AccordionContentProps = {
37
38
  asChild?: boolean;
38
39
  forceMount?: boolean;
39
40
  transition?: PresenceMotionConfig;
40
41
  children?: React.ReactNode;
41
- };
42
+ } & AccordionContentGuiProps;
43
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lattice-ui/accordion",
3
- "version": "0.5.0-next.1",
3
+ "version": "0.5.0-next.3",
4
4
  "private": false,
5
5
  "main": "out/init.luau",
6
6
  "types": "out/index.d.ts",
@@ -16,9 +16,9 @@
16
16
  "url": "https://github.com/astra-void/lattice-ui.git"
17
17
  },
18
18
  "dependencies": {
19
- "@lattice-ui/core": "0.5.0-next.1",
20
- "@lattice-ui/layer": "0.5.0-next.1",
21
- "@lattice-ui/motion": "0.5.0-next.1"
19
+ "@lattice-ui/core": "0.5.0-next.3",
20
+ "@lattice-ui/layer": "0.5.0-next.3",
21
+ "@lattice-ui/motion": "0.5.0-next.3"
22
22
  },
23
23
  "devDependencies": {
24
24
  "@rbxts/react": "17.3.7-ts.1",
@@ -30,8 +30,8 @@
30
30
  },
31
31
  "scripts": {
32
32
  "build": "rbxtsc -p tsconfig.json",
33
- "lint": "eslint .",
34
- "lint:fix": "eslint . --fix",
33
+ "lint": "biome check src",
34
+ "lint:fix": "biome check src --write --unsafe",
35
35
  "typecheck": "tsc -p tsconfig.typecheck.json",
36
36
  "watch": "rbxtsc -p tsconfig.json -w"
37
37
  }
@@ -1,30 +1,93 @@
1
1
  import { React, Slot } from "@lattice-ui/core";
2
2
  import { Presence } from "@lattice-ui/layer";
3
- import { createSurfaceRevealRecipe, type PresenceMotionConfig, usePresenceMotion } from "@lattice-ui/motion";
3
+ import { createSurfaceRevealRecipe, usePresenceMotionController } from "@lattice-ui/motion";
4
4
  import { useAccordionItemContext } from "./context";
5
5
  import type { AccordionContentProps } from "./types";
6
6
 
7
- function AccordionContentImpl(props: {
8
- motionPresent: boolean;
9
- visible: boolean;
10
- transition?: PresenceMotionConfig;
7
+ type GuiPropBag = React.Attributes & Record<string, unknown>;
8
+
9
+ type AccordionContentImplProps = AccordionContentProps & {
10
+ present: boolean;
11
11
  onExitComplete?: () => void;
12
- asChild?: boolean;
13
- children?: React.ReactNode;
14
- }) {
12
+ };
13
+
14
+ function getImplRestProps(props: AccordionContentImplProps): GuiPropBag {
15
+ const restProps: GuiPropBag = {};
16
+
17
+ for (const [rawKey, value] of pairs(props as Record<string, unknown>)) {
18
+ if (!typeIs(rawKey, "string")) {
19
+ continue;
20
+ }
21
+
22
+ if (
23
+ rawKey === "present" ||
24
+ rawKey === "forceMount" ||
25
+ rawKey === "transition" ||
26
+ rawKey === "onExitComplete" ||
27
+ rawKey === "asChild" ||
28
+ rawKey === "children"
29
+ ) {
30
+ continue;
31
+ }
32
+
33
+ restProps[rawKey] = value;
34
+ }
35
+
36
+ return restProps;
37
+ }
38
+
39
+ function getContentRestProps(props: AccordionContentProps): GuiPropBag {
40
+ const restProps: GuiPropBag = {};
41
+
42
+ for (const [rawKey, value] of pairs(props as Record<string, unknown>)) {
43
+ if (!typeIs(rawKey, "string")) {
44
+ continue;
45
+ }
46
+
47
+ if (rawKey === "asChild" || rawKey === "forceMount" || rawKey === "transition" || rawKey === "children") {
48
+ continue;
49
+ }
50
+
51
+ restProps[rawKey] = value;
52
+ }
53
+
54
+ return restProps;
55
+ }
56
+
57
+ function AccordionContentImpl(props: AccordionContentImplProps) {
58
+ const present = props.present;
59
+ const forceMount = props.forceMount;
60
+ const transition = props.transition;
61
+ const onExitComplete = props.onExitComplete;
62
+ const asChild = props.asChild;
63
+ const children = props.children;
64
+ const restProps = getImplRestProps(props);
65
+
15
66
  const defaultTransition = React.useMemo(() => createSurfaceRevealRecipe(), []);
16
- const config = props.transition ?? defaultTransition;
67
+ const config = transition ?? defaultTransition;
68
+
69
+ const motion = usePresenceMotionController<Frame>({
70
+ present,
71
+ forceMount,
72
+ config,
73
+ onExitComplete,
74
+ });
17
75
 
18
- const motionRef = usePresenceMotion<Frame>(props.motionPresent, config, props.onExitComplete);
76
+ const mounted = motion.mounted;
77
+ const visible = mounted && (motion.present || motion.phase !== "exited");
19
78
 
20
- if (props.asChild) {
21
- const child = props.children;
22
- if (!React.isValidElement(child)) {
23
- error("[AccordionContent] `asChild` requires a child element.");
79
+ if (!mounted) {
80
+ return undefined;
81
+ }
82
+
83
+ if (asChild) {
84
+ const child = children;
85
+ if (React.Children.count(children) !== 1 || !React.isValidElement(child)) {
86
+ error("[AccordionContent] `asChild` requires a single child element.");
24
87
  }
25
88
 
26
89
  return (
27
- <Slot Visible={props.visible} ref={motionRef}>
90
+ <Slot {...restProps} Visible={visible} ref={motion.ref}>
28
91
  {child}
29
92
  </Slot>
30
93
  );
@@ -34,28 +97,35 @@ function AccordionContentImpl(props: {
34
97
  <frame
35
98
  BackgroundColor3={Color3.fromRGB(35, 41, 54)}
36
99
  BorderSizePixel={0}
37
- Size={UDim2.fromOffset(260, 44)}
38
- Visible={props.visible}
39
- ref={motionRef}
100
+ {...restProps}
101
+ Visible={visible}
102
+ ref={motion.ref}
40
103
  >
41
- {props.children}
104
+ {children}
42
105
  </frame>
43
106
  );
44
107
  }
45
108
 
46
109
  export function AccordionContent(props: AccordionContentProps) {
47
110
  const itemContext = useAccordionItemContext();
48
- const forceMount = props.forceMount === true;
111
+ const asChild = props.asChild;
112
+ const forceMount = props.forceMount;
113
+ const transition = props.transition;
114
+ const children = props.children;
115
+ const restProps = getContentRestProps(props);
116
+
117
+ const shouldForceMount = forceMount === true;
49
118
 
50
- if (forceMount) {
119
+ if (shouldForceMount) {
51
120
  return (
52
121
  <AccordionContentImpl
53
- asChild={props.asChild}
54
- motionPresent={itemContext.open}
55
- transition={props.transition}
56
- visible={itemContext.open}
122
+ {...restProps}
123
+ asChild={asChild}
124
+ forceMount={true}
125
+ present={itemContext.open}
126
+ transition={transition}
57
127
  >
58
- {props.children}
128
+ {children}
59
129
  </AccordionContentImpl>
60
130
  );
61
131
  }
@@ -65,13 +135,13 @@ export function AccordionContent(props: AccordionContentProps) {
65
135
  present={itemContext.open}
66
136
  render={(state) => (
67
137
  <AccordionContentImpl
68
- asChild={props.asChild}
69
- motionPresent={state.isPresent}
138
+ {...restProps}
139
+ asChild={asChild}
70
140
  onExitComplete={state.onExitComplete}
71
- transition={props.transition}
72
- visible={true}
141
+ present={state.isPresent}
142
+ transition={transition}
73
143
  >
74
- {props.children}
144
+ {children}
75
145
  </AccordionContentImpl>
76
146
  )}
77
147
  />
@@ -40,9 +40,11 @@ export type AccordionTriggerProps = {
40
40
  children?: React.ReactElement;
41
41
  };
42
42
 
43
+ type AccordionContentGuiProps = React.Attributes & Record<string, unknown>;
44
+
43
45
  export type AccordionContentProps = {
44
46
  asChild?: boolean;
45
47
  forceMount?: boolean;
46
48
  transition?: PresenceMotionConfig;
47
49
  children?: React.ReactNode;
48
- };
50
+ } & AccordionContentGuiProps;