@lattice-ui/system 0.1.1 → 0.3.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/LICENSE +7 -0
- package/out/index.d.ts +2 -1
- package/out/init.luau +1 -0
- package/out/layout/Grid.d.ts +3 -0
- package/out/layout/Grid.luau +147 -0
- package/out/layout/gridMath.d.ts +7 -0
- package/out/layout/gridMath.luau +33 -0
- package/out/layout/types.d.ts +11 -0
- package/package.json +7 -3
- package/src/density/DensityProvider.tsx +0 -61
- package/src/density/density.ts +0 -102
- package/src/density/types.ts +0 -15
- package/src/index.ts +0 -22
- package/src/layout/Row.tsx +0 -7
- package/src/layout/Stack.tsx +0 -133
- package/src/layout/space.ts +0 -101
- package/src/layout/types.ts +0 -34
- package/src/surface/surface.ts +0 -45
- package/src/surface/surfacePrimitive.tsx +0 -59
- package/src/system/SystemProvider.tsx +0 -47
- package/src/system/baseThemeContext.ts +0 -5
- package/src/system/systemThemeContext.ts +0 -5
- package/src/system/types.ts +0 -29
- package/tsconfig.json +0 -16
- package/tsconfig.typecheck.json +0 -25
package/LICENSE
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
Copyright 2026 astra-void
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
4
|
+
|
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/out/index.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
export { DensityProvider, useDensity } from "./density/DensityProvider";
|
|
2
2
|
export { applyDensity, density } from "./density/density";
|
|
3
3
|
export type { DensityContextValue, DensityProviderProps, DensityToken } from "./density/types";
|
|
4
|
+
export { Grid } from "./layout/Grid";
|
|
4
5
|
export { Row } from "./layout/Row";
|
|
5
6
|
export { Stack } from "./layout/Stack";
|
|
6
|
-
export type { LayoutDirection, RowProps, SpaceToken, SpaceValue, StackAlign, StackAutoSize, StackJustify, StackPadding, StackProps, } from "./layout/types";
|
|
7
|
+
export type { GridProps, LayoutDirection, RowProps, SpaceToken, SpaceValue, StackAlign, StackAutoSize, StackJustify, StackPadding, StackProps, } from "./layout/types";
|
|
7
8
|
export type { SurfaceToken } from "./surface/surface";
|
|
8
9
|
export { surface } from "./surface/surface";
|
|
9
10
|
export type { SurfaceProps } from "./surface/surfacePrimitive";
|
package/out/init.luau
CHANGED
|
@@ -7,6 +7,7 @@ exports.useDensity = _DensityProvider.useDensity
|
|
|
7
7
|
local _density = TS.import(script, script, "density", "density")
|
|
8
8
|
exports.applyDensity = _density.applyDensity
|
|
9
9
|
exports.density = _density.density
|
|
10
|
+
exports.Grid = TS.import(script, script, "layout", "Grid").Grid
|
|
10
11
|
exports.Row = TS.import(script, script, "layout", "Row").Row
|
|
11
12
|
exports.Stack = TS.import(script, script, "layout", "Stack").Stack
|
|
12
13
|
exports.surface = TS.import(script, script, "surface", "surface").surface
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local React = TS.import(script, TS.getModule(script, "@lattice-ui", "core").out).React
|
|
4
|
+
local _style = TS.import(script, TS.getModule(script, "@lattice-ui", "style").out)
|
|
5
|
+
local mergeGuiProps = _style.mergeGuiProps
|
|
6
|
+
local resolveSx = _style.resolveSx
|
|
7
|
+
local useTheme = _style.useTheme
|
|
8
|
+
local _gridMath = TS.import(script, script.Parent, "gridMath")
|
|
9
|
+
local resolveGridCellWidth = _gridMath.resolveGridCellWidth
|
|
10
|
+
local resolveGridColumns = _gridMath.resolveGridColumns
|
|
11
|
+
local _space = TS.import(script, script.Parent, "space")
|
|
12
|
+
local resolvePadding = _space.resolvePadding
|
|
13
|
+
local resolveSpace = _space.resolveSpace
|
|
14
|
+
local function Grid(props)
|
|
15
|
+
local _condition = props.gap
|
|
16
|
+
if _condition == nil then
|
|
17
|
+
_condition = 0
|
|
18
|
+
end
|
|
19
|
+
local gap = _condition
|
|
20
|
+
local _condition_1 = props.rowGap
|
|
21
|
+
if _condition_1 == nil then
|
|
22
|
+
_condition_1 = gap
|
|
23
|
+
end
|
|
24
|
+
local rowGap = _condition_1
|
|
25
|
+
local _condition_2 = props.columnGap
|
|
26
|
+
if _condition_2 == nil then
|
|
27
|
+
_condition_2 = gap
|
|
28
|
+
end
|
|
29
|
+
local columnGap = _condition_2
|
|
30
|
+
local _condition_3 = props.autoSize
|
|
31
|
+
if _condition_3 == nil then
|
|
32
|
+
_condition_3 = false
|
|
33
|
+
end
|
|
34
|
+
local autoSize = _condition_3
|
|
35
|
+
local columns = props.columns
|
|
36
|
+
local minColumnWidth = props.minColumnWidth
|
|
37
|
+
local _condition_4 = props.cellHeight
|
|
38
|
+
if _condition_4 == nil then
|
|
39
|
+
_condition_4 = 32
|
|
40
|
+
end
|
|
41
|
+
local cellHeight = _condition_4
|
|
42
|
+
local children = props.children
|
|
43
|
+
local sx = props.sx
|
|
44
|
+
local asChild = props.asChild
|
|
45
|
+
if asChild ~= nil then
|
|
46
|
+
error("[Grid] `asChild` is not supported in M3.")
|
|
47
|
+
end
|
|
48
|
+
local restProps = {}
|
|
49
|
+
for rawKey, value in pairs(props) do
|
|
50
|
+
if not (type(rawKey) == "string") then
|
|
51
|
+
continue
|
|
52
|
+
end
|
|
53
|
+
if rawKey == "gap" or rawKey == "rowGap" or rawKey == "columnGap" or rawKey == "columns" or rawKey == "minColumnWidth" or rawKey == "cellHeight" or rawKey == "autoSize" or rawKey == "sx" or rawKey == "asChild" or rawKey == "padding" or rawKey == "paddingX" or rawKey == "paddingY" or rawKey == "paddingTop" or rawKey == "paddingRight" or rawKey == "paddingBottom" or rawKey == "paddingLeft" or rawKey == "children" then
|
|
54
|
+
continue
|
|
55
|
+
end
|
|
56
|
+
restProps[rawKey] = value
|
|
57
|
+
end
|
|
58
|
+
local _binding = useTheme()
|
|
59
|
+
local theme = _binding.theme
|
|
60
|
+
local resolvedRowGap = resolveSpace(theme, rowGap)
|
|
61
|
+
local resolvedColumnGap = resolveSpace(theme, columnGap)
|
|
62
|
+
local resolvedMinColumnWidth = if minColumnWidth ~= nil then resolveSpace(theme, minColumnWidth) else nil
|
|
63
|
+
local resolvedCellHeight = resolveSpace(theme, cellHeight)
|
|
64
|
+
local padding = resolvePadding(theme, props)
|
|
65
|
+
local hasPadding = padding.top > 0 or padding.right > 0 or padding.bottom > 0 or padding.left > 0
|
|
66
|
+
local layoutState, setLayoutState = React.useState(function()
|
|
67
|
+
local _object = {
|
|
68
|
+
columns = resolveGridColumns(0, {
|
|
69
|
+
columns = columns,
|
|
70
|
+
minColumnWidth = resolvedMinColumnWidth,
|
|
71
|
+
columnGap = resolvedColumnGap,
|
|
72
|
+
}),
|
|
73
|
+
}
|
|
74
|
+
local _left = "cellWidth"
|
|
75
|
+
local _condition_5 = resolvedMinColumnWidth
|
|
76
|
+
if _condition_5 == nil then
|
|
77
|
+
_condition_5 = 120
|
|
78
|
+
end
|
|
79
|
+
_object[_left] = math.max(1, _condition_5)
|
|
80
|
+
return _object
|
|
81
|
+
end)
|
|
82
|
+
local frameRef = React.useRef()
|
|
83
|
+
local setFrameRef = React.useCallback(function(instance)
|
|
84
|
+
if not instance or not instance:IsA("Frame") then
|
|
85
|
+
frameRef.current = nil
|
|
86
|
+
return nil
|
|
87
|
+
end
|
|
88
|
+
frameRef.current = instance
|
|
89
|
+
end, {})
|
|
90
|
+
local updateLayout = React.useCallback(function()
|
|
91
|
+
local frame = frameRef.current
|
|
92
|
+
if not frame then
|
|
93
|
+
return nil
|
|
94
|
+
end
|
|
95
|
+
local containerWidth = frame.AbsoluteSize.X - padding.left - padding.right
|
|
96
|
+
local nextColumns = resolveGridColumns(containerWidth, {
|
|
97
|
+
columns = columns,
|
|
98
|
+
minColumnWidth = resolvedMinColumnWidth,
|
|
99
|
+
columnGap = resolvedColumnGap,
|
|
100
|
+
})
|
|
101
|
+
local nextCellWidth = resolveGridCellWidth(containerWidth, nextColumns, resolvedColumnGap)
|
|
102
|
+
setLayoutState(function(current)
|
|
103
|
+
if current.columns == nextColumns and current.cellWidth == nextCellWidth then
|
|
104
|
+
return current
|
|
105
|
+
end
|
|
106
|
+
return {
|
|
107
|
+
columns = nextColumns,
|
|
108
|
+
cellWidth = nextCellWidth,
|
|
109
|
+
}
|
|
110
|
+
end)
|
|
111
|
+
end, { columns, padding.left, padding.right, resolvedColumnGap, resolvedMinColumnWidth })
|
|
112
|
+
React.useEffect(function()
|
|
113
|
+
updateLayout()
|
|
114
|
+
local frame = frameRef.current
|
|
115
|
+
if not frame then
|
|
116
|
+
return nil
|
|
117
|
+
end
|
|
118
|
+
local sizeConnection = frame:GetPropertyChangedSignal("AbsoluteSize"):Connect(updateLayout)
|
|
119
|
+
return function()
|
|
120
|
+
sizeConnection:Disconnect()
|
|
121
|
+
end
|
|
122
|
+
end, { updateLayout })
|
|
123
|
+
local sxProps = resolveSx(sx, theme)
|
|
124
|
+
local baseProps = {
|
|
125
|
+
BackgroundTransparency = 1,
|
|
126
|
+
BorderSizePixel = 0,
|
|
127
|
+
AutomaticSize = if autoSize == true then Enum.AutomaticSize.Y elseif autoSize == "x" then Enum.AutomaticSize.X elseif autoSize == "y" then Enum.AutomaticSize.Y elseif autoSize == "xy" then Enum.AutomaticSize.XY else Enum.AutomaticSize.None,
|
|
128
|
+
}
|
|
129
|
+
local mergedProps = mergeGuiProps(baseProps, sxProps, restProps)
|
|
130
|
+
local _attributes = table.clone(mergedProps)
|
|
131
|
+
setmetatable(_attributes, nil)
|
|
132
|
+
_attributes.ref = setFrameRef
|
|
133
|
+
return React.createElement("frame", _attributes, React.createElement("uigridlayout", {
|
|
134
|
+
CellPadding = UDim2.fromOffset(resolvedColumnGap, resolvedRowGap),
|
|
135
|
+
CellSize = UDim2.fromOffset(layoutState.cellWidth, resolvedCellHeight),
|
|
136
|
+
FillDirectionMaxCells = layoutState.columns,
|
|
137
|
+
SortOrder = Enum.SortOrder.LayoutOrder,
|
|
138
|
+
}), if hasPadding then (React.createElement("uipadding", {
|
|
139
|
+
PaddingBottom = UDim.new(0, padding.bottom),
|
|
140
|
+
PaddingLeft = UDim.new(0, padding.left),
|
|
141
|
+
PaddingRight = UDim.new(0, padding.right),
|
|
142
|
+
PaddingTop = UDim.new(0, padding.top),
|
|
143
|
+
})) else nil, children)
|
|
144
|
+
end
|
|
145
|
+
return {
|
|
146
|
+
Grid = Grid,
|
|
147
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export type ResolveGridColumnsOptions = {
|
|
2
|
+
columns?: number;
|
|
3
|
+
minColumnWidth?: number;
|
|
4
|
+
columnGap?: number;
|
|
5
|
+
};
|
|
6
|
+
export declare function resolveGridColumns(containerWidth: number, options: ResolveGridColumnsOptions): number;
|
|
7
|
+
export declare function resolveGridCellWidth(containerWidth: number, columns: number, columnGap: number): number;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local function resolveGridColumns(containerWidth, options)
|
|
3
|
+
local explicitColumns = options.columns
|
|
4
|
+
if explicitColumns ~= nil and explicitColumns > 0 then
|
|
5
|
+
return math.max(1, math.floor(explicitColumns))
|
|
6
|
+
end
|
|
7
|
+
local minColumnWidth = options.minColumnWidth
|
|
8
|
+
if minColumnWidth == nil or minColumnWidth <= 0 then
|
|
9
|
+
return 1
|
|
10
|
+
end
|
|
11
|
+
local _condition = options.columnGap
|
|
12
|
+
if _condition == nil then
|
|
13
|
+
_condition = 0
|
|
14
|
+
end
|
|
15
|
+
local gap = math.max(0, _condition)
|
|
16
|
+
local width = math.max(0, containerWidth)
|
|
17
|
+
local withGap = width + gap
|
|
18
|
+
local perColumn = minColumnWidth + gap
|
|
19
|
+
if perColumn <= 0 then
|
|
20
|
+
return 1
|
|
21
|
+
end
|
|
22
|
+
return math.max(1, math.floor(withGap / perColumn))
|
|
23
|
+
end
|
|
24
|
+
local function resolveGridCellWidth(containerWidth, columns, columnGap)
|
|
25
|
+
local safeColumns = math.max(1, math.floor(columns))
|
|
26
|
+
local safeGap = math.max(0, columnGap)
|
|
27
|
+
local availableWidth = math.max(0, containerWidth - safeGap * (safeColumns - 1))
|
|
28
|
+
return math.max(1, math.floor(availableWidth / safeColumns))
|
|
29
|
+
end
|
|
30
|
+
return {
|
|
31
|
+
resolveGridColumns = resolveGridColumns,
|
|
32
|
+
resolveGridCellWidth = resolveGridCellWidth,
|
|
33
|
+
}
|
package/out/layout/types.d.ts
CHANGED
|
@@ -26,4 +26,15 @@ export type StackProps = {
|
|
|
26
26
|
children?: React.ReactNode;
|
|
27
27
|
} & StackPadding & StyleProps;
|
|
28
28
|
export type RowProps = Omit<StackProps, "direction">;
|
|
29
|
+
export type GridProps = {
|
|
30
|
+
columns?: number;
|
|
31
|
+
minColumnWidth?: SpaceValue;
|
|
32
|
+
cellHeight?: SpaceValue;
|
|
33
|
+
gap?: SpaceValue;
|
|
34
|
+
rowGap?: SpaceValue;
|
|
35
|
+
columnGap?: SpaceValue;
|
|
36
|
+
autoSize?: StackAutoSize;
|
|
37
|
+
sx?: Sx<StyleProps>;
|
|
38
|
+
children?: React.ReactNode;
|
|
39
|
+
} & StackPadding & StyleProps;
|
|
29
40
|
export {};
|
package/package.json
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lattice-ui/system",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"main": "out/init.luau",
|
|
6
6
|
"types": "out/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"out",
|
|
9
|
+
"README.md"
|
|
10
|
+
],
|
|
7
11
|
"dependencies": {
|
|
8
|
-
"@lattice-ui/core": "0.
|
|
9
|
-
"@lattice-ui/style": "0.
|
|
12
|
+
"@lattice-ui/core": "0.3.1",
|
|
13
|
+
"@lattice-ui/style": "0.3.1"
|
|
10
14
|
},
|
|
11
15
|
"peerDependencies": {
|
|
12
16
|
"@rbxts/react": "^17",
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { createStrictContext, React, useControllableState } from "@lattice-ui/core";
|
|
2
|
-
import { ThemeProvider } from "@lattice-ui/style";
|
|
3
|
-
import { useSystemBaseThemeContext } from "../system/baseThemeContext";
|
|
4
|
-
import { SystemThemeContextProvider } from "../system/systemThemeContext";
|
|
5
|
-
import type { SystemThemeContextValue } from "../system/types";
|
|
6
|
-
import { applyDensity } from "./density";
|
|
7
|
-
import type { DensityContextValue, DensityProviderProps, DensityToken } from "./types";
|
|
8
|
-
|
|
9
|
-
const [DensityContextProvider, useDensityContext] = createStrictContext<DensityContextValue>("DensityProvider");
|
|
10
|
-
const DEFAULT_DENSITY: DensityToken = "comfortable";
|
|
11
|
-
|
|
12
|
-
export function DensityProvider(props: DensityProviderProps) {
|
|
13
|
-
const { baseTheme, setBaseTheme } = useSystemBaseThemeContext();
|
|
14
|
-
|
|
15
|
-
const [densityValue, setDensityValue] = useControllableState<DensityToken>({
|
|
16
|
-
value: props.density,
|
|
17
|
-
defaultValue: props.defaultDensity ?? DEFAULT_DENSITY,
|
|
18
|
-
onChange: props.onDensityChange,
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
// Read-path contract: resolvedTheme is derived from baseTheme + current density.
|
|
22
|
-
const resolvedTheme = React.useMemo(() => applyDensity(baseTheme, densityValue), [baseTheme, densityValue]);
|
|
23
|
-
|
|
24
|
-
const setDensity = React.useCallback(
|
|
25
|
-
(nextDensity: DensityToken) => {
|
|
26
|
-
setDensityValue(nextDensity);
|
|
27
|
-
},
|
|
28
|
-
[setDensityValue],
|
|
29
|
-
);
|
|
30
|
-
|
|
31
|
-
const densityContextValue = React.useMemo<DensityContextValue>(
|
|
32
|
-
() => ({
|
|
33
|
-
density: densityValue,
|
|
34
|
-
setDensity,
|
|
35
|
-
}),
|
|
36
|
-
[densityValue, setDensity],
|
|
37
|
-
);
|
|
38
|
-
|
|
39
|
-
const systemThemeContextValue = React.useMemo<SystemThemeContextValue>(
|
|
40
|
-
() => ({
|
|
41
|
-
theme: resolvedTheme,
|
|
42
|
-
baseTheme,
|
|
43
|
-
density: densityValue,
|
|
44
|
-
setBaseTheme,
|
|
45
|
-
setDensity,
|
|
46
|
-
}),
|
|
47
|
-
[baseTheme, densityValue, resolvedTheme, setBaseTheme, setDensity],
|
|
48
|
-
);
|
|
49
|
-
|
|
50
|
-
return (
|
|
51
|
-
<DensityContextProvider value={densityContextValue}>
|
|
52
|
-
<SystemThemeContextProvider value={systemThemeContextValue}>
|
|
53
|
-
<ThemeProvider theme={resolvedTheme}>{props.children}</ThemeProvider>
|
|
54
|
-
</SystemThemeContextProvider>
|
|
55
|
-
</DensityContextProvider>
|
|
56
|
-
);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export function useDensity() {
|
|
60
|
-
return useDensityContext();
|
|
61
|
-
}
|
package/src/density/density.ts
DELETED
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
import type { Theme } from "@lattice-ui/style";
|
|
2
|
-
import type { DensityToken } from "./types";
|
|
3
|
-
|
|
4
|
-
type DensityScale = {
|
|
5
|
-
space: number;
|
|
6
|
-
radius: number;
|
|
7
|
-
typography: number;
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
const DENSITY_SCALES: Record<DensityToken, DensityScale> = {
|
|
11
|
-
compact: {
|
|
12
|
-
space: 0.85,
|
|
13
|
-
radius: 0.9,
|
|
14
|
-
typography: 0.92,
|
|
15
|
-
},
|
|
16
|
-
comfortable: {
|
|
17
|
-
space: 1,
|
|
18
|
-
radius: 1,
|
|
19
|
-
typography: 1,
|
|
20
|
-
},
|
|
21
|
-
spacious: {
|
|
22
|
-
space: 1.15,
|
|
23
|
-
radius: 1.1,
|
|
24
|
-
typography: 1.08,
|
|
25
|
-
},
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
function scaleNonNegative(value: number, factor: number) {
|
|
29
|
-
return math.max(0, math.round(value * factor));
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function scaleTextSize(value: number, factor: number) {
|
|
33
|
-
return math.max(10, math.round(value * factor));
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function scaleSpace(theme: Theme, factor: number): Theme["space"] {
|
|
37
|
-
return {
|
|
38
|
-
0: scaleNonNegative(theme.space[0], factor),
|
|
39
|
-
2: scaleNonNegative(theme.space[2], factor),
|
|
40
|
-
4: scaleNonNegative(theme.space[4], factor),
|
|
41
|
-
6: scaleNonNegative(theme.space[6], factor),
|
|
42
|
-
8: scaleNonNegative(theme.space[8], factor),
|
|
43
|
-
10: scaleNonNegative(theme.space[10], factor),
|
|
44
|
-
12: scaleNonNegative(theme.space[12], factor),
|
|
45
|
-
14: scaleNonNegative(theme.space[14], factor),
|
|
46
|
-
16: scaleNonNegative(theme.space[16], factor),
|
|
47
|
-
20: scaleNonNegative(theme.space[20], factor),
|
|
48
|
-
24: scaleNonNegative(theme.space[24], factor),
|
|
49
|
-
32: scaleNonNegative(theme.space[32], factor),
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function scaleRadius(theme: Theme, factor: number): Theme["radius"] {
|
|
54
|
-
return {
|
|
55
|
-
none: scaleNonNegative(theme.radius.none, factor),
|
|
56
|
-
sm: scaleNonNegative(theme.radius.sm, factor),
|
|
57
|
-
md: scaleNonNegative(theme.radius.md, factor),
|
|
58
|
-
lg: scaleNonNegative(theme.radius.lg, factor),
|
|
59
|
-
xl: scaleNonNegative(theme.radius.xl, factor),
|
|
60
|
-
full: scaleNonNegative(theme.radius.full, factor),
|
|
61
|
-
};
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
function scaleTypography(theme: Theme, factor: number): Theme["typography"] {
|
|
65
|
-
return {
|
|
66
|
-
labelSm: {
|
|
67
|
-
font: theme.typography.labelSm.font,
|
|
68
|
-
textSize: scaleTextSize(theme.typography.labelSm.textSize, factor),
|
|
69
|
-
},
|
|
70
|
-
bodyMd: {
|
|
71
|
-
font: theme.typography.bodyMd.font,
|
|
72
|
-
textSize: scaleTextSize(theme.typography.bodyMd.textSize, factor),
|
|
73
|
-
},
|
|
74
|
-
titleMd: {
|
|
75
|
-
font: theme.typography.titleMd.font,
|
|
76
|
-
textSize: scaleTextSize(theme.typography.titleMd.textSize, factor),
|
|
77
|
-
},
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* M1 limitation: density is a pure theme transformer.
|
|
83
|
-
* It does not create layout or child instances.
|
|
84
|
-
*/
|
|
85
|
-
export function applyDensity(theme: Theme, token: DensityToken): Theme {
|
|
86
|
-
const scale = DENSITY_SCALES[token];
|
|
87
|
-
|
|
88
|
-
return {
|
|
89
|
-
colors: { ...theme.colors },
|
|
90
|
-
space: scaleSpace(theme, scale.space),
|
|
91
|
-
radius: scaleRadius(theme, scale.radius),
|
|
92
|
-
typography: scaleTypography(theme, scale.typography),
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* M1 limitation: this helper only returns a theme transformer.
|
|
98
|
-
* It does not modify instance graphs or perform runtime layout composition.
|
|
99
|
-
*/
|
|
100
|
-
export function density(token: DensityToken) {
|
|
101
|
-
return (theme: Theme) => applyDensity(theme, token);
|
|
102
|
-
}
|
package/src/density/types.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import type React from "@rbxts/react";
|
|
2
|
-
|
|
3
|
-
export type DensityToken = "compact" | "comfortable" | "spacious";
|
|
4
|
-
|
|
5
|
-
export type DensityProviderProps = {
|
|
6
|
-
density?: DensityToken;
|
|
7
|
-
defaultDensity?: DensityToken;
|
|
8
|
-
onDensityChange?: (next: DensityToken) => void;
|
|
9
|
-
children?: React.ReactNode;
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
export type DensityContextValue = {
|
|
13
|
-
density: DensityToken;
|
|
14
|
-
setDensity: (next: DensityToken) => void;
|
|
15
|
-
};
|
package/src/index.ts
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
export { DensityProvider, useDensity } from "./density/DensityProvider";
|
|
2
|
-
export { applyDensity, density } from "./density/density";
|
|
3
|
-
export type { DensityContextValue, DensityProviderProps, DensityToken } from "./density/types";
|
|
4
|
-
export { Row } from "./layout/Row";
|
|
5
|
-
export { Stack } from "./layout/Stack";
|
|
6
|
-
export type {
|
|
7
|
-
LayoutDirection,
|
|
8
|
-
RowProps,
|
|
9
|
-
SpaceToken,
|
|
10
|
-
SpaceValue,
|
|
11
|
-
StackAlign,
|
|
12
|
-
StackAutoSize,
|
|
13
|
-
StackJustify,
|
|
14
|
-
StackPadding,
|
|
15
|
-
StackProps,
|
|
16
|
-
} from "./layout/types";
|
|
17
|
-
export type { SurfaceToken } from "./surface/surface";
|
|
18
|
-
export { surface } from "./surface/surface";
|
|
19
|
-
export type { SurfaceProps } from "./surface/surfacePrimitive";
|
|
20
|
-
export { Surface } from "./surface/surfacePrimitive";
|
|
21
|
-
export { SystemProvider, useSystemTheme } from "./system/SystemProvider";
|
|
22
|
-
export type { SystemProviderProps, SystemThemeContextValue } from "./system/types";
|
package/src/layout/Row.tsx
DELETED
package/src/layout/Stack.tsx
DELETED
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
import { React } from "@lattice-ui/core";
|
|
2
|
-
import { mergeGuiProps, resolveSx, useTheme } from "@lattice-ui/style";
|
|
3
|
-
import { resolvePadding, resolveSpace } from "./space";
|
|
4
|
-
import type { LayoutDirection, StackAlign, StackAutoSize, StackJustify, StackProps } from "./types";
|
|
5
|
-
|
|
6
|
-
type StyleProps = React.Attributes & Record<string, unknown>;
|
|
7
|
-
|
|
8
|
-
function toHorizontalAlignment(value: StackAlign | StackJustify) {
|
|
9
|
-
switch (value) {
|
|
10
|
-
case "center":
|
|
11
|
-
return Enum.HorizontalAlignment.Center;
|
|
12
|
-
case "end":
|
|
13
|
-
return Enum.HorizontalAlignment.Right;
|
|
14
|
-
case "start":
|
|
15
|
-
default:
|
|
16
|
-
return Enum.HorizontalAlignment.Left;
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function toVerticalAlignment(value: StackAlign | StackJustify) {
|
|
21
|
-
switch (value) {
|
|
22
|
-
case "center":
|
|
23
|
-
return Enum.VerticalAlignment.Center;
|
|
24
|
-
case "end":
|
|
25
|
-
return Enum.VerticalAlignment.Bottom;
|
|
26
|
-
case "start":
|
|
27
|
-
default:
|
|
28
|
-
return Enum.VerticalAlignment.Top;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function toAutomaticSize(autoSize: StackAutoSize | undefined, direction: LayoutDirection) {
|
|
33
|
-
if (autoSize === undefined || autoSize === false) {
|
|
34
|
-
return Enum.AutomaticSize.None;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
if (autoSize === true) {
|
|
38
|
-
return direction === "vertical" ? Enum.AutomaticSize.Y : Enum.AutomaticSize.X;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
switch (autoSize) {
|
|
42
|
-
case "x":
|
|
43
|
-
return Enum.AutomaticSize.X;
|
|
44
|
-
case "y":
|
|
45
|
-
return Enum.AutomaticSize.Y;
|
|
46
|
-
case "xy":
|
|
47
|
-
return Enum.AutomaticSize.XY;
|
|
48
|
-
default:
|
|
49
|
-
return Enum.AutomaticSize.None;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export function Stack(props: StackProps) {
|
|
54
|
-
const direction = props.direction ?? "vertical";
|
|
55
|
-
const gap = props.gap ?? 0;
|
|
56
|
-
const align = props.align ?? "start";
|
|
57
|
-
const justify = props.justify ?? "start";
|
|
58
|
-
const autoSize = props.autoSize;
|
|
59
|
-
const sx = props.sx;
|
|
60
|
-
const children = props.children;
|
|
61
|
-
const asChild = (props as { asChild?: unknown }).asChild;
|
|
62
|
-
|
|
63
|
-
if (asChild !== undefined) {
|
|
64
|
-
error("[Stack] `asChild` is not supported in M3.");
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
const restProps: StyleProps = {};
|
|
68
|
-
for (const [rawKey, value] of pairs(props as Record<string, unknown>)) {
|
|
69
|
-
if (!typeIs(rawKey, "string")) {
|
|
70
|
-
continue;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
if (
|
|
74
|
-
rawKey === "direction" ||
|
|
75
|
-
rawKey === "gap" ||
|
|
76
|
-
rawKey === "align" ||
|
|
77
|
-
rawKey === "justify" ||
|
|
78
|
-
rawKey === "autoSize" ||
|
|
79
|
-
rawKey === "sx" ||
|
|
80
|
-
rawKey === "asChild" ||
|
|
81
|
-
rawKey === "padding" ||
|
|
82
|
-
rawKey === "paddingX" ||
|
|
83
|
-
rawKey === "paddingY" ||
|
|
84
|
-
rawKey === "paddingTop" ||
|
|
85
|
-
rawKey === "paddingRight" ||
|
|
86
|
-
rawKey === "paddingBottom" ||
|
|
87
|
-
rawKey === "paddingLeft" ||
|
|
88
|
-
rawKey === "children"
|
|
89
|
-
) {
|
|
90
|
-
continue;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
restProps[rawKey] = value;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const { theme } = useTheme();
|
|
97
|
-
const sxProps = resolveSx(sx, theme);
|
|
98
|
-
const baseProps: Partial<StyleProps> = {
|
|
99
|
-
BackgroundTransparency: 1,
|
|
100
|
-
BorderSizePixel: 0,
|
|
101
|
-
AutomaticSize: toAutomaticSize(autoSize, direction),
|
|
102
|
-
};
|
|
103
|
-
const mergedProps = mergeGuiProps(baseProps, sxProps, restProps);
|
|
104
|
-
|
|
105
|
-
const resolvedGap = resolveSpace(theme, gap);
|
|
106
|
-
const padding = resolvePadding(theme, props);
|
|
107
|
-
const hasPadding = padding.top > 0 || padding.right > 0 || padding.bottom > 0 || padding.left > 0;
|
|
108
|
-
|
|
109
|
-
const vertical = direction === "vertical";
|
|
110
|
-
const horizontalAlignment = vertical ? toHorizontalAlignment(align) : toHorizontalAlignment(justify);
|
|
111
|
-
const verticalAlignment = vertical ? toVerticalAlignment(justify) : toVerticalAlignment(align);
|
|
112
|
-
|
|
113
|
-
return (
|
|
114
|
-
<frame {...(mergedProps as Record<string, unknown>)}>
|
|
115
|
-
<uilistlayout
|
|
116
|
-
FillDirection={vertical ? Enum.FillDirection.Vertical : Enum.FillDirection.Horizontal}
|
|
117
|
-
HorizontalAlignment={horizontalAlignment}
|
|
118
|
-
Padding={new UDim(0, resolvedGap)}
|
|
119
|
-
SortOrder={Enum.SortOrder.LayoutOrder}
|
|
120
|
-
VerticalAlignment={verticalAlignment}
|
|
121
|
-
/>
|
|
122
|
-
{hasPadding ? (
|
|
123
|
-
<uipadding
|
|
124
|
-
PaddingBottom={new UDim(0, padding.bottom)}
|
|
125
|
-
PaddingLeft={new UDim(0, padding.left)}
|
|
126
|
-
PaddingRight={new UDim(0, padding.right)}
|
|
127
|
-
PaddingTop={new UDim(0, padding.top)}
|
|
128
|
-
/>
|
|
129
|
-
) : undefined}
|
|
130
|
-
{children}
|
|
131
|
-
</frame>
|
|
132
|
-
);
|
|
133
|
-
}
|
package/src/layout/space.ts
DELETED
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
import type { Theme } from "@lattice-ui/style";
|
|
2
|
-
import type { SpaceValue, StackPadding } from "./types";
|
|
3
|
-
|
|
4
|
-
type ResolvedPadding = {
|
|
5
|
-
top: number;
|
|
6
|
-
right: number;
|
|
7
|
-
bottom: number;
|
|
8
|
-
left: number;
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
function spaceTokenValue(theme: Theme, value: number) {
|
|
12
|
-
switch (value) {
|
|
13
|
-
case 0:
|
|
14
|
-
return theme.space[0];
|
|
15
|
-
case 2:
|
|
16
|
-
return theme.space[2];
|
|
17
|
-
case 4:
|
|
18
|
-
return theme.space[4];
|
|
19
|
-
case 6:
|
|
20
|
-
return theme.space[6];
|
|
21
|
-
case 8:
|
|
22
|
-
return theme.space[8];
|
|
23
|
-
case 10:
|
|
24
|
-
return theme.space[10];
|
|
25
|
-
case 12:
|
|
26
|
-
return theme.space[12];
|
|
27
|
-
case 14:
|
|
28
|
-
return theme.space[14];
|
|
29
|
-
case 16:
|
|
30
|
-
return theme.space[16];
|
|
31
|
-
case 20:
|
|
32
|
-
return theme.space[20];
|
|
33
|
-
case 24:
|
|
34
|
-
return theme.space[24];
|
|
35
|
-
case 32:
|
|
36
|
-
return theme.space[32];
|
|
37
|
-
default:
|
|
38
|
-
return undefined;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function clampSpace(value: number) {
|
|
43
|
-
return math.max(0, value);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export function resolveSpace(theme: Theme, value?: SpaceValue) {
|
|
47
|
-
if (value === undefined) {
|
|
48
|
-
return 0;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const tokenValue = spaceTokenValue(theme, value);
|
|
52
|
-
if (tokenValue !== undefined) {
|
|
53
|
-
return clampSpace(tokenValue);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
return clampSpace(value);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export function resolvePadding(theme: Theme, value: StackPadding): ResolvedPadding {
|
|
60
|
-
const base = resolveSpace(theme, value.padding);
|
|
61
|
-
|
|
62
|
-
let top = base;
|
|
63
|
-
let right = base;
|
|
64
|
-
let bottom = base;
|
|
65
|
-
let left = base;
|
|
66
|
-
|
|
67
|
-
if (value.paddingX !== undefined) {
|
|
68
|
-
const axis = resolveSpace(theme, value.paddingX);
|
|
69
|
-
left = axis;
|
|
70
|
-
right = axis;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
if (value.paddingY !== undefined) {
|
|
74
|
-
const axis = resolveSpace(theme, value.paddingY);
|
|
75
|
-
top = axis;
|
|
76
|
-
bottom = axis;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
if (value.paddingTop !== undefined) {
|
|
80
|
-
top = resolveSpace(theme, value.paddingTop);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
if (value.paddingRight !== undefined) {
|
|
84
|
-
right = resolveSpace(theme, value.paddingRight);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
if (value.paddingBottom !== undefined) {
|
|
88
|
-
bottom = resolveSpace(theme, value.paddingBottom);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
if (value.paddingLeft !== undefined) {
|
|
92
|
-
left = resolveSpace(theme, value.paddingLeft);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
return {
|
|
96
|
-
top,
|
|
97
|
-
right,
|
|
98
|
-
bottom,
|
|
99
|
-
left,
|
|
100
|
-
};
|
|
101
|
-
}
|
package/src/layout/types.ts
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import type { Sx, Theme } from "@lattice-ui/style";
|
|
2
|
-
import type React from "@rbxts/react";
|
|
3
|
-
|
|
4
|
-
export type LayoutDirection = "vertical" | "horizontal";
|
|
5
|
-
export type StackAlign = "start" | "center" | "end";
|
|
6
|
-
export type StackJustify = "start" | "center" | "end";
|
|
7
|
-
export type StackAutoSize = boolean | "x" | "y" | "xy";
|
|
8
|
-
export type SpaceToken = keyof Theme["space"];
|
|
9
|
-
export type SpaceValue = SpaceToken | number;
|
|
10
|
-
|
|
11
|
-
export type StackPadding = {
|
|
12
|
-
padding?: SpaceValue;
|
|
13
|
-
paddingX?: SpaceValue;
|
|
14
|
-
paddingY?: SpaceValue;
|
|
15
|
-
paddingTop?: SpaceValue;
|
|
16
|
-
paddingRight?: SpaceValue;
|
|
17
|
-
paddingBottom?: SpaceValue;
|
|
18
|
-
paddingLeft?: SpaceValue;
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
type StyleProps = React.Attributes & Record<string, unknown>;
|
|
22
|
-
|
|
23
|
-
export type StackProps = {
|
|
24
|
-
direction?: LayoutDirection;
|
|
25
|
-
gap?: SpaceValue;
|
|
26
|
-
align?: StackAlign;
|
|
27
|
-
justify?: StackJustify;
|
|
28
|
-
autoSize?: StackAutoSize;
|
|
29
|
-
sx?: Sx<StyleProps>;
|
|
30
|
-
children?: React.ReactNode;
|
|
31
|
-
} & StackPadding &
|
|
32
|
-
StyleProps;
|
|
33
|
-
|
|
34
|
-
export type RowProps = Omit<StackProps, "direction">;
|
package/src/surface/surface.ts
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import type { Sx } from "@lattice-ui/style";
|
|
2
|
-
|
|
3
|
-
type GuiPropRecord = Record<string, unknown>;
|
|
4
|
-
|
|
5
|
-
export type SurfaceToken = "surface" | "elevated" | "sunken" | "overlay";
|
|
6
|
-
|
|
7
|
-
function asSxProps<Props extends GuiPropRecord>(value: GuiPropRecord): Partial<Props> {
|
|
8
|
-
return value as unknown as Partial<Props>;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Props-only surface helper.
|
|
13
|
-
* Use this when you only want host props (including host border props).
|
|
14
|
-
* It does not create child instances like UICorner/UIStroke/shadow nodes.
|
|
15
|
-
*/
|
|
16
|
-
export function surface<Props extends GuiPropRecord>(token: SurfaceToken): Sx<Props> {
|
|
17
|
-
return (theme) => {
|
|
18
|
-
switch (token) {
|
|
19
|
-
case "surface":
|
|
20
|
-
return asSxProps<Props>({
|
|
21
|
-
BackgroundColor3: theme.colors.surface,
|
|
22
|
-
BorderColor3: theme.colors.border,
|
|
23
|
-
BorderSizePixel: 1,
|
|
24
|
-
});
|
|
25
|
-
case "elevated":
|
|
26
|
-
return asSxProps<Props>({
|
|
27
|
-
BackgroundColor3: theme.colors.surfaceElevated,
|
|
28
|
-
BorderColor3: theme.colors.border,
|
|
29
|
-
BorderSizePixel: 1,
|
|
30
|
-
});
|
|
31
|
-
case "sunken":
|
|
32
|
-
return asSxProps<Props>({
|
|
33
|
-
BackgroundColor3: theme.colors.background,
|
|
34
|
-
BorderColor3: theme.colors.border,
|
|
35
|
-
BorderSizePixel: 1,
|
|
36
|
-
});
|
|
37
|
-
case "overlay":
|
|
38
|
-
return asSxProps<Props>({
|
|
39
|
-
BackgroundColor3: theme.colors.overlay,
|
|
40
|
-
BackgroundTransparency: 0.35,
|
|
41
|
-
BorderSizePixel: 0,
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
};
|
|
45
|
-
}
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import { React } from "@lattice-ui/core";
|
|
2
|
-
import type { Sx } from "@lattice-ui/style";
|
|
3
|
-
import { mergeGuiProps, resolveSx, useTheme } from "@lattice-ui/style";
|
|
4
|
-
import type { SurfaceToken } from "./surface";
|
|
5
|
-
import { surface } from "./surface";
|
|
6
|
-
|
|
7
|
-
type StyleProps = React.Attributes & Record<string, unknown>;
|
|
8
|
-
|
|
9
|
-
export type SurfaceProps = {
|
|
10
|
-
tone?: SurfaceToken;
|
|
11
|
-
sx?: Sx<StyleProps>;
|
|
12
|
-
children?: React.ReactNode;
|
|
13
|
-
} & StyleProps;
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Decorated surface host primitive.
|
|
17
|
-
* Unlike `surface()`, this renders instance-graph decoration via UICorner/UIStroke.
|
|
18
|
-
* Host border props are not the canonical border representation here.
|
|
19
|
-
* `asChild` is intentionally not supported in this milestone.
|
|
20
|
-
*/
|
|
21
|
-
export function Surface(props: SurfaceProps) {
|
|
22
|
-
const tone = props.tone ?? "surface";
|
|
23
|
-
const sx = props.sx;
|
|
24
|
-
const children = props.children;
|
|
25
|
-
|
|
26
|
-
const asChild = (props as { asChild?: unknown }).asChild;
|
|
27
|
-
if (asChild !== undefined) {
|
|
28
|
-
error("[Surface] `asChild` is not supported in M2.");
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const restProps: StyleProps = {};
|
|
32
|
-
for (const [rawKey, value] of pairs(props as Record<string, unknown>)) {
|
|
33
|
-
if (!typeIs(rawKey, "string")) {
|
|
34
|
-
continue;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
if (rawKey === "tone" || rawKey === "sx" || rawKey === "children" || rawKey === "asChild") {
|
|
38
|
-
continue;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
restProps[rawKey] = value;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const { theme } = useTheme();
|
|
45
|
-
const toneProps = resolveSx(surface<StyleProps>(tone), theme);
|
|
46
|
-
const sxProps = resolveSx(sx, theme);
|
|
47
|
-
const baseProps =
|
|
48
|
-
tone === "overlay" ? toneProps : mergeGuiProps(toneProps, { BorderSizePixel: 0 } as Partial<StyleProps>);
|
|
49
|
-
const mergedProps = mergeGuiProps(baseProps, sxProps, restProps);
|
|
50
|
-
const decorated = tone !== "overlay";
|
|
51
|
-
|
|
52
|
-
return (
|
|
53
|
-
<frame {...(mergedProps as Record<string, unknown>)}>
|
|
54
|
-
{decorated ? <uicorner CornerRadius={new UDim(0, theme.radius.lg)} /> : undefined}
|
|
55
|
-
{decorated ? <uistroke Color={theme.colors.border} Thickness={1} /> : undefined}
|
|
56
|
-
{children}
|
|
57
|
-
</frame>
|
|
58
|
-
);
|
|
59
|
-
}
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import { React, useControllableState } from "@lattice-ui/core";
|
|
2
|
-
import { defaultLightTheme } from "@lattice-ui/style";
|
|
3
|
-
import { DensityProvider } from "../density/DensityProvider";
|
|
4
|
-
import { SystemBaseThemeContextProvider } from "./baseThemeContext";
|
|
5
|
-
import { useSystemThemeContext } from "./systemThemeContext";
|
|
6
|
-
import type { SystemProviderProps, SystemThemeContextValue } from "./types";
|
|
7
|
-
|
|
8
|
-
export function SystemProvider(props: SystemProviderProps) {
|
|
9
|
-
// SystemProvider owns raw/base theme state. Density is applied in DensityProvider.
|
|
10
|
-
const [baseThemeValue, setBaseThemeValue] = useControllableState({
|
|
11
|
-
value: props.theme,
|
|
12
|
-
defaultValue: props.defaultTheme ?? defaultLightTheme,
|
|
13
|
-
onChange: props.onThemeChange,
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
const setBaseTheme = React.useCallback<SystemThemeContextValue["setBaseTheme"]>(
|
|
17
|
-
(nextTheme) => {
|
|
18
|
-
// Write-path contract: updates should target baseTheme, not resolved theme.
|
|
19
|
-
setBaseThemeValue(nextTheme);
|
|
20
|
-
},
|
|
21
|
-
[setBaseThemeValue],
|
|
22
|
-
);
|
|
23
|
-
|
|
24
|
-
const baseThemeContextValue = React.useMemo(
|
|
25
|
-
() => ({
|
|
26
|
-
baseTheme: baseThemeValue,
|
|
27
|
-
setBaseTheme,
|
|
28
|
-
}),
|
|
29
|
-
[baseThemeValue, setBaseTheme],
|
|
30
|
-
);
|
|
31
|
-
|
|
32
|
-
return (
|
|
33
|
-
<SystemBaseThemeContextProvider value={baseThemeContextValue}>
|
|
34
|
-
<DensityProvider
|
|
35
|
-
defaultDensity={props.defaultDensity}
|
|
36
|
-
density={props.density}
|
|
37
|
-
onDensityChange={props.onDensityChange}
|
|
38
|
-
>
|
|
39
|
-
{props.children}
|
|
40
|
-
</DensityProvider>
|
|
41
|
-
</SystemBaseThemeContextProvider>
|
|
42
|
-
);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export function useSystemTheme() {
|
|
46
|
-
return useSystemThemeContext();
|
|
47
|
-
}
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import { createStrictContext } from "@lattice-ui/core";
|
|
2
|
-
import type { SystemBaseThemeContextValue } from "./types";
|
|
3
|
-
|
|
4
|
-
export const [SystemBaseThemeContextProvider, useSystemBaseThemeContext] =
|
|
5
|
-
createStrictContext<SystemBaseThemeContextValue>("SystemBaseThemeContext");
|
package/src/system/types.ts
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import type { Theme } from "@lattice-ui/style";
|
|
2
|
-
import type React from "@rbxts/react";
|
|
3
|
-
import type { DensityToken } from "../density/types";
|
|
4
|
-
|
|
5
|
-
export type SystemProviderProps = {
|
|
6
|
-
theme?: Theme;
|
|
7
|
-
defaultTheme?: Theme;
|
|
8
|
-
onThemeChange?: (nextTheme: Theme) => void;
|
|
9
|
-
density?: DensityToken;
|
|
10
|
-
defaultDensity?: DensityToken;
|
|
11
|
-
onDensityChange?: (next: DensityToken) => void;
|
|
12
|
-
children?: React.ReactNode;
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
export type SystemThemeContextValue = {
|
|
16
|
-
/** Density-resolved theme for read usage in system-managed trees. */
|
|
17
|
-
theme: Theme;
|
|
18
|
-
/** Raw theme before density transforms. Writes must target this base theme. */
|
|
19
|
-
baseTheme: Theme;
|
|
20
|
-
density: DensityToken;
|
|
21
|
-
/** Use this to update the raw/base theme source. */
|
|
22
|
-
setBaseTheme: (next: Theme) => void;
|
|
23
|
-
setDensity: (next: DensityToken) => void;
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
export type SystemBaseThemeContextValue = {
|
|
27
|
-
baseTheme: Theme;
|
|
28
|
-
setBaseTheme: (nextTheme: Theme) => void;
|
|
29
|
-
};
|
package/tsconfig.json
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"extends": "../../tsconfig.base.json",
|
|
3
|
-
"compilerOptions": {
|
|
4
|
-
"rootDir": "src",
|
|
5
|
-
"outDir": "out",
|
|
6
|
-
"declaration": true,
|
|
7
|
-
"typeRoots": [
|
|
8
|
-
"./node_modules/@rbxts",
|
|
9
|
-
"../../node_modules/@rbxts",
|
|
10
|
-
"./node_modules/@lattice-ui",
|
|
11
|
-
"../../node_modules/@lattice-ui"
|
|
12
|
-
],
|
|
13
|
-
"types": ["types", "compiler-types"]
|
|
14
|
-
},
|
|
15
|
-
"include": ["src"]
|
|
16
|
-
}
|
package/tsconfig.typecheck.json
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
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
|
-
}
|