Skip to content

Commit d3eeecb

Browse files
committed
feat: initial work into using solid-forms
1 parent 9e4d075 commit d3eeecb

File tree

13 files changed

+249
-25
lines changed

13 files changed

+249
-25
lines changed

Diff for: .vscode/settings.json

+2-4
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@
1313
"editor.insertSpaces": true,
1414
"editor.tabSize": 2,
1515
// Tell `rust-analyzer` where the Tauri project is.
16-
"rust-analyzer.linkedProjects": [
17-
"./packages/desktop/src-tauri/Cargo.toml"
18-
],
19-
"nixEnvSelector.nixFile": "${workspaceRoot}/default.nix"
16+
"rust-analyzer.linkedProjects": ["./packages/desktop/src-tauri/Cargo.toml"],
17+
"nixEnvSelector.nixFile": "${workspaceFolder}/default.nix"
2018
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { BiSolidCloud, BiSolidTrash } from "solid-icons/bi";
2+
import { For, Match, Show, Switch, createSignal, onMount } from "solid-js";
3+
4+
import type { ChannelWebhook } from "revolt.js";
5+
6+
import { useClient } from "@revolt/client";
7+
import {
8+
Avatar,
9+
CategoryButton,
10+
Column,
11+
Form2,
12+
Preloader,
13+
Text,
14+
Typography,
15+
} from "@revolt/ui";
16+
17+
import { useSettingsNavigation } from "../Settings";
18+
19+
import { ChannelSettingsProps } from ".";
20+
import { createFormControl, createFormGroup } from "solid-forms";
21+
import { useTranslation } from "@revolt/i18n";
22+
23+
/**
24+
* Overview
25+
*/
26+
export default function ChannelOverview(props: ChannelSettingsProps) {
27+
const t = useTranslation();
28+
29+
const editGroup = createFormGroup({
30+
name: createFormControl(props.channel.name),
31+
description: createFormControl(props.channel.description),
32+
});
33+
34+
return (
35+
<Column gap="xl">
36+
<form>
37+
<Column>
38+
<Text class="label">Channel Info</Text>
39+
<Form2.TextField
40+
name="name"
41+
control={editGroup.controls.name}
42+
label={t("app.settings.channel_pages.overview.name")}
43+
/>
44+
<Form2.TextField
45+
autosize
46+
min-rows={2}
47+
name="description"
48+
control={editGroup.controls.description}
49+
label={t("app.settings.channel_pages.overview.description")}
50+
placeholder="This channel is about..."
51+
/>
52+
</Column>
53+
</form>
54+
</Column>
55+
);
56+
}

Diff for: packages/client/components/app/interface/settings/channel/index.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { ColouredText } from "@revolt/ui";
1616
import { SettingsConfiguration } from "..";
1717

1818
import Webhooks, { Webhook } from "./Webhooks";
19+
import ChannelOverview from "./Overview";
1920

