@indietabletop/appkit 3.2.0-6 → 3.2.0-8

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.
@@ -0,0 +1,21 @@
1
+ import type { Meta, StoryObj } from "@storybook/react-vite";
2
+ import { LetterheadReadonlyTextField } from "./index.tsx";
3
+
4
+ const meta = {
5
+ title: "ReadonlyTextField",
6
+ component: LetterheadReadonlyTextField,
7
+ tags: ["autodocs"],
8
+ args: {},
9
+ } satisfies Meta<typeof LetterheadReadonlyTextField>;
10
+
11
+ export default meta;
12
+
13
+ type Story = StoryObj<typeof meta>;
14
+
15
+ export const Default: Story = {
16
+ args: {
17
+ label: "Email",
18
+ value: "john@example.com",
19
+ placeholder: "john@example.com",
20
+ },
21
+ };
@@ -0,0 +1,23 @@
1
+ import type { Meta, StoryObj } from "@storybook/react-vite";
2
+ import { form } from "../storybook/decorators.tsx";
3
+ import { LetterheadSubmitError } from "./index.tsx";
4
+
5
+ const meta = {
6
+ title: "LetterheadSubmitError",
7
+ component: LetterheadSubmitError,
8
+ tags: ["autodocs"],
9
+ args: {},
10
+ decorators: [
11
+ form({ defaultErrors: { submit: "This is an error message." } }),
12
+ ],
13
+ } satisfies Meta<typeof LetterheadSubmitError>;
14
+
15
+ export default meta;
16
+
17
+ type Story = StoryObj<typeof meta>;
18
+
19
+ export const Default: Story = {
20
+ args: {
21
+ name: "submit",
22
+ },
23
+ };
@@ -0,0 +1,23 @@
1
+ import type { Meta, StoryObj } from "@storybook/react-vite";
2
+ import { form } from "../storybook/decorators.tsx";
3
+ import { LetterheadTextField } from "./index.tsx";
4
+
5
+ const meta = {
6
+ title: "FormTextField",
7
+ component: LetterheadTextField,
8
+ tags: ["autodocs"],
9
+ args: {},
10
+ decorators: [form()],
11
+ } satisfies Meta<typeof LetterheadTextField>;
12
+
13
+ export default meta;
14
+
15
+ type Story = StoryObj<typeof meta>;
16
+
17
+ export const Default: Story = {
18
+ args: {
19
+ label: "Email",
20
+ placeholder: "john@example.com",
21
+ name: "foo",
22
+ },
23
+ };
@@ -0,0 +1,94 @@
1
+ import {
2
+ FormError,
3
+ FormInput,
4
+ type FormInputProps,
5
+ FormLabel,
6
+ useFormContext,
7
+ useStoreState,
8
+ } from "@ariakit/react";
9
+ import { type ReactNode } from "react";
10
+ import * as css from "./style.css.ts";
11
+
12
+ type LetterheadTextFieldProps = FormInputProps & {
13
+ label: string;
14
+ hint?: ReactNode;
15
+ };
16
+
17
+ export function LetterheadTextField(props: LetterheadTextFieldProps) {
18
+ const { name, label, hint, ...inputProps } = props;
19
+
20
+ return (
21
+ <div>
22
+ <div className={css.field}>
23
+ <FormLabel name={name} className={css.fieldLabel}>
24
+ {label}
25
+ </FormLabel>
26
+
27
+ <FormInput {...inputProps} name={name} className={css.fieldInput} />
28
+ </div>
29
+
30
+ <FormError name={name} className={css.fieldIssue} />
31
+
32
+ {hint && <div className={css.fieldHint}>{hint}</div>}
33
+ </div>
34
+ );
35
+ }
36
+
37
+ type LetterheadReadonlyTextFieldProps = {
38
+ label: string;
39
+ value: string;
40
+ placeholder?: string;
41
+ hint?: ReactNode;
42
+ type?: "text" | "email" | "password";
43
+ };
44
+
45
+ /**
46
+ * Renders a read-only text field.
47
+ *
48
+ * For an editable text field, use {@link LetterheadTextField} along with
49
+ * Ariakit form store.
50
+ */
51
+ export function LetterheadReadonlyTextField(
52
+ props: LetterheadReadonlyTextFieldProps,
53
+ ) {
54
+ return (
55
+ <div>
56
+ <label className={css.field}>
57
+ <span className={css.fieldLabel}>{props.label}</span>
58
+
59
+ <input
60
+ className={css.fieldInput}
61
+ type={props.type ?? "text"}
62
+ value={props.value}
63
+ placeholder={props.placeholder}
64
+ readOnly
65
+ />
66
+ </label>
67
+
68
+ {props.hint && <div className={css.fieldHint}>{props.hint}</div>}
69
+ </div>
70
+ );
71
+ }
72
+
73
+ type LetterheadSubmitErrorProps = {
74
+ name: string;
75
+ };
76
+
77
+ /**
78
+ * Renders an error message from form context.
79
+ *
80
+ * If there is no error message, will be completely omitted from DOM, so there
81
+ * is no need to guard it with an if statement.
82
+ */
83
+ export function LetterheadSubmitError(props: LetterheadSubmitErrorProps) {
84
+ const form = useFormContext();
85
+ const message = useStoreState(form, (s) => {
86
+ return s?.errors[props.name] as string | undefined;
87
+ });
88
+
89
+ return (
90
+ <div role="alert" className={css.submitError}>
91
+ {message}
92
+ </div>
93
+ );
94
+ }
@@ -0,0 +1,80 @@
1
+ import { style } from "@vanilla-extract/css";
2
+ import { manofa, minion } from "../common.css.ts";
3
+ import { Color } from "../vars.css.ts";
4
+
5
+ const border = style({
6
+ borderRadius: "0.5rem",
7
+ border: `1px solid ${Color.GRAY}`,
8
+ });
9
+
10
+ export const field = style({
11
+ display: "block",
12
+ });
13
+
14
+ export const fieldLabel = style([
15
+ manofa,
16
+ {
17
+ display: "block",
18
+ textTransform: "uppercase",
19
+ fontSize: "0.75rem",
20
+ fontWeight: 600,
21
+ marginBottom: "0.5rem",
22
+ },
23
+ ]);
24
+
25
+ export const fieldInput = style([
26
+ border,
27
+ minion,
28
+ {
29
+ display: "block",
30
+ width: "100%",
31
+ fontSize: "1rem",
32
+ lineHeight: "1.25rem",
33
+ padding: "1rem 0 1rem 1rem",
34
+
35
+ ":read-only": {
36
+ backgroundColor: "hsl(0 0% 0% / 0.05)",
37
+ },
38
+
39
+ // Hide MS Edge widgets -- we handle them manually
40
+ "::-ms-clear": {
41
+ display: "none",
42
+ },
43
+ "::-ms-reveal": {
44
+ display: "none",
45
+ },
46
+ },
47
+ ]);
48
+
49
+ export const fieldIssue = style({
50
+ color: Color.PURPLE,
51
+ fontSize: "0.875rem",
52
+ marginTop: "0.5rem",
53
+
54
+ ":empty": {
55
+ display: "none",
56
+ },
57
+ });
58
+
59
+ export const fieldHint = style({
60
+ color: Color.MID_GRAY,
61
+ fontSize: "0.875rem",
62
+ marginTop: "0.5rem",
63
+
64
+ selectors: {
65
+ [`${fieldIssue}:not(:empty) + &`]: {
66
+ display: "none",
67
+ },
68
+ },
69
+ });
70
+
71
+ export const submitError = style({
72
+ padding: "1rem",
73
+ color: Color.PURPLE,
74
+ backgroundColor: Color.PALE_GRAY,
75
+ borderRadius: "0.75rem",
76
+
77
+ ":empty": {
78
+ display: "none",
79
+ },
80
+ });
@@ -1,5 +1,8 @@
1
1
  import { globalStyle } from "@vanilla-extract/css";
