@lattice-ui/core 0.1.1
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/out/context.d.ts +2 -0
- package/out/context.luau +17 -0
- package/out/index.d.ts +6 -0
- package/out/init.luau +18 -0
- package/out/react.d.ts +2 -0
- package/out/react.luau +7 -0
- package/out/reactRoblox.d.ts +2 -0
- package/out/reactRoblox.luau +7 -0
- package/out/refs.d.ts +5 -0
- package/out/refs.luau +36 -0
- package/out/slot.d.ts +9 -0
- package/out/slot.luau +111 -0
- package/out/useControllableState.d.ts +7 -0
- package/out/useControllableState.luau +29 -0
- package/package.json +22 -0
- package/scripts/ensure-hoisted-links.mjs +35 -0
- package/src/context.ts +15 -0
- package/src/index.ts +6 -0
- package/src/react.ts +3 -0
- package/src/reactRoblox.ts +3 -0
- package/src/refs.ts +28 -0
- package/src/slot.tsx +116 -0
- package/src/useControllableState.ts +28 -0
- package/tsconfig.json +11 -0
- package/tsconfig.typecheck.json +25 -0
package/out/context.d.ts
ADDED
package/out/context.luau
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local React = TS.import(script, TS.getModule(script, "@rbxts", "react"))
|
|
4
|
+
local function createStrictContext(name)
|
|
5
|
+
local Ctx = React.createContext(nil)
|
|
6
|
+
local function useCtx()
|
|
7
|
+
local v = React.useContext(Ctx)
|
|
8
|
+
if v == nil then
|
|
9
|
+
error(`[{name}] context is undefined. Wrap components with <{name}.Provider>.`)
|
|
10
|
+
end
|
|
11
|
+
return v
|
|
12
|
+
end
|
|
13
|
+
return { Ctx.Provider, useCtx }
|
|
14
|
+
end
|
|
15
|
+
return {
|
|
16
|
+
createStrictContext = createStrictContext,
|
|
17
|
+
}
|
package/out/index.d.ts
ADDED
package/out/init.luau
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local exports = {}
|
|
4
|
+
for _k, _v in TS.import(script, script, "context") or {} do
|
|
5
|
+
exports[_k] = _v
|
|
6
|
+
end
|
|
7
|
+
exports.React = TS.import(script, script, "react").default
|
|
8
|
+
exports.ReactRoblox = TS.import(script, script, "reactRoblox").default
|
|
9
|
+
for _k, _v in TS.import(script, script, "refs") or {} do
|
|
10
|
+
exports[_k] = _v
|
|
11
|
+
end
|
|
12
|
+
for _k, _v in TS.import(script, script, "slot") or {} do
|
|
13
|
+
exports[_k] = _v
|
|
14
|
+
end
|
|
15
|
+
for _k, _v in TS.import(script, script, "useControllableState") or {} do
|
|
16
|
+
exports[_k] = _v
|
|
17
|
+
end
|
|
18
|
+
return exports
|
package/out/react.d.ts
ADDED
package/out/react.luau
ADDED
package/out/refs.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type React from "@rbxts/react";
|
|
2
|
+
type AnyRef<T> = React.Ref<T> | React.ForwardedRef<T>;
|
|
3
|
+
export declare function setRef<T>(ref: AnyRef<T> | undefined, value: T | undefined): void;
|
|
4
|
+
export declare function composeRefs<T>(...refs: Array<AnyRef<T> | undefined>): (node: T | undefined) => void;
|
|
5
|
+
export {};
|
package/out/refs.luau
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local function isRefCallback(ref)
|
|
3
|
+
local _ref = ref
|
|
4
|
+
return type(_ref) == "function"
|
|
5
|
+
end
|
|
6
|
+
local function isMutableRefObject(ref)
|
|
7
|
+
local _ref = ref
|
|
8
|
+
local _condition = type(_ref) == "table"
|
|
9
|
+
if _condition then
|
|
10
|
+
_condition = ref.current ~= nil
|
|
11
|
+
end
|
|
12
|
+
return _condition
|
|
13
|
+
end
|
|
14
|
+
local function setRef(ref, value)
|
|
15
|
+
if isRefCallback(ref) then
|
|
16
|
+
ref(value)
|
|
17
|
+
return nil
|
|
18
|
+
end
|
|
19
|
+
if isMutableRefObject(ref) then
|
|
20
|
+
ref.current = value
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
local function composeRefs(...)
|
|
24
|
+
local refs = { ... }
|
|
25
|
+
return function(node)
|
|
26
|
+
for _, ref in refs do
|
|
27
|
+
if ref then
|
|
28
|
+
setRef(ref, node)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
return {
|
|
34
|
+
setRef = setRef,
|
|
35
|
+
composeRefs = composeRefs,
|
|
36
|
+
}
|
package/out/slot.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import React from "@rbxts/react";
|
|
2
|
+
type SlotRef = React.ForwardedRef<Instance>;
|
|
3
|
+
type SlotPropBag = React.Attributes & Record<string, unknown>;
|
|
4
|
+
export type SlotProps = {
|
|
5
|
+
children: React.ReactElement<SlotPropBag>;
|
|
6
|
+
ref?: SlotRef;
|
|
7
|
+
} & SlotPropBag;
|
|
8
|
+
export declare const Slot: React.ForwardRefExoticComponent<Omit<SlotProps, "ref"> & React.RefAttributes<Instance>>;
|
|
9
|
+
export {};
|
package/out/slot.luau
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local React = TS.import(script, TS.getModule(script, "@rbxts", "react"))
|
|
4
|
+
local composeRefs = TS.import(script, script.Parent, "refs").composeRefs
|
|
5
|
+
local function isRecord(value)
|
|
6
|
+
local _value = value
|
|
7
|
+
return type(_value) == "table"
|
|
8
|
+
end
|
|
9
|
+
local function toSlotPropBag(value)
|
|
10
|
+
return if isRecord(value) then value else {}
|
|
11
|
+
end
|
|
12
|
+
local function isFn(value)
|
|
13
|
+
local _value = value
|
|
14
|
+
return type(_value) == "function"
|
|
15
|
+
end
|
|
16
|
+
local function toHandlerTable(value)
|
|
17
|
+
if not isRecord(value) then
|
|
18
|
+
return nil
|
|
19
|
+
end
|
|
20
|
+
local out = {}
|
|
21
|
+
for rawKey, candidate in pairs(value) do
|
|
22
|
+
if not (type(rawKey) == "string") then
|
|
23
|
+
continue
|
|
24
|
+
end
|
|
25
|
+
if isFn(candidate) then
|
|
26
|
+
out[rawKey] = candidate
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
return if (next(out)) ~= nil then out else nil
|
|
30
|
+
end
|
|
31
|
+
local isInstanceRefCallback, isInstanceMutableRefObject
|
|
32
|
+
local function toForwardedRef(value)
|
|
33
|
+
if value == nil then
|
|
34
|
+
return nil
|
|
35
|
+
end
|
|
36
|
+
if isInstanceRefCallback(value) then
|
|
37
|
+
return value
|
|
38
|
+
end
|
|
39
|
+
if isInstanceMutableRefObject(value) then
|
|
40
|
+
return value
|
|
41
|
+
end
|
|
42
|
+
return nil
|
|
43
|
+
end
|
|
44
|
+
function isInstanceRefCallback(value)
|
|
45
|
+
local _value = value
|
|
46
|
+
return type(_value) == "function"
|
|
47
|
+
end
|
|
48
|
+
function isInstanceMutableRefObject(value)
|
|
49
|
+
local _value = value
|
|
50
|
+
local _condition = type(_value) == "table"
|
|
51
|
+
if _condition then
|
|
52
|
+
_condition = value.current ~= nil
|
|
53
|
+
end
|
|
54
|
+
return _condition
|
|
55
|
+
end
|
|
56
|
+
local function mergeHandlerTable(a, b)
|
|
57
|
+
if not a then
|
|
58
|
+
return b
|
|
59
|
+
end
|
|
60
|
+
if not b then
|
|
61
|
+
return a
|
|
62
|
+
end
|
|
63
|
+
local _object = table.clone(a)
|
|
64
|
+
setmetatable(_object, nil)
|
|
65
|
+
local out = _object
|
|
66
|
+
for rawKey, candidate in pairs(b) do
|
|
67
|
+
if not (type(rawKey) == "string") or not isFn(candidate) then
|
|
68
|
+
continue
|
|
69
|
+
end
|
|
70
|
+
local af = a[rawKey]
|
|
71
|
+
local bf = candidate
|
|
72
|
+
out[rawKey] = if af and bf then function(...)
|
|
73
|
+
local args = { ... }
|
|
74
|
+
bf(unpack(args))
|
|
75
|
+
af(unpack(args))
|
|
76
|
+
end else (bf or af)
|
|
77
|
+
end
|
|
78
|
+
return out
|
|
79
|
+
end
|
|
80
|
+
local Slot = React.forwardRef(function(props, forwardedRef)
|
|
81
|
+
local child = props.children
|
|
82
|
+
local childProps = toSlotPropBag(child.props)
|
|
83
|
+
local _object = table.clone(props)
|
|
84
|
+
setmetatable(_object, nil)
|
|
85
|
+
for _k, _v in childProps do
|
|
86
|
+
_object[_k] = _v
|
|
87
|
+
end
|
|
88
|
+
local mergedProps = _object
|
|
89
|
+
mergedProps.children = childProps.children
|
|
90
|
+
local slotEvent = toHandlerTable(props.Event)
|
|
91
|
+
local childEvent = toHandlerTable(childProps.Event)
|
|
92
|
+
local slotChange = toHandlerTable(props.Change)
|
|
93
|
+
local childChange = toHandlerTable(childProps.Change)
|
|
94
|
+
local Event = mergeHandlerTable(slotEvent, childEvent)
|
|
95
|
+
local Change = mergeHandlerTable(slotChange, childChange)
|
|
96
|
+
if Event then
|
|
97
|
+
mergedProps.Event = Event
|
|
98
|
+
end
|
|
99
|
+
if Change then
|
|
100
|
+
mergedProps.Change = Change
|
|
101
|
+
end
|
|
102
|
+
local slotRef = toForwardedRef(props.ref)
|
|
103
|
+
local childRef = toForwardedRef(childProps.ref)
|
|
104
|
+
local mergedRef = composeRefs(childRef, forwardedRef, slotRef)
|
|
105
|
+
mergedProps.ref = mergedRef
|
|
106
|
+
return React.cloneElement(child, mergedProps)
|
|
107
|
+
end)
|
|
108
|
+
Slot.displayName = "Slot"
|
|
109
|
+
return {
|
|
110
|
+
Slot = Slot,
|
|
111
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local React = TS.import(script, TS.getModule(script, "@rbxts", "react"))
|
|
4
|
+
local function isUpdater(value)
|
|
5
|
+
local _value = value
|
|
6
|
+
return type(_value) == "function"
|
|
7
|
+
end
|
|
8
|
+
local function useControllableState(_param)
|
|
9
|
+
local value = _param.value
|
|
10
|
+
local defaultValue = _param.defaultValue
|
|
11
|
+
local onChange = _param.onChange
|
|
12
|
+
local inner, setInner = React.useState(defaultValue)
|
|
13
|
+
local controlled = value ~= nil
|
|
14
|
+
local state = if value ~= nil then value else inner
|
|
15
|
+
local setState = React.useCallback(function(nextValue)
|
|
16
|
+
local computed = if isUpdater(nextValue) then nextValue(state) else nextValue
|
|
17
|
+
if not controlled then
|
|
18
|
+
setInner(computed)
|
|
19
|
+
end
|
|
20
|
+
local _result = onChange
|
|
21
|
+
if _result ~= nil then
|
|
22
|
+
_result(computed)
|
|
23
|
+
end
|
|
24
|
+
end, { controlled, onChange, state })
|
|
25
|
+
return { state, setState }
|
|
26
|
+
end
|
|
27
|
+
return {
|
|
28
|
+
useControllableState = useControllableState,
|
|
29
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@lattice-ui/core",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"private": false,
|
|
5
|
+
"main": "out/init.luau",
|
|
6
|
+
"types": "out/index.d.ts",
|
|
7
|
+
"devDependencies": {
|
|
8
|
+
"@rbxts/react": "17.3.7-ts.1",
|
|
9
|
+
"@rbxts/react-roblox": "17.3.7-ts.1"
|
|
10
|
+
},
|
|
11
|
+
"peerDependencies": {
|
|
12
|
+
"@rbxts/react": "^17",
|
|
13
|
+
"@rbxts/react-roblox": "^17"
|
|
14
|
+
},
|
|
15
|
+
"scripts": {
|
|
16
|
+
"prebuild": "node ./scripts/ensure-hoisted-links.mjs",
|
|
17
|
+
"build": "rbxtsc -p tsconfig.json",
|
|
18
|
+
"prewatch": "node ./scripts/ensure-hoisted-links.mjs",
|
|
19
|
+
"watch": "rbxtsc -p tsconfig.json -w",
|
|
20
|
+
"typecheck": "tsc -p tsconfig.typecheck.json"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
|
|
5
|
+
const scriptDir = path.dirname(fileURLToPath(import.meta.url));
|
|
6
|
+
const packageDir = path.resolve(scriptDir, "..");
|
|
7
|
+
const packageNodeModulesDir = path.join(packageDir, "node_modules");
|
|
8
|
+
const rootNodeModulesDir = path.resolve(packageDir, "../../node_modules");
|
|
9
|
+
const scopedDirs = ["@rbxts", "@rbxts-js"];
|
|
10
|
+
const symlinkType = process.platform === "win32" ? "junction" : "dir";
|
|
11
|
+
|
|
12
|
+
fs.mkdirSync(packageNodeModulesDir, { recursive: true });
|
|
13
|
+
|
|
14
|
+
for (const scopedDir of scopedDirs) {
|
|
15
|
+
const targetPath = path.join(rootNodeModulesDir, scopedDir);
|
|
16
|
+
const linkPath = path.join(packageNodeModulesDir, scopedDir);
|
|
17
|
+
|
|
18
|
+
if (!fs.existsSync(targetPath)) {
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
let shouldRelink = true;
|
|
23
|
+
if (fs.existsSync(linkPath)) {
|
|
24
|
+
const current = fs.realpathSync(linkPath);
|
|
25
|
+
const expected = fs.realpathSync(targetPath);
|
|
26
|
+
shouldRelink = current !== expected;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (!shouldRelink) {
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
fs.rmSync(linkPath, { recursive: true, force: true });
|
|
34
|
+
fs.symlinkSync(targetPath, linkPath, symlinkType);
|
|
35
|
+
}
|
package/src/context.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import React from "@rbxts/react";
|
|
2
|
+
|
|
3
|
+
export function createStrictContext<T>(name: string) {
|
|
4
|
+
const Ctx = React.createContext<T | undefined>(undefined);
|
|
5
|
+
|
|
6
|
+
function useCtx(): T {
|
|
7
|
+
const v = React.useContext(Ctx);
|
|
8
|
+
if (v === undefined) {
|
|
9
|
+
error(`[${name}] context is undefined. Wrap components with <${name}.Provider>.`);
|
|
10
|
+
}
|
|
11
|
+
return v;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return [Ctx.Provider, useCtx] as const;
|
|
15
|
+
}
|
package/src/index.ts
ADDED
package/src/react.ts
ADDED
package/src/refs.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type React from "@rbxts/react";
|
|
2
|
+
|
|
3
|
+
type AnyRef<T> = React.Ref<T> | React.ForwardedRef<T>;
|
|
4
|
+
type RefCallback<T> = (value: T | undefined) => void;
|
|
5
|
+
|
|
6
|
+
function isRefCallback<T>(ref: AnyRef<T> | undefined): ref is RefCallback<T> {
|
|
7
|
+
return typeIs(ref, "function");
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function isMutableRefObject<T>(ref: AnyRef<T> | undefined): ref is React.MutableRefObject<T | undefined> {
|
|
11
|
+
return typeIs(ref, "table") && "current" in ref;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function setRef<T>(ref: AnyRef<T> | undefined, value: T | undefined) {
|
|
15
|
+
if (isRefCallback(ref)) {
|
|
16
|
+
ref(value);
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
if (isMutableRefObject(ref)) {
|
|
20
|
+
ref.current = value;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function composeRefs<T>(...refs: Array<AnyRef<T> | undefined>) {
|
|
25
|
+
return (node: T | undefined) => {
|
|
26
|
+
for (const ref of refs) if (ref) setRef(ref, node);
|
|
27
|
+
};
|
|
28
|
+
}
|
package/src/slot.tsx
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import React from "@rbxts/react";
|
|
2
|
+
import { composeRefs } from "./refs";
|
|
3
|
+
|
|
4
|
+
type Fn = (...args: unknown[]) => void;
|
|
5
|
+
type HandlerTable = Partial<Record<string, Fn>>;
|
|
6
|
+
type SlotRef = React.ForwardedRef<Instance>;
|
|
7
|
+
type SlotPropBag = React.Attributes & Record<string, unknown>;
|
|
8
|
+
type InstanceRefCallback = (instance: Instance | undefined) => void;
|
|
9
|
+
|
|
10
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
11
|
+
return typeIs(value, "table");
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function toSlotPropBag(value: unknown): SlotPropBag {
|
|
15
|
+
return isRecord(value) ? (value as SlotPropBag) : {};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function isFn(value: unknown): value is Fn {
|
|
19
|
+
return typeIs(value, "function");
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function toHandlerTable(value: unknown): HandlerTable | undefined {
|
|
23
|
+
if (!isRecord(value)) {
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const out: HandlerTable = {};
|
|
28
|
+
for (const [rawKey, candidate] of pairs(value)) {
|
|
29
|
+
if (!typeIs(rawKey, "string")) {
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (isFn(candidate)) {
|
|
34
|
+
out[rawKey] = candidate;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return next(out)[0] !== undefined ? out : undefined;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function toForwardedRef(value: unknown): SlotRef | undefined {
|
|
42
|
+
if (value === undefined) {
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (isInstanceRefCallback(value)) {
|
|
47
|
+
return value;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (isInstanceMutableRefObject(value)) {
|
|
51
|
+
return value;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function isInstanceRefCallback(value: unknown): value is InstanceRefCallback {
|
|
58
|
+
return typeIs(value, "function");
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function isInstanceMutableRefObject(value: unknown): value is React.MutableRefObject<Instance | undefined> {
|
|
62
|
+
return typeIs(value, "table") && "current" in value;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function mergeHandlerTable(a?: HandlerTable, b?: HandlerTable) {
|
|
66
|
+
if (!a) return b;
|
|
67
|
+
if (!b) return a;
|
|
68
|
+
const out: HandlerTable = { ...a };
|
|
69
|
+
for (const [rawKey, candidate] of pairs(b)) {
|
|
70
|
+
if (!typeIs(rawKey, "string") || !isFn(candidate)) {
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const af = a[rawKey];
|
|
75
|
+
const bf = candidate;
|
|
76
|
+
out[rawKey] =
|
|
77
|
+
af && bf
|
|
78
|
+
? (...args) => {
|
|
79
|
+
bf(...args);
|
|
80
|
+
af(...args);
|
|
81
|
+
}
|
|
82
|
+
: (bf ?? af)!;
|
|
83
|
+
}
|
|
84
|
+
return out;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export type SlotProps = {
|
|
88
|
+
children: React.ReactElement<SlotPropBag>;
|
|
89
|
+
ref?: SlotRef;
|
|
90
|
+
} & SlotPropBag;
|
|
91
|
+
|
|
92
|
+
export const Slot = React.forwardRef<Instance, SlotProps>((props, forwardedRef) => {
|
|
93
|
+
const child = props.children;
|
|
94
|
+
const childProps = toSlotPropBag((child as { props?: unknown }).props);
|
|
95
|
+
|
|
96
|
+
const mergedProps: SlotPropBag = { ...props, ...childProps };
|
|
97
|
+
mergedProps.children = childProps.children;
|
|
98
|
+
|
|
99
|
+
const slotEvent = toHandlerTable(props.Event);
|
|
100
|
+
const childEvent = toHandlerTable(childProps.Event);
|
|
101
|
+
const slotChange = toHandlerTable(props.Change);
|
|
102
|
+
const childChange = toHandlerTable(childProps.Change);
|
|
103
|
+
|
|
104
|
+
const Event = mergeHandlerTable(slotEvent, childEvent);
|
|
105
|
+
const Change = mergeHandlerTable(slotChange, childChange);
|
|
106
|
+
if (Event) mergedProps.Event = Event;
|
|
107
|
+
if (Change) mergedProps.Change = Change;
|
|
108
|
+
|
|
109
|
+
const slotRef = toForwardedRef(props.ref);
|
|
110
|
+
const childRef = toForwardedRef(childProps.ref);
|
|
111
|
+
const mergedRef = composeRefs(childRef, forwardedRef, slotRef);
|
|
112
|
+
mergedProps.ref = mergedRef;
|
|
113
|
+
|
|
114
|
+
return React.cloneElement(child, mergedProps);
|
|
115
|
+
});
|
|
116
|
+
Slot.displayName = "Slot";
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import React from "@rbxts/react";
|
|
2
|
+
|
|
3
|
+
type Props<T> = {
|
|
4
|
+
value?: T;
|
|
5
|
+
defaultValue: T;
|
|
6
|
+
onChange?: (next: T) => void;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
function isUpdater<T>(value: T | ((prev: T) => T)): value is (prev: T) => T {
|
|
10
|
+
return typeIs(value, "function");
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function useControllableState<T>({ value, defaultValue, onChange }: Props<T>) {
|
|
14
|
+
const [inner, setInner] = React.useState(defaultValue);
|
|
15
|
+
const controlled = value !== undefined;
|
|
16
|
+
const state = value !== undefined ? value : inner;
|
|
17
|
+
|
|
18
|
+
const setState = React.useCallback(
|
|
19
|
+
(nextValue: T | ((prev: T) => T)) => {
|
|
20
|
+
const computed = isUpdater(nextValue) ? nextValue(state) : nextValue;
|
|
21
|
+
if (!controlled) setInner(computed);
|
|
22
|
+
onChange?.(computed);
|
|
23
|
+
},
|
|
24
|
+
[controlled, onChange, state],
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
return [state, setState] as const;
|
|
28
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../../tsconfig.base.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"rootDir": "src",
|
|
5
|
+
"outDir": "out",
|
|
6
|
+
"declaration": true,
|
|
7
|
+
"typeRoots": ["./node_modules/@rbxts", "../../node_modules/@rbxts"],
|
|
8
|
+
"types": ["types", "compiler-types"]
|
|
9
|
+
},
|
|
10
|
+
"include": ["src"]
|
|
11
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "./tsconfig.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"noEmit": true,
|
|
5
|
+
"baseUrl": "..",
|
|
6
|
+
"rootDir": "..",
|
|
7
|
+
"paths": {
|
|
8
|
+
"@lattice-ui/checkbox": ["checkbox/src/index.ts"],
|
|
9
|
+
"@lattice-ui/core": ["core/src/index.ts"],
|
|
10
|
+
"@lattice-ui/dialog": ["dialog/src/index.ts"],
|
|
11
|
+
"@lattice-ui/focus": ["focus/src/index.ts"],
|
|
12
|
+
"@lattice-ui/layer": ["layer/src/index.ts"],
|
|
13
|
+
"@lattice-ui/menu": ["menu/src/index.ts"],
|
|
14
|
+
"@lattice-ui/popover": ["popover/src/index.ts"],
|
|
15
|
+
"@lattice-ui/popper": ["popper/src/index.ts"],
|
|
16
|
+
"@lattice-ui/radio-group": ["radio-group/src/index.ts"],
|
|
17
|
+
"@lattice-ui/style": ["style/src/index.ts"],
|
|
18
|
+
"@lattice-ui/switch": ["switch/src/index.ts"],
|
|
19
|
+
"@lattice-ui/system": ["system/src/index.ts"],
|
|
20
|
+
"@lattice-ui/tabs": ["tabs/src/index.ts"],
|
|
21
|
+
"@lattice-ui/toggle-group": ["toggle-group/src/index.ts"],
|
|
22
|
+
"@lattice-ui/tooltip": ["tooltip/src/index.ts"]
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|