2021
const Config: SettingsConfiguration<Channel> = {
2122
/**
@@ -53,7 +54,7 @@ const Config: SettingsConfiguration<Channel> = {
5354
case "webhooks":
5455
return <Webhooks channel={channel} />;
5556
default:
56-
return null;
57+
return <ChannelOverview channel={channel} />;
5758
}
5859
},
5960

Diff for: packages/client/components/ui/components/material/TextField.tsx

+4
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ export function TextField(
88
required?: boolean;
99
name?: string;
1010
label?: string;
11+
autosize?: boolean;
12+
disabled?: boolean;
13+
"min-rows"?: number;
14+
"max-rows"?: number;
1115
placeholder?: string;
1216
type?: "text" | "password" | "email";
1317
variant?: "filled" | "outlined";
+99
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import {
2+
Show,
3+
For,
4+
mergeProps,
5+
type Component,
6+
splitProps,
7+
ComponentProps,
8+
} from "solid-js";
9+
import { createFormControl, IFormGroup, type IFormControl } from "solid-forms";
10+
import { TextField } from "../material";
11+
12+
const FormTextField = (
13+
props: {
14+
control: IFormControl<string>;
15+
} & ComponentProps<typeof TextField>
16+
) => {
17+
const [local, remote] = splitProps(props, ["control"]);
18+
19+
return (
20+
<>
21+
<TextField
22+
{...remote}
23+
value={local.control.value}
24+
oninput={(e) => {
25+
local.control.setValue(e.currentTarget.value);
26+
}}
27+
onblur={() => local.control.markTouched(true)}
28+
required={local.control.isRequired}
29+
disabled={local.control.isDisabled}
30+
/>
31+
32+
<Show when={local.control.isTouched && !local.control.isValid}>
33+
<For each={Object.keys(local.control.errors!)}>
34+
{(errorMsg: string) => <small>{errorMsg}</small>}
35+
</For>
36+
</Show>
37+
</>
38+
);
39+
};
40+
41+
const SampleTextInput: Component<{
42+
control?: IFormControl<string>;
43+
name?: string;
44+
type?: string;
45+
}> = (props) => {
46+
// here we provide a default form control in case the user doesn't supply one
47+
let localProps = mergeProps(
48+
{ control: createFormControl(""), type: "text" },
49+
props
50+
);
51+
52+
return (
53+
<div
54+
classList={{
55+
"is-invalid": !!localProps.control.errors,
56+
"is-touched": localProps.control.isTouched,
57+
"is-required": localProps.control.isRequired,
58+
"is-disabled": localProps.control.isDisabled,
59+
}}
60+
>
61+
<input
62+
name={localProps.name}
63+
type={localProps.type}
64+
value={localProps.control.value}
65+
oninput={(e) => {
66+
localProps.control.setValue(e.currentTarget.value);
67+
}}
68+
onblur={() => localProps.control.markTouched(true)}
69+
required={localProps.control.isRequired}
70+
disabled={localProps.control.isDisabled}
71+
/>
72+
73+
<Show when={localProps.control.isTouched && !localProps.control.isValid}>
74+
<For each={Object.values(localProps.control.errors!)}>
75+
{(errorMsg: string) => <small>{errorMsg}</small>}
76+
</For>
77+
</Show>
78+
</div>
79+
);
80+
};
81+
82+
function submitHandler(group: IFormGroup, handler: () => Promise<void>) {
83+
return async (e: Event) => {
84+
e.preventDefault();
85+
86+
for (const control of Object.values(group.controls)) {
87+
control.markTouched(true);
88+
}
89+
90+
if (group.isSubmitted || !group.isValid) return;
91+
92+
group.markSubmitted(true);
93+
};
94+
}
95+
96+
export const Form2 = {
97+
TextField: FormTextField,
98+
submitHandler,
99+
};
+2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
export { InputElement } from "./InputElement";
22
export { Form } from "./Form";
3+
export { Form2 } from "./Form2";
34
export { Deferred } from "./Deferred";
5+
46
export * from "./files";

Diff for: packages/client/components/ui/themes/darkTheme.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -374,9 +374,9 @@ export const darkTheme: (
374374
materialColour("onSurfaceVariant"),
375375

376376
// Settings
377-
"settings-background": materialColour("secondary", 96),
377+
"settings-background": materialColour("secondary", 92),
378378
"settings-foreground": materialColour("onSecondaryContainer"),
379-
"settings-content-background": materialColour("secondary", 92),
379+
"settings-content-background": materialColour("secondary", 96),
380380
"settings-content-foreground": materialColour("onSecondary", 20),
381381
"settings-content-scroll-thumb": materialColour("secondary", 70),
382382
"settings-close-anchor": materialColour("primary"),

Diff for: packages/client/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@
9595
"shiki": "^1.22.0",
9696
"solid-dnd-directive": "^0.2.0",
9797
"solid-floating-ui": "^0.3.1",
98+
"solid-forms": "^0.5.2",
9899
"solid-hcaptcha": "^0.4.0",
99100
"solid-icons": "^1.1.0",
100101
"solid-js": "^1.9.2",

Diff for: packages/client/src/index.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ import "@revolt/ui/styles";
5454
import AuthPage from "./Auth";
5555
import Interface from "./Interface";
5656
import "./index.css";
57-
import 'mdui/mdui.css';
57+
import "mdui/mdui.css";
5858
import { DevelopmentPage } from "./interface/Development";
5959
import { Friends } from "./interface/Friends";
6060
import { HomePage } from "./interface/Home";

Diff for: packages/client/src/interface/Development.tsx

+48
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,14 @@ import {
1212
CategoryCollapse,
1313
Column,
1414
ComboBox,
15+
Form2,
1516
OverrideSwitch,
17+
TextField,
1618
iconSize,
1719
} from "@revolt/ui";
1820

1921
import Face from "@material-design-icons/svg/filled/face.svg?component-solid";
22+
import { createFormControl, createFormGroup } from "solid-forms";
2023

2124
const NewComponent = styled("div", {
2225
base: {
@@ -32,6 +35,37 @@ const newComponent = cva({
3235
},
3336
});
3437

38+
function FormTest() {
39+
const group = createFormGroup({
40+
name: createFormControl(""),
41+
email: createFormControl("", {
42+
// required: true,
43+
validators: (value: string) =>
44+
value.length <= 15 ? { isMissing: true } : null,
45+
}),
46+
});
47+
48+
const onSubmit = async () => {
49+
console.info(group.data);
50+
};
51+
52+
return (
53+
<form onSubmit={Form2.submitHandler(group, onSubmit)}>
54+
<label for="name">Your name</label>
55+
<Form2.TextField name="name" control={group.controls.name} />
56+
57+
<label for="email">Your email address</label>
58+
<Form2.TextField
59+
name="email"
60+
type="email"
61+
control={group.controls.email}
62+
/>
63+
64+
<button>Submit</button>
65+
</form>
66+
);
67+
}
68+
3569
export function DevelopmentPage() {
3670
function open() {
3771
modalController.push({
@@ -67,6 +101,8 @@ export function DevelopmentPage() {
67101

68102
return (
69103
<Column>
104+
<FormTest />
105+
70106
<div
71107
style={{
72108
width: "480px",
@@ -79,6 +115,18 @@ export function DevelopmentPage() {
79115
</div>
80116
<OverrideSwitch />
81117

118+
<TextField
119+
variant="outlined"
120+
label="Outlined Input"
121+
placeholder="Type here :D"
122+
/>
123+
124+
<TextField
125+
variant="filled"
126+
label="Filled Input"
127+
placeholder="Type here :D"
128+
/>
129+
82130
<div
83131
// have to wrap the component in something that can receive a directive
84132
use:floating={{ tooltip: { content: "hello", placement: "bottom" } }}

Diff for: packages/revolt.js

0 commit comments

Comments
 (0)