2
2
 
3
+ // Apply global vars
4
+ import "./vars.css.ts";
5
+
3
6
  globalStyle(":root", {
4
7
  fontSynthesis: "none",
5
8
  textRendering: "optimizeLegibility",
package/lib/index.ts CHANGED
@@ -4,6 +4,7 @@ export * from "./FormSubmitButton.tsx";
4
4
  export * from "./FullscreenDismissBlocker.tsx";
5
5
  export * from "./IndieTabletopClubSymbol.tsx";
6
6
  export * from "./Letterhead/index.tsx";
7
+ export * from "./LetterheadForm/index.tsx";
7
8
  export * from "./LoadingIndicator.tsx";
8
9
  export * from "./ServiceWorkerHandler.tsx";
9
10
 
@@ -22,6 +23,7 @@ export * from "./async-op.ts";
22
23
  export * from "./caught-value.ts";
23
24
  export * from "./class-names.ts";
24
25
  export * from "./client.ts";
26
+ export * from "./knownFailure.ts";
25
27
  export * from "./media.ts";
26
28
  export * from "./structs.ts";
27
29
  export * from "./types.ts";
@@ -0,0 +1,9 @@
1
+ import type { FailurePayload } from "./types.ts";
2
+
3
+ export function toKnownFailureMessage(failure: FailurePayload) {
4
+ if (failure.type === "NETWORK_ERROR") {
5
+ return "Could not submit form due to network error. Make sure you are connected to the internet and try again.";
6
+ }
7
+
8
+ return "Could not submit form due to an unexpected error. Please refresh the page and try again.";
9
+ }
@@ -0,0 +1,10 @@
1
+ import { FormProvider, type FormProviderProps } from "@ariakit/react";
2
+ import { type Decorator } from "@storybook/react-vite";
3
+
4
+ export function form(props?: FormProviderProps): Decorator {
5
+ return (Story) => (
6
+ <FormProvider {...props}>
7
+ <Story />
8
+ </FormProvider>
9
+ );
10
+ }
@@ -0,0 +1,9 @@
1
+ import { createGlobalTheme } from "@vanilla-extract/css";
2
+
3
+ export const Color = createGlobalTheme(":root", {
4
+ GRAY: "#ececec",
5
+ MID_GRAY: "hsl(0 0% 50%)",
6
+ LIGHT_GRAY: "#e7e8e8",
7
+ PALE_GRAY: "#f6f7f7",
8
+ PURPLE: "#d6446e",
9
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@indietabletop/appkit",
3
- "version": "3.2.0-6",
3
+ "version": "3.2.0-8",
4
4
  "description": "A collection of modules used in apps built by Indie Tabletop Club",
5
5
  "private": false,
6
6
  "type": "module",