@lattice-ui/text-field 0.3.0

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/README.md ADDED
@@ -0,0 +1,18 @@
1
+ # @lattice-ui/text-field
2
+
3
+ Headless text input primitives for Roblox UI with controlled/uncontrolled state and commit events.
4
+
5
+ ## Exports
6
+
7
+ - `TextField`
8
+ - `TextField.Root`
9
+ - `TextField.Input`
10
+ - `TextField.Label`
11
+ - `TextField.Description`
12
+ - `TextField.Message`
13
+
14
+ ## Notes
15
+
16
+ - `onValueChange` runs when input text changes.
17
+ - `onValueCommit` runs on `FocusLost`/enter-style commit moments.
18
+ - `disabled` and `readOnly` are enforced at the root context and reflected by `TextFieldInput`.
@@ -0,0 +1,3 @@
1
+ import { React } from "@lattice-ui/core";
2
+ import type { TextFieldDescriptionProps } from "./types";
3
+ export declare function TextFieldDescription(props: TextFieldDescriptionProps): React.JSX.Element;
@@ -0,0 +1,31 @@
1
+ -- Compiled with roblox-ts v3.0.0
2
+ local TS = _G[script]
3
+ local _core = TS.import(script, TS.getModule(script, "@lattice-ui", "core").out)
4
+ local React = _core.React
5
+ local Slot = _core.Slot
6
+ local useTextFieldContext = TS.import(script, script.Parent, "context").useTextFieldContext
7
+ local function TextFieldDescription(props)
8
+ local textFieldContext = useTextFieldContext()
9
+ if props.asChild then
10
+ local child = props.children
11
+ if not child then
12
+ error("[TextFieldDescription] `asChild` requires a child element.")
13
+ end
14
+ return React.createElement(Slot, {
15
+ Name = "TextFieldDescription",
16
+ Text = "Description",
17
+ }, child)
18
+ end
19
+ return React.createElement("textlabel", {
20
+ BackgroundTransparency = 1,
21
+ BorderSizePixel = 0,
22
+ Size = UDim2.fromOffset(300, 20),
23
+ Text = "Description",
24
+ TextColor3 = if textFieldContext.disabled then Color3.fromRGB(132, 139, 154) else Color3.fromRGB(170, 179, 195),
25
+ TextSize = 13,
26
+ TextXAlignment = Enum.TextXAlignment.Left,
27
+ })
28
+ end
29
+ return {
30
+ TextFieldDescription = TextFieldDescription,
31
+ }
@@ -0,0 +1,3 @@
1
+ import { React } from "@lattice-ui/core";
2
+ import type { TextFieldInputProps } from "./types";
3
+ export declare function TextFieldInput(props: TextFieldInputProps): React.JSX.Element;
@@ -0,0 +1,74 @@
1
+ -- Compiled with roblox-ts v3.0.0
2
+ local TS = _G[script]
3
+ local _core = TS.import(script, TS.getModule(script, "@lattice-ui", "core").out)
4
+ local React = _core.React
5
+ local Slot = _core.Slot
6
+ local useTextFieldContext = TS.import(script, script.Parent, "context").useTextFieldContext
7
+ local function toTextBox(instance)
8
+ if not instance or not instance:IsA("TextBox") then
9
+ return nil
10
+ end
11
+ return instance
12
+ end
13
+ local function TextFieldInput(props)
14
+ local textFieldContext = useTextFieldContext()
15
+ local disabled = textFieldContext.disabled or props.disabled == true
16
+ local readOnly = textFieldContext.readOnly or props.readOnly == true
17
+ local setInputRef = React.useCallback(function(instance)
18
+ textFieldContext.inputRef.current = toTextBox(instance)
19
+ end, { textFieldContext.inputRef })
20
+ local handleTextChanged = React.useCallback(function(textBox)
21
+ if disabled or readOnly then
22
+ if textBox.Text ~= textFieldContext.value then
23
+ textBox.Text = textFieldContext.value
24
+ end
25
+ return nil
26
+ end
27
+ textFieldContext.setValue(textBox.Text)
28
+ end, { disabled, readOnly, textFieldContext })
29
+ local handleFocusLost = React.useCallback(function(textBox)
30
+ if disabled then
31
+ return nil
32
+ end
33
+ textFieldContext.commitValue(textBox.Text)
34
+ end, { disabled, textFieldContext })
35
+ local sharedProps = {
36
+ Active = not disabled,
37
+ ClearTextOnFocus = false,
38
+ Selectable = not disabled,
39
+ Text = textFieldContext.value,
40
+ TextEditable = not disabled and not readOnly,
41
+ Change = {
42
+ Text = handleTextChanged,
43
+ },
44
+ Event = {
45
+ FocusLost = handleFocusLost,
46
+ },
47
+ ref = setInputRef,
48
+ }
49
+ if props.asChild then
50
+ local child = props.children
51
+ if not child then
52
+ error("[TextFieldInput] `asChild` requires a child element.")
53
+ end
54
+ local _attributes = table.clone(sharedProps)
55
+ setmetatable(_attributes, nil)
56
+ return React.createElement(Slot, _attributes, child)
57
+ end
58
+ local _attributes = table.clone(sharedProps)
59
+ setmetatable(_attributes, nil)
60
+ _attributes.BackgroundColor3 = Color3.fromRGB(39, 46, 61)
61
+ _attributes.BorderSizePixel = 0
62
+ _attributes.PlaceholderText = "Type..."
63
+ _attributes.Size = UDim2.fromOffset(240, 36)
64
+ _attributes.TextColor3 = if disabled then Color3.fromRGB(137, 145, 162) else Color3.fromRGB(235, 240, 248)
65
+ _attributes.TextSize = 15
66
+ _attributes.TextXAlignment = Enum.TextXAlignment.Left
67
+ return React.createElement("textbox", _attributes, React.createElement("uipadding", {
68
+ PaddingLeft = UDim.new(0, 10),
69
+ PaddingRight = UDim.new(0, 10),
70
+ }))
71
+ end
72
+ return {
73
+ TextFieldInput = TextFieldInput,
74
+ }
@@ -0,0 +1,3 @@
1
+ import { React } from "@lattice-ui/core";
2
+ import type { TextFieldLabelProps } from "./types";
3
+ export declare function TextFieldLabel(props: TextFieldLabelProps): React.JSX.Element;
@@ -0,0 +1,49 @@
1
+ -- Compiled with roblox-ts v3.0.0
2
+ local TS = _G[script]
3
+ local _core = TS.import(script, TS.getModule(script, "@lattice-ui", "core").out)
4
+ local React = _core.React
5
+ local Slot = _core.Slot
6
+ local useTextFieldContext = TS.import(script, script.Parent, "context").useTextFieldContext
7
+ local function TextFieldLabel(props)
8
+ local textFieldContext = useTextFieldContext()
9
+ local disabled = textFieldContext.disabled
10
+ local handleActivated = React.useCallback(function()
11
+ if disabled then
12
+ return nil
13
+ end
14
+ local _result = textFieldContext.inputRef.current
15
+ if _result ~= nil then
16
+ _result:CaptureFocus()
17
+ end
18
+ end, { disabled, textFieldContext.inputRef })
19
+ local sharedProps = {
20
+ Active = not disabled,
21
+ Selectable = not disabled,
22
+ Text = "Label",
23
+ Event = {
24
+ Activated = handleActivated,
25
+ },
26
+ }
27
+ if props.asChild then
28
+ local child = props.children
29
+ if not child then
30
+ error("[TextFieldLabel] `asChild` requires a child element.")
31
+ end
32
+ local _attributes = table.clone(sharedProps)
33
+ setmetatable(_attributes, nil)
34
+ return React.createElement(Slot, _attributes, child)
35
+ end
36
+ local _attributes = table.clone(sharedProps)
37
+ setmetatable(_attributes, nil)
38
+ _attributes.AutoButtonColor = false
39
+ _attributes.BackgroundTransparency = 1
40
+ _attributes.BorderSizePixel = 0
41
+ _attributes.Size = UDim2.fromOffset(240, 22)
42
+ _attributes.TextColor3 = if disabled then Color3.fromRGB(149, 157, 173) else Color3.fromRGB(225, 231, 241)
43
+ _attributes.TextSize = 14
44
+ _attributes.TextXAlignment = Enum.TextXAlignment.Left
45
+ return React.createElement("textbutton", _attributes)
46
+ end
47
+ return {
48
+ TextFieldLabel = TextFieldLabel,
49
+ }
@@ -0,0 +1,3 @@
1
+ import { React } from "@lattice-ui/core";
2
+ import type { TextFieldMessageProps } from "./types";
3
+ export declare function TextFieldMessage(props: TextFieldMessageProps): React.JSX.Element;
@@ -0,0 +1,31 @@
1
+ -- Compiled with roblox-ts v3.0.0
2
+ local TS = _G[script]
3
+ local _core = TS.import(script, TS.getModule(script, "@lattice-ui", "core").out)
4
+ local React = _core.React
5
+ local Slot = _core.Slot
6
+ local useTextFieldContext = TS.import(script, script.Parent, "context").useTextFieldContext
7
+ local function TextFieldMessage(props)
8
+ local textFieldContext = useTextFieldContext()
9
+ if props.asChild then
10
+ local child = props.children
11
+ if not child then
12
+ error("[TextFieldMessage] `asChild` requires a child element.")
13
+ end
14
+ return React.createElement(Slot, {
15
+ Name = "TextFieldMessage",
16
+ Text = "Message",
17
+ }, child)
18
+ end
19
+ return React.createElement("textlabel", {
20
+ BackgroundTransparency = 1,
21
+ BorderSizePixel = 0,
22
+ Size = UDim2.fromOffset(300, 20),
23
+ Text = "Message",
24
+ TextColor3 = if textFieldContext.invalid == true then Color3.fromRGB(255, 128, 128) elseif textFieldContext.disabled then Color3.fromRGB(132, 139, 154) else Color3.fromRGB(170, 179, 195),
25
+ TextSize = 13,
26
+ TextXAlignment = Enum.TextXAlignment.Left,
27
+ })
28
+ end
29
+ return {
30
+ TextFieldMessage = TextFieldMessage,
31
+ }
@@ -0,0 +1,3 @@
1
+ import { React } from "@lattice-ui/core";
2
+ import type { TextFieldProps } from "./types";
3
+ export declare function TextFieldRoot(props: TextFieldProps): React.JSX.Element;
@@ -0,0 +1,57 @@
1
+ -- Compiled with roblox-ts v3.0.0
2
+ local TS = _G[script]
3
+ local _core = TS.import(script, TS.getModule(script, "@lattice-ui", "core").out)
4
+ local React = _core.React
5
+ local useControllableState = _core.useControllableState
6
+ local TextFieldContextProvider = TS.import(script, script.Parent, "context").TextFieldContextProvider
7
+ local function TextFieldRoot(props)
8
+ local _object = {
9
+ value = props.value,
10
+ }
11
+ local _left = "defaultValue"
12
+ local _condition = props.defaultValue
13
+ if _condition == nil then
14
+ _condition = ""
15
+ end
16
+ _object[_left] = _condition
17
+ _object.onChange = props.onValueChange
18
+ local _binding = useControllableState(_object)
19
+ local value = _binding[1]
20
+ local setValueState = _binding[2]
21
+ local disabled = props.disabled == true
22
+ local readOnly = props.readOnly == true
23
+ local required = props.required == true
24
+ local invalid = props.invalid == true
25
+ local inputRef = React.useRef()
26
+ local setValue = React.useCallback(function(nextValue)
27
+ if disabled or readOnly then
28
+ return nil
29
+ end
30
+ setValueState(nextValue)
31
+ end, { disabled, readOnly, setValueState })
32
+ local commitValue = React.useCallback(function(nextValue)
33
+ local _result = props.onValueCommit
34
+ if _result ~= nil then
35
+ _result(nextValue)
36
+ end
37
+ end, { props.onValueCommit })
38
+ local contextValue = React.useMemo(function()
39
+ return {
40
+ value = value,
41
+ setValue = setValue,
42
+ commitValue = commitValue,
43
+ disabled = disabled,
44
+ readOnly = readOnly,
45
+ required = required,
46
+ invalid = invalid,
47
+ name = props.name,
48
+ inputRef = inputRef,
49
+ }
50
+ end, { commitValue, disabled, invalid, props.name, readOnly, required, setValue, value })
51
+ return React.createElement(TextFieldContextProvider, {
52
+ value = contextValue,
53
+ }, props.children)
54
+ end
55
+ return {
56
+ TextFieldRoot = TextFieldRoot,
57
+ }
@@ -0,0 +1,3 @@
1
+ import type { TextFieldContextValue } from "./types";
2
+ declare const TextFieldContextProvider: import("@rbxts/react").Provider<TextFieldContextValue | undefined>, useTextFieldContext: () => TextFieldContextValue;
3
+ export { TextFieldContextProvider, useTextFieldContext };
@@ -0,0 +1,10 @@
1
+ -- Compiled with roblox-ts v3.0.0
2
+ local TS = _G[script]
3
+ local createStrictContext = TS.import(script, TS.getModule(script, "@lattice-ui", "core").out).createStrictContext
4
+ local _binding = createStrictContext("TextField")
5
+ local TextFieldContextProvider = _binding[1]
6
+ local useTextFieldContext = _binding[2]
7
+ return {
8
+ TextFieldContextProvider = TextFieldContextProvider,
9
+ useTextFieldContext = useTextFieldContext,
10
+ }
@@ -0,0 +1,44 @@
1
+ import type React from "@rbxts/react";
2
+ export type TextFieldSetValue = (value: string) => void;
3
+ export type TextFieldCommitValue = (value: string) => void;
4
+ export type TextFieldContextValue = {
5
+ value: string;
6
+ setValue: TextFieldSetValue;
7
+ commitValue: TextFieldCommitValue;
8
+ disabled: boolean;
9
+ readOnly: boolean;
10
+ required: boolean;
11
+ invalid: boolean;
12
+ name?: string;
13
+ inputRef: React.MutableRefObject<TextBox | undefined>;
14
+ };
15
+ export type TextFieldProps = {
16
+ value?: string;
17
+ defaultValue?: string;
18
+ onValueChange?: (value: string) => void;
19
+ onValueCommit?: (value: string) => void;
20
+ disabled?: boolean;
21
+ readOnly?: boolean;
22
+ required?: boolean;
23
+ invalid?: boolean;
24
+ name?: string;
25
+ children?: React.ReactNode;
26
+ };
27
+ export type TextFieldInputProps = {
28
+ asChild?: boolean;
29
+ disabled?: boolean;
30
+ readOnly?: boolean;
31
+ children?: React.ReactElement;
32
+ };
33
+ export type TextFieldLabelProps = {
34
+ asChild?: boolean;
35
+ children?: React.ReactElement;
36
+ };
37
+ export type TextFieldDescriptionProps = {
38
+ asChild?: boolean;
39
+ children?: React.ReactElement;
40
+ };
41
+ export type TextFieldMessageProps = {
42
+ asChild?: boolean;
43
+ children?: React.ReactElement;
44
+ };
@@ -0,0 +1,2 @@
1
+ -- Compiled with roblox-ts v3.0.0
2
+ return nil
package/out/index.d.ts ADDED
@@ -0,0 +1,13 @@
1
+ import { TextFieldDescription } from "./TextField/TextFieldDescription";
2
+ import { TextFieldInput } from "./TextField/TextFieldInput";
3
+ import { TextFieldLabel } from "./TextField/TextFieldLabel";
4
+ import { TextFieldMessage } from "./TextField/TextFieldMessage";
5
+ import { TextFieldRoot } from "./TextField/TextFieldRoot";
6
+ export declare const TextField: {
7
+ readonly Root: typeof TextFieldRoot;
8
+ readonly Input: typeof TextFieldInput;
9
+ readonly Label: typeof TextFieldLabel;
10
+ readonly Description: typeof TextFieldDescription;
11
+ readonly Message: typeof TextFieldMessage;
12
+ };
13
+ export type { TextFieldCommitValue, TextFieldContextValue, TextFieldDescriptionProps, TextFieldInputProps, TextFieldLabelProps, TextFieldMessageProps, TextFieldProps, TextFieldSetValue, } from "./TextField/types";
package/out/init.luau ADDED
@@ -0,0 +1,17 @@
1
+ -- Compiled with roblox-ts v3.0.0
2
+ local TS = _G[script]
3
+ local TextFieldDescription = TS.import(script, script, "TextField", "TextFieldDescription").TextFieldDescription
4
+ local TextFieldInput = TS.import(script, script, "TextField", "TextFieldInput").TextFieldInput
5
+ local TextFieldLabel = TS.import(script, script, "TextField", "TextFieldLabel").TextFieldLabel
6
+ local TextFieldMessage = TS.import(script, script, "TextField", "TextFieldMessage").TextFieldMessage
7
+ local TextFieldRoot = TS.import(script, script, "TextField", "TextFieldRoot").TextFieldRoot
8
+ local TextField = {
9
+ Root = TextFieldRoot,
10
+ Input = TextFieldInput,
11
+ Label = TextFieldLabel,
12
+ Description = TextFieldDescription,
13
+ Message = TextFieldMessage,
14
+ }
15
+ return {
16
+ TextField = TextField,
17
+ }
package/package.json ADDED
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "@lattice-ui/text-field",
3
+ "version": "0.3.0",
4
+ "private": false,
5
+ "main": "out/init.luau",
6
+ "types": "out/index.d.ts",
7
+ "dependencies": {
8
+ "@lattice-ui/core": "0.3.0"
9
+ },
10
+ "devDependencies": {
11
+ "@rbxts/react": "17.3.7-ts.1",
12
+ "@rbxts/react-roblox": "17.3.7-ts.1"
13
+ },
14
+ "peerDependencies": {
15
+ "@rbxts/react": "^17",
16
+ "@rbxts/react-roblox": "^17"
17
+ },
18
+ "scripts": {
19
+ "build": "rbxtsc -p tsconfig.json",
20
+ "typecheck": "tsc -p tsconfig.typecheck.json",
21
+ "watch": "rbxtsc -p tsconfig.json -w"
22
+ }
23
+ }
@@ -0,0 +1,32 @@
1
+ import { React, Slot } from "@lattice-ui/core";
2
+ import { useTextFieldContext } from "./context";
3
+ import type { TextFieldDescriptionProps } from "./types";
4
+
5
+ export function TextFieldDescription(props: TextFieldDescriptionProps) {
6
+ const textFieldContext = useTextFieldContext();
7
+
8
+ if (props.asChild) {
9
+ const child = props.children;
10
+ if (!child) {
11
+ error("[TextFieldDescription] `asChild` requires a child element.");
12
+ }
13
+
14
+ return (
15
+ <Slot Name="TextFieldDescription" Text="Description">
16
+ {child}
17
+ </Slot>
18
+ );
19
+ }
20
+
21
+ return (
22
+ <textlabel
23
+ BackgroundTransparency={1}
24
+ BorderSizePixel={0}
25
+ Size={UDim2.fromOffset(300, 20)}
26
+ Text="Description"
27
+ TextColor3={textFieldContext.disabled ? Color3.fromRGB(132, 139, 154) : Color3.fromRGB(170, 179, 195)}
28
+ TextSize={13}
29
+ TextXAlignment={Enum.TextXAlignment.Left}
30
+ />
31
+ );
32
+ }
@@ -0,0 +1,89 @@
1
+ import { React, Slot } from "@lattice-ui/core";
2
+ import { useTextFieldContext } from "./context";
3
+ import type { TextFieldInputProps } from "./types";
4
+
5
+ function toTextBox(instance: Instance | undefined) {
6
+ if (!instance || !instance.IsA("TextBox")) {
7
+ return undefined;
8
+ }
9
+
10
+ return instance;
11
+ }
12
+
13
+ export function TextFieldInput(props: TextFieldInputProps) {
14
+ const textFieldContext = useTextFieldContext();
15
+ const disabled = textFieldContext.disabled || props.disabled === true;
16
+ const readOnly = textFieldContext.readOnly || props.readOnly === true;
17
+
18
+ const setInputRef = React.useCallback(
19
+ (instance: Instance | undefined) => {
20
+ textFieldContext.inputRef.current = toTextBox(instance);
21
+ },
22
+ [textFieldContext.inputRef],
23
+ );
24
+
25
+ const handleTextChanged = React.useCallback(
26
+ (textBox: TextBox) => {
27
+ if (disabled || readOnly) {
28
+ if (textBox.Text !== textFieldContext.value) {
29
+ textBox.Text = textFieldContext.value;
30
+ }
31
+
32
+ return;
33
+ }
34
+
35
+ textFieldContext.setValue(textBox.Text);
36
+ },
37
+ [disabled, readOnly, textFieldContext],
38
+ );
39
+
40
+ const handleFocusLost = React.useCallback(
41
+ (textBox: TextBox) => {
42
+ if (disabled) {
43
+ return;
44
+ }
45
+
46
+ textFieldContext.commitValue(textBox.Text);
47
+ },
48
+ [disabled, textFieldContext],
49
+ );
50
+
51
+ const sharedProps = {
52
+ Active: !disabled,
53
+ ClearTextOnFocus: false,
54
+ Selectable: !disabled,
55
+ Text: textFieldContext.value,
56
+ TextEditable: !disabled && !readOnly,
57
+ Change: {
58
+ Text: handleTextChanged,
59
+ },
60
+ Event: {
61
+ FocusLost: handleFocusLost,
62
+ },
63
+ ref: setInputRef,
64
+ };
65
+
66
+ if (props.asChild) {
67
+ const child = props.children;
68
+ if (!child) {
69
+ error("[TextFieldInput] `asChild` requires a child element.");
70
+ }
71
+
72
+ return <Slot {...sharedProps}>{child}</Slot>;
73
+ }
74
+
75
+ return (
76
+ <textbox
77
+ {...sharedProps}
78
+ BackgroundColor3={Color3.fromRGB(39, 46, 61)}
79
+ BorderSizePixel={0}
80
+ PlaceholderText="Type..."
81
+ Size={UDim2.fromOffset(240, 36)}
82
+ TextColor3={disabled ? Color3.fromRGB(137, 145, 162) : Color3.fromRGB(235, 240, 248)}
83
+ TextSize={15}
84
+ TextXAlignment={Enum.TextXAlignment.Left}
85
+ >
86
+ <uipadding PaddingLeft={new UDim(0, 10)} PaddingRight={new UDim(0, 10)} />
87
+ </textbox>
88
+ );
89
+ }
@@ -0,0 +1,47 @@
1
+ import { React, Slot } from "@lattice-ui/core";
2
+ import { useTextFieldContext } from "./context";
3
+ import type { TextFieldLabelProps } from "./types";
4
+
5
+ export function TextFieldLabel(props: TextFieldLabelProps) {
6
+ const textFieldContext = useTextFieldContext();
7
+ const disabled = textFieldContext.disabled;
8
+
9
+ const handleActivated = React.useCallback(() => {
10
+ if (disabled) {
11
+ return;
12
+ }
13
+
14
+ textFieldContext.inputRef.current?.CaptureFocus();
15
+ }, [disabled, textFieldContext.inputRef]);
16
+
17
+ const sharedProps = {
18
+ Active: !disabled,
19
+ Selectable: !disabled,
20
+ Text: "Label",
21
+ Event: {
22
+ Activated: handleActivated,
23
+ },
24
+ };
25
+
26
+ if (props.asChild) {
27
+ const child = props.children;
28
+ if (!child) {
29
+ error("[TextFieldLabel] `asChild` requires a child element.");
30
+ }
31
+
32
+ return <Slot {...sharedProps}>{child}</Slot>;
33
+ }
34
+
35
+ return (
36
+ <textbutton
37
+ {...sharedProps}
38
+ AutoButtonColor={false}
39
+ BackgroundTransparency={1}
40
+ BorderSizePixel={0}
41
+ Size={UDim2.fromOffset(240, 22)}
42
+ TextColor3={disabled ? Color3.fromRGB(149, 157, 173) : Color3.fromRGB(225, 231, 241)}
43
+ TextSize={14}
44
+ TextXAlignment={Enum.TextXAlignment.Left}
45
+ />
46
+ );
47
+ }
@@ -0,0 +1,38 @@
1
+ import { React, Slot } from "@lattice-ui/core";
2
+ import { useTextFieldContext } from "./context";
3
+ import type { TextFieldMessageProps } from "./types";
4
+
5
+ export function TextFieldMessage(props: TextFieldMessageProps) {
6
+ const textFieldContext = useTextFieldContext();
7
+
8
+ if (props.asChild) {
9
+ const child = props.children;
10
+ if (!child) {
11
+ error("[TextFieldMessage] `asChild` requires a child element.");
12
+ }
13
+
14
+ return (
15
+ <Slot Name="TextFieldMessage" Text="Message">
16
+ {child}
17
+ </Slot>
18
+ );
19
+ }
20
+
21
+ return (
22
+ <textlabel
23
+ BackgroundTransparency={1}
24
+ BorderSizePixel={0}
25
+ Size={UDim2.fromOffset(300, 20)}
26
+ Text="Message"
27
+ TextColor3={
28
+ textFieldContext.invalid === true
29
+ ? Color3.fromRGB(255, 128, 128)
30
+ : textFieldContext.disabled
31
+ ? Color3.fromRGB(132, 139, 154)
32
+ : Color3.fromRGB(170, 179, 195)
33
+ }
34
+ TextSize={13}
35
+ TextXAlignment={Enum.TextXAlignment.Left}
36
+ />
37
+ );
38
+ }
@@ -0,0 +1,53 @@
1
+ import { React, useControllableState } from "@lattice-ui/core";
2
+ import { TextFieldContextProvider } from "./context";
3
+ import type { TextFieldProps } from "./types";
4
+
5
+ export function TextFieldRoot(props: TextFieldProps) {
6
+ const [value, setValueState] = useControllableState<string>({
7
+ value: props.value,
8
+ defaultValue: props.defaultValue ?? "",
9
+ onChange: props.onValueChange,
10
+ });
11
+
12
+ const disabled = props.disabled === true;
13
+ const readOnly = props.readOnly === true;
14
+ const required = props.required === true;
15
+ const invalid = props.invalid === true;
16
+
17
+ const inputRef = React.useRef<TextBox>();
18
+
19
+ const setValue = React.useCallback(
20
+ (nextValue: string) => {
21
+ if (disabled || readOnly) {
22
+ return;
23
+ }
24
+
25
+ setValueState(nextValue);
26
+ },
27
+ [disabled, readOnly, setValueState],
28
+ );
29
+
30
+ const commitValue = React.useCallback(
31
+ (nextValue: string) => {
32
+ props.onValueCommit?.(nextValue);
33
+ },
34
+ [props.onValueCommit],
35
+ );
36
+
37
+ const contextValue = React.useMemo(
38
+ () => ({
39
+ value,
40
+ setValue,
41
+ commitValue,
42
+ disabled,
43
+ readOnly,
44
+ required,
45
+ invalid,
46
+ name: props.name,
47
+ inputRef,
48
+ }),
49
+ [commitValue, disabled, invalid, props.name, readOnly, required, setValue, value],
50
+ );
51
+
52
+ return <TextFieldContextProvider value={contextValue}>{props.children}</TextFieldContextProvider>;
53
+ }
@@ -0,0 +1,6 @@
1
+ import { createStrictContext } from "@lattice-ui/core";
2
+ import type { TextFieldContextValue } from "./types";
3
+
4
+ const [TextFieldContextProvider, useTextFieldContext] = createStrictContext<TextFieldContextValue>("TextField");
5
+
6
+ export { TextFieldContextProvider, useTextFieldContext };
@@ -0,0 +1,51 @@
1
+ import type React from "@rbxts/react";
2
+
3
+ export type TextFieldSetValue = (value: string) => void;
4
+ export type TextFieldCommitValue = (value: string) => void;
5
+
6
+ export type TextFieldContextValue = {
7
+ value: string;
8
+ setValue: TextFieldSetValue;
9
+ commitValue: TextFieldCommitValue;
10
+ disabled: boolean;
11
+ readOnly: boolean;
12
+ required: boolean;
13
+ invalid: boolean;
14
+ name?: string;
15
+ inputRef: React.MutableRefObject<TextBox | undefined>;
16
+ };
17
+
18
+ export type TextFieldProps = {
19
+ value?: string;
20
+ defaultValue?: string;
21
+ onValueChange?: (value: string) => void;
22
+ onValueCommit?: (value: string) => void;
23
+ disabled?: boolean;
24
+ readOnly?: boolean;
25
+ required?: boolean;
26
+ invalid?: boolean;
27
+ name?: string;
28
+ children?: React.ReactNode;
29
+ };
30
+
31
+ export type TextFieldInputProps = {
32
+ asChild?: boolean;
33
+ disabled?: boolean;
34
+ readOnly?: boolean;
35
+ children?: React.ReactElement;
36
+ };
37
+
38
+ export type TextFieldLabelProps = {
39
+ asChild?: boolean;
40
+ children?: React.ReactElement;
41
+ };
42
+
43
+ export type TextFieldDescriptionProps = {
44
+ asChild?: boolean;
45
+ children?: React.ReactElement;
46
+ };
47
+
48
+ export type TextFieldMessageProps = {
49
+ asChild?: boolean;
50
+ children?: React.ReactElement;
51
+ };
package/src/index.ts ADDED
@@ -0,0 +1,24 @@
1
+ import { TextFieldDescription } from "./TextField/TextFieldDescription";
2
+ import { TextFieldInput } from "./TextField/TextFieldInput";
3
+ import { TextFieldLabel } from "./TextField/TextFieldLabel";
4
+ import { TextFieldMessage } from "./TextField/TextFieldMessage";
5
+ import { TextFieldRoot } from "./TextField/TextFieldRoot";
6
+
7
+ export const TextField = {
8
+ Root: TextFieldRoot,
9
+ Input: TextFieldInput,
10
+ Label: TextFieldLabel,
11
+ Description: TextFieldDescription,
12
+ Message: TextFieldMessage,
13
+ } as const;
14
+
15
+ export type {
16
+ TextFieldCommitValue,
17
+ TextFieldContextValue,
18
+ TextFieldDescriptionProps,
19
+ TextFieldInputProps,
20
+ TextFieldLabelProps,
21
+ TextFieldMessageProps,
22
+ TextFieldProps,
23
+ TextFieldSetValue,
24
+ } from "./TextField/types";
package/tsconfig.json ADDED
@@ -0,0 +1,16 @@
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
+ }
@@ -0,0 +1,35 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "noEmit": true,
5
+ "baseUrl": "..",
6
+ "rootDir": "..",
7
+ "paths": {
8
+ "@lattice-ui/accordion": ["accordion/src/index.ts"],
9
+ "@lattice-ui/avatar": ["avatar/src/index.ts"],
10
+ "@lattice-ui/checkbox": ["checkbox/src/index.ts"],
11
+ "@lattice-ui/combobox": ["combobox/src/index.ts"],
12
+ "@lattice-ui/core": ["core/src/index.ts"],
13
+ "@lattice-ui/dialog": ["dialog/src/index.ts"],
14
+ "@lattice-ui/focus": ["focus/src/index.ts"],
15
+ "@lattice-ui/layer": ["layer/src/index.ts"],
16
+ "@lattice-ui/menu": ["menu/src/index.ts"],
17
+ "@lattice-ui/popover": ["popover/src/index.ts"],
18
+ "@lattice-ui/popper": ["popper/src/index.ts"],
19
+ "@lattice-ui/progress": ["progress/src/index.ts"],
20
+ "@lattice-ui/radio-group": ["radio-group/src/index.ts"],
21
+ "@lattice-ui/scroll-area": ["scroll-area/src/index.ts"],
22
+ "@lattice-ui/select": ["select/src/index.ts"],
23
+ "@lattice-ui/slider": ["slider/src/index.ts"],
24
+ "@lattice-ui/style": ["style/src/index.ts"],
25
+ "@lattice-ui/switch": ["switch/src/index.ts"],
26
+ "@lattice-ui/system": ["system/src/index.ts"],
27
+ "@lattice-ui/tabs": ["tabs/src/index.ts"],
28
+ "@lattice-ui/text-field": ["text-field/src/index.ts"],
29
+ "@lattice-ui/textarea": ["textarea/src/index.ts"],
30
+ "@lattice-ui/toast": ["toast/src/index.ts"],
31
+ "@lattice-ui/toggle-group": ["toggle-group/src/index.ts"],
32
+ "@lattice-ui/tooltip": ["tooltip/src/index.ts"]
33
+ }
34
+ }
35
+ }