From 1dec7b49ee712e45554ab2f78d75b259b5705d70 Mon Sep 17 00:00:00 2001 From: AOKI Takashi <55625375+RyushiAok@users.noreply.github.com> Date: Fri, 4 Oct 2024 14:30:05 +0900 Subject: [PATCH 1/9] wip: number-input --- packages/constants/component/name.ts | 1 + packages/styles/bases/number-input.css.ts | 69 ++++++++++++++ .../inputs/number-input/components/index.ts | 1 + .../number-input/components/number-input.tsx | 92 +++++++++++++++++++ .../base/inputs/number-input/index.ts | 1 + .../stories/number-input.stories.tsx | 34 +++++++ 6 files changed, 198 insertions(+) create mode 100644 packages/styles/bases/number-input.css.ts create mode 100644 packages/wiz-ui-react/src/components/base/inputs/number-input/components/index.ts create mode 100644 packages/wiz-ui-react/src/components/base/inputs/number-input/components/number-input.tsx create mode 100644 packages/wiz-ui-react/src/components/base/inputs/number-input/index.ts create mode 100644 packages/wiz-ui-react/src/components/base/inputs/number-input/stories/number-input.stories.tsx diff --git a/packages/constants/component/name.ts b/packages/constants/component/name.ts index e32fb8505..c583f9b8f 100644 --- a/packages/constants/component/name.ts +++ b/packages/constants/component/name.ts @@ -68,6 +68,7 @@ export const ComponentName = { ChatCard: "WizChatCard", ChatForm: "WizChatForm", ChatItem: "WizChatItem", + NumberInput: "WizNumberInput", Notification: "WizNotification", NotificationList: "WizNotificationList", NotificationPanel: "WizNotificationPanel", diff --git a/packages/styles/bases/number-input.css.ts b/packages/styles/bases/number-input.css.ts new file mode 100644 index 000000000..bd7cc7c9c --- /dev/null +++ b/packages/styles/bases/number-input.css.ts @@ -0,0 +1,69 @@ +import { style } from "@vanilla-extract/css"; +import { THEME } from "@wizleap-inc/wiz-ui-constants"; + +const BORDER_WIDTH = "1px"; + +export const datePickerStyle = style({ + width: "100%", + borderRadius: THEME.spacing.xs2, + boxSizing: "border-box", + padding: `calc(${THEME.spacing.xs} - ${BORDER_WIDTH}) ${THEME.spacing.xs}`, + fontSize: THEME.fontSize.sm, + lineHeight: THEME.fontSize.xl3, +}); + +export const InputStyle = style({ + "::-webkit-outer-spin-button": { + appearance: "none", + margin: 0, + }, + "::-webkit-inner-spin-button": { + appearance: "none", + margin: 0, + }, + MozAppearance: "textfield", + minWidth: "30%", + border: "none", + outline: "none", + padding: `${THEME.spacing.xs2} ${THEME.spacing.no}`, + lineHeight: THEME.fontSize.xl, + flexGrow: 1, + fontSize: THEME.fontSize.sm, + color: THEME.color.gray["800"], + "::placeholder": { + color: THEME.color.gray["500"], + userSelect: "none", + }, + ":disabled": { + cursor: "not-allowed", + backgroundColor: THEME.color.gray["300"], + }, +}); + +export const ButtonStyle = style({ + lineHeight: 0.2, + position: "relative", + cursor: "pointer", + padding: THEME.spacing.no, + borderRadius: THEME.spacing.xs2, + border: "none", + background: "transparent", + fill: THEME.color.gray["800"], + "@media": { + "(any-hover: hover)": { + ":hover": { + backgroundColor: THEME.color.green["300"], + fill: THEME.color.green["800"], + }, + }, + }, + ":active": { + backgroundColor: THEME.color.green["800"], + fill: THEME.color.white["800"], + }, +}); + +export const ArrowIconStyle = style({ + transform: "scale(2)", + pointerEvents: "none", +}); diff --git a/packages/wiz-ui-react/src/components/base/inputs/number-input/components/index.ts b/packages/wiz-ui-react/src/components/base/inputs/number-input/components/index.ts new file mode 100644 index 000000000..5f0382f95 --- /dev/null +++ b/packages/wiz-ui-react/src/components/base/inputs/number-input/components/index.ts @@ -0,0 +1 @@ +export { WizNumberInput } from "./number-input"; diff --git a/packages/wiz-ui-react/src/components/base/inputs/number-input/components/number-input.tsx b/packages/wiz-ui-react/src/components/base/inputs/number-input/components/number-input.tsx new file mode 100644 index 000000000..92ff27c98 --- /dev/null +++ b/packages/wiz-ui-react/src/components/base/inputs/number-input/components/number-input.tsx @@ -0,0 +1,92 @@ +import { ARIA_LABELS, ComponentName } from "@wizleap-inc/wiz-ui-constants"; +import * as styles from "@wizleap-inc/wiz-ui-styles/bases/number-input.css"; +import { + fillStyle, + fontSizeStyle, + inputBorderStyle, +} from "@wizleap-inc/wiz-ui-styles/commons"; +import clsx from "clsx"; +import { ChangeEvent, useRef } from "react"; + +import { WizIArrowDropDown, WizIArrowDropUp, WizVStack } from "@/components"; +import { BaseProps } from "@/types"; + +type Props = BaseProps & { + value: number; + onChange: (e: ChangeEvent) => void; + placeholder?: string; + disabled?: boolean; + width?: string; + error?: boolean; + min?: number; + max?: number; + step?: number; + precision?: number; +}; + +const NumberInput = (props: Props) => { + const { value, onChange, ...rest } = props; + const inputRef = useRef(null); + + const handleStepUp = () => inputRef.current?.stepUp(); + const handleStepDown = () => inputRef.current?.stepDown(); + + return ( +
+ + + + + + +
+ ); +}; + +export default NumberInput; + +NumberInput.displayName = ComponentName.NumberInput; + +export const WizNumberInput = NumberInput; diff --git a/packages/wiz-ui-react/src/components/base/inputs/number-input/index.ts b/packages/wiz-ui-react/src/components/base/inputs/number-input/index.ts new file mode 100644 index 000000000..40b494c5f --- /dev/null +++ b/packages/wiz-ui-react/src/components/base/inputs/number-input/index.ts @@ -0,0 +1 @@ +export * from "./components"; diff --git a/packages/wiz-ui-react/src/components/base/inputs/number-input/stories/number-input.stories.tsx b/packages/wiz-ui-react/src/components/base/inputs/number-input/stories/number-input.stories.tsx new file mode 100644 index 000000000..a0154ff2f --- /dev/null +++ b/packages/wiz-ui-react/src/components/base/inputs/number-input/stories/number-input.stories.tsx @@ -0,0 +1,34 @@ +/* eslint-disable react-hooks/rules-of-hooks */ +import { Meta, StoryObj } from "@storybook/react"; +import { userEvent, within } from "@storybook/testing-library"; + +import { WizNumberInput } from ".."; + +const meta: Meta = { + title: "Base/Input/NumberInput", + component: WizNumberInput, +}; + +export default meta; +type Story = StoryObj; + +const Template: Story = { + render: (args) => { + return ( +
+ +
+ ); + }, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + const textbox = canvas.getByRole("textbox"); + userEvent.click(textbox); + textbox.blur(); + }, +}; + +export const Default: Story = { + ...Template, + args: {}, +}; From 4d6a8f2dee35ed6a7d897b7b490a399903e7dbd2 Mon Sep 17 00:00:00 2001 From: AOKI Takashi <55625375+RyushiAok@users.noreply.github.com> Date: Fri, 4 Oct 2024 16:21:39 +0900 Subject: [PATCH 2/9] feat(number-input): react --- packages/styles/bases/number-input.css.ts | 30 +++++--- .../number-input/components/number-input.tsx | 75 +++++++++++++------ .../stories/number-input.stories.tsx | 22 +++++- 3 files changed, 88 insertions(+), 39 deletions(-) diff --git a/packages/styles/bases/number-input.css.ts b/packages/styles/bases/number-input.css.ts index bd7cc7c9c..ab2fbed2e 100644 --- a/packages/styles/bases/number-input.css.ts +++ b/packages/styles/bases/number-input.css.ts @@ -1,18 +1,20 @@ import { style } from "@vanilla-extract/css"; import { THEME } from "@wizleap-inc/wiz-ui-constants"; -const BORDER_WIDTH = "1px"; - -export const datePickerStyle = style({ - width: "100%", +export const container = style({ + position: "relative", + background: THEME.color.white["800"], borderRadius: THEME.spacing.xs2, boxSizing: "border-box", - padding: `calc(${THEME.spacing.xs} - ${BORDER_WIDTH}) ${THEME.spacing.xs}`, - fontSize: THEME.fontSize.sm, - lineHeight: THEME.fontSize.xl3, + overflow: "hidden", }); -export const InputStyle = style({ +export const disabled = style({ + backgroundColor: THEME.color.gray[300], + cursor: "not-allowed", +}); + +export const input = style({ "::-webkit-outer-spin-button": { appearance: "none", margin: 0, @@ -22,6 +24,7 @@ export const InputStyle = style({ margin: 0, }, MozAppearance: "textfield", + textAlign: "center", minWidth: "30%", border: "none", outline: "none", @@ -40,13 +43,16 @@ export const InputStyle = style({ }, }); -export const ButtonStyle = style({ +export const button = style({ lineHeight: 0.2, position: "relative", cursor: "pointer", - padding: THEME.spacing.no, - borderRadius: THEME.spacing.xs2, + padding: THEME.spacing.xs2, + borderRadius: THEME.spacing.no, + boxSizing: "border-box", + borderColor: THEME.color.gray["400"], border: "none", + borderLeft: `1px solid ${THEME.color.gray["400"]}`, background: "transparent", fill: THEME.color.gray["800"], "@media": { @@ -63,7 +69,7 @@ export const ButtonStyle = style({ }, }); -export const ArrowIconStyle = style({ +export const arrowIcon = style({ transform: "scale(2)", pointerEvents: "none", }); diff --git a/packages/wiz-ui-react/src/components/base/inputs/number-input/components/number-input.tsx b/packages/wiz-ui-react/src/components/base/inputs/number-input/components/number-input.tsx index 92ff27c98..e4cb0bcdd 100644 --- a/packages/wiz-ui-react/src/components/base/inputs/number-input/components/number-input.tsx +++ b/packages/wiz-ui-react/src/components/base/inputs/number-input/components/number-input.tsx @@ -8,11 +8,16 @@ import { import clsx from "clsx"; import { ChangeEvent, useRef } from "react"; -import { WizIArrowDropDown, WizIArrowDropUp, WizVStack } from "@/components"; +import { + WizDivider, + WizIArrowDropDown, + WizIArrowDropUp, + WizVStack, +} from "@/components"; import { BaseProps } from "@/types"; type Props = BaseProps & { - value: number; + value: number | null; onChange: (e: ChangeEvent) => void; placeholder?: string; disabled?: boolean; @@ -25,58 +30,82 @@ type Props = BaseProps & { }; const NumberInput = (props: Props) => { - const { value, onChange, ...rest } = props; + const { + className, + style, + value, + width = "7rem", + onChange, + disabled, + ...rest + } = props; const inputRef = useRef(null); - const handleStepUp = () => inputRef.current?.stepUp(); - const handleStepDown = () => inputRef.current?.stepDown(); + const triggerChangeEvent = () => { + const event = new Event("input", { bubbles: true }); + if (inputRef.current) { + inputRef.current.dispatchEvent(event); + } + }; + const handleStepUp = () => { + inputRef.current?.stepUp(); + triggerChangeEvent(); + }; + const handleStepDown = () => { + inputRef.current?.stepDown(); + triggerChangeEvent(); + }; return (
- + @@ -85,8 +114,6 @@ const NumberInput = (props: Props) => { ); }; -export default NumberInput; - NumberInput.displayName = ComponentName.NumberInput; export const WizNumberInput = NumberInput; diff --git a/packages/wiz-ui-react/src/components/base/inputs/number-input/stories/number-input.stories.tsx b/packages/wiz-ui-react/src/components/base/inputs/number-input/stories/number-input.stories.tsx index a0154ff2f..ba0045ffb 100644 --- a/packages/wiz-ui-react/src/components/base/inputs/number-input/stories/number-input.stories.tsx +++ b/packages/wiz-ui-react/src/components/base/inputs/number-input/stories/number-input.stories.tsx @@ -1,6 +1,7 @@ /* eslint-disable react-hooks/rules-of-hooks */ import { Meta, StoryObj } from "@storybook/react"; import { userEvent, within } from "@storybook/testing-library"; +import { useState } from "react"; import { WizNumberInput } from ".."; @@ -14,10 +15,18 @@ type Story = StoryObj; const Template: Story = { render: (args) => { + const [value, setValue] = useState(0); return ( -
- -
+ <> + { + setValue(e.currentTarget.valueAsNumber); + }} + /> +
value : {value}
+ ); }, play: async ({ canvasElement }) => { @@ -32,3 +41,10 @@ export const Default: Story = { ...Template, args: {}, }; + +export const Placeholder: Story = { + ...Template, + args: { + placeholder: "数字を入力", + }, +}; From d2e789e606ed0be4d5ce0d1ffb7ffd09f2598d4b Mon Sep 17 00:00:00 2001 From: AOKI Takashi <55625375+RyushiAok@users.noreply.github.com> Date: Fri, 4 Oct 2024 16:37:26 +0900 Subject: [PATCH 3/9] feat(number-input): vue --- packages/styles/bases/number-input.css.ts | 4 +- .../base/inputs/number-input/index.ts | 1 + .../number-input/number-input.stories.ts | 30 ++++ .../base/inputs/number-input/number-input.vue | 137 ++++++++++++++++++ 4 files changed, 170 insertions(+), 2 deletions(-) create mode 100644 packages/wiz-ui-next/src/components/base/inputs/number-input/index.ts create mode 100644 packages/wiz-ui-next/src/components/base/inputs/number-input/number-input.stories.ts create mode 100644 packages/wiz-ui-next/src/components/base/inputs/number-input/number-input.vue diff --git a/packages/styles/bases/number-input.css.ts b/packages/styles/bases/number-input.css.ts index ab2fbed2e..37102743b 100644 --- a/packages/styles/bases/number-input.css.ts +++ b/packages/styles/bases/number-input.css.ts @@ -24,11 +24,11 @@ export const input = style({ margin: 0, }, MozAppearance: "textfield", - textAlign: "center", + textAlign: "right", minWidth: "30%", border: "none", outline: "none", - padding: `${THEME.spacing.xs2} ${THEME.spacing.no}`, + padding: `${THEME.spacing.xs2} ${THEME.spacing.xs}`, lineHeight: THEME.fontSize.xl, flexGrow: 1, fontSize: THEME.fontSize.sm, diff --git a/packages/wiz-ui-next/src/components/base/inputs/number-input/index.ts b/packages/wiz-ui-next/src/components/base/inputs/number-input/index.ts new file mode 100644 index 000000000..dd5b23851 --- /dev/null +++ b/packages/wiz-ui-next/src/components/base/inputs/number-input/index.ts @@ -0,0 +1 @@ +export { default as WizNumberInput } from "./number-input.vue"; diff --git a/packages/wiz-ui-next/src/components/base/inputs/number-input/number-input.stories.ts b/packages/wiz-ui-next/src/components/base/inputs/number-input/number-input.stories.ts new file mode 100644 index 000000000..462ff8305 --- /dev/null +++ b/packages/wiz-ui-next/src/components/base/inputs/number-input/number-input.stories.ts @@ -0,0 +1,30 @@ +import { Meta, StoryFn } from "@storybook/vue3"; + +import { WizHStack } from "@/components"; + +import { WizNumberInput } from "."; + +export default { + title: "Base/Input/NumberInput", + component: WizNumberInput, +} as Meta; + +const Template: StoryFn = (args) => ({ + components: { WizNumberInput, WizHStack }, + setup() { + return { args }; + }, + template: ` +
+ +
value : {{ args.modelValue }}
+
+ `, +}); +export const Default = Template.bind({}); +Default.args = {}; + +export const Placeholder = Template.bind({}); +Placeholder.args = { + placeholder: "数字を入力", +}; diff --git a/packages/wiz-ui-next/src/components/base/inputs/number-input/number-input.vue b/packages/wiz-ui-next/src/components/base/inputs/number-input/number-input.vue new file mode 100644 index 000000000..f7eca9c12 --- /dev/null +++ b/packages/wiz-ui-next/src/components/base/inputs/number-input/number-input.vue @@ -0,0 +1,137 @@ + + + From 333fceac8332db74a281a514b7635fbc540ef88f Mon Sep 17 00:00:00 2001 From: RyushiAok <55625375+RyushiAok@users.noreply.github.com> Date: Sun, 6 Oct 2024 00:03:46 +0900 Subject: [PATCH 4/9] feat(number-input): button, disabled, error style --- packages/styles/bases/number-input.css.ts | 24 +++++---- .../number-input/number-input.stories.ts | 11 ++++ .../base/inputs/number-input/number-input.vue | 47 ++++++++-------- .../components/custom/form/group.stories.ts | 13 +++++ .../number-input/components/number-input.tsx | 53 ++++++++++--------- .../stories/number-input.stories.tsx | 31 +++++------ .../custom/form/stories/all-input.tsx | 9 ++++ 7 files changed, 117 insertions(+), 71 deletions(-) diff --git a/packages/styles/bases/number-input.css.ts b/packages/styles/bases/number-input.css.ts index 37102743b..532c9f191 100644 --- a/packages/styles/bases/number-input.css.ts +++ b/packages/styles/bases/number-input.css.ts @@ -40,6 +40,7 @@ export const input = style({ ":disabled": { cursor: "not-allowed", backgroundColor: THEME.color.gray["300"], + color: THEME.color.gray["500"], }, }); @@ -54,18 +55,23 @@ export const button = style({ border: "none", borderLeft: `1px solid ${THEME.color.gray["400"]}`, background: "transparent", - fill: THEME.color.gray["800"], - "@media": { - "(any-hover: hover)": { - ":hover": { - backgroundColor: THEME.color.green["300"], - fill: THEME.color.green["800"], + fill: THEME.color.gray["500"], + selectors: { + "&:hover:not(:disabled)": { + "@media": { + "(any-hover: hover)": { + backgroundColor: THEME.color.green["300"], + fill: THEME.color.green["800"], + }, }, }, + "&:active:not(:disabled)": { + backgroundColor: THEME.color.green["800"], + fill: THEME.color.white["800"], + }, }, - ":active": { - backgroundColor: THEME.color.green["800"], - fill: THEME.color.white["800"], + ":disabled": { + cursor: "not-allowed", }, }); diff --git a/packages/wiz-ui-next/src/components/base/inputs/number-input/number-input.stories.ts b/packages/wiz-ui-next/src/components/base/inputs/number-input/number-input.stories.ts index 462ff8305..c1c2d3cbd 100644 --- a/packages/wiz-ui-next/src/components/base/inputs/number-input/number-input.stories.ts +++ b/packages/wiz-ui-next/src/components/base/inputs/number-input/number-input.stories.ts @@ -28,3 +28,14 @@ export const Placeholder = Template.bind({}); Placeholder.args = { placeholder: "数字を入力", }; + +export const WithValue = Template.bind({}); +WithValue.args = { + modelValue: 100, +}; + +export const Disabled = Template.bind({}); +Disabled.args = { + modelValue: 100, + disabled: true, +}; diff --git a/packages/wiz-ui-next/src/components/base/inputs/number-input/number-input.vue b/packages/wiz-ui-next/src/components/base/inputs/number-input/number-input.vue index f7eca9c12..9676cfdac 100644 --- a/packages/wiz-ui-next/src/components/base/inputs/number-input/number-input.vue +++ b/packages/wiz-ui-next/src/components/base/inputs/number-input/number-input.vue @@ -3,9 +3,11 @@ :class="[ styles.container, disabled && styles.disabled, - inputBorderStyle['default'], + inputBorderStyle[isError ? 'error' : hasFocus ? 'active' : 'default'], ]" :style="{ display: 'flex', width: width }" + @focusin="hasFocus = true" + @focusout="hasFocus = false" > @@ -35,14 +37,13 @@ type="button" @click="handleStepDown" :class="styles.button" - :aria-label="ARIA_LABELS.YEAR_SELECTOR_PREV" + :disabled="disabled" > - @@ -50,21 +51,19 @@