Skip to content

Commit

Permalink
feat(component): add number-filed (#64)
Browse files Browse the repository at this point in the history
* feat(component): add number-filed

* clean up

---------

Co-authored-by: Stefan E-K <[email protected]>
  • Loading branch information
NoahCodeGG and stefan-karger authored Mar 13, 2024
1 parent 18a5845 commit 30e267a
Show file tree
Hide file tree
Showing 10 changed files with 309 additions and 52 deletions.
2 changes: 1 addition & 1 deletion apps/docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
},
"dependencies": {
"@fontsource/inter": "^5.0.16",
"@kobalte/core": "^0.12.1",
"@kobalte/core": "^0.12.4",
"@modular-forms/solid": "^0.20.0",
"@solid-primitives/keyboard": "^1.2.5",
"@solid-primitives/refs": "^1.0.6",
Expand Down
10 changes: 10 additions & 0 deletions apps/docs/public/registry/index.json
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,16 @@
],
"type": "ui"
},
{
"name": "number-field",
"dependencies": [
"@kobalte/core"
],
"files": [
"ui/number-field.tsx"
],
"type": "ui"
},
{
"name": "pagination",
"dependencies": [
Expand Down
13 changes: 13 additions & 0 deletions apps/docs/public/registry/ui/number-field.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"name": "number-field",
"dependencies": [
"@kobalte/core"
],
"files": [
{
"name": "number-field.tsx",
"content": "import { splitProps, type Component } from \"solid-js\"\n\nimport { NumberField as NumberFieldPrimitive } from \"@kobalte/core\"\nimport { TbChevronDown, TbChevronUp } from \"solid-icons/tb\"\n\nimport { cn } from \"~/lib/utils\"\n\nconst NumberField = NumberFieldPrimitive.Root\n\nconst NumberFieldLabel: Component<NumberFieldPrimitive.NumberFieldLabelProps> = (props) => {\n const [, rest] = splitProps(props, [\"class\"])\n return (\n <NumberFieldPrimitive.Label\n class={cn(\n \"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70\",\n props.class\n )}\n {...rest}\n />\n )\n}\n\nconst NumberFieldInput: Component<NumberFieldPrimitive.NumberFieldInputProps> = (props) => {\n const [, rest] = splitProps(props, [\"class\"])\n return (\n <NumberFieldPrimitive.Input\n class={cn(\n \"flex h-10 w-full rounded-md border border-input bg-transparent px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[invalid]:border-error-foreground data-[invalid]:text-error-foreground\",\n props.class\n )}\n {...rest}\n />\n )\n}\n\nconst NumberFieldIncrementTrigger: Component<\n NumberFieldPrimitive.NumberFieldIncrementTriggerProps\n> = (props) => {\n const [, rest] = splitProps(props, [\"class\", \"children\"])\n return (\n <NumberFieldPrimitive.IncrementTrigger\n class={cn(\n \"absolute right-1 top-1 inline-flex size-4 items-center justify-center\",\n props.class\n )}\n {...rest}\n >\n {props.children ?? <TbChevronUp />}\n </NumberFieldPrimitive.IncrementTrigger>\n )\n}\n\nconst NumberFieldDecrementTrigger: Component<\n NumberFieldPrimitive.NumberFieldDecrementTriggerProps\n> = (props) => {\n const [, rest] = splitProps(props, [\"class\", \"children\"])\n return (\n <NumberFieldPrimitive.DecrementTrigger\n class={cn(\n \"absolute bottom-1 right-1 inline-flex size-4 items-center justify-center\",\n props.class\n )}\n {...rest}\n >\n {props.children ?? <TbChevronDown />}\n </NumberFieldPrimitive.DecrementTrigger>\n )\n}\n\nconst NumberFieldDescription: Component<NumberFieldPrimitive.NumberFieldDescriptionProps> = (\n props\n) => {\n const [, rest] = splitProps(props, [\"class\"])\n return (\n <NumberFieldPrimitive.Description\n class={cn(\"text-sm text-muted-foreground\", props.class)}\n {...rest}\n />\n )\n}\n\nconst NumberFieldErrorMessage: Component<NumberFieldPrimitive.NumberFieldErrorMessageProps> = (\n props\n) => {\n const [, rest] = splitProps(props, [\"class\"])\n return (\n <NumberFieldPrimitive.ErrorMessage\n class={cn(\"text-sm text-error-foreground\", props.class)}\n {...rest}\n />\n )\n}\n\nexport {\n NumberField,\n NumberFieldLabel,\n NumberFieldInput,\n NumberFieldIncrementTrigger,\n NumberFieldDecrementTrigger,\n NumberFieldDescription,\n NumberFieldErrorMessage\n}\n"
}
],
"type": "ui"
}
28 changes: 21 additions & 7 deletions apps/docs/src/__registry__/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,13 @@ export const Index: Record<string, any> = {
component: lazy(() => import("~/registry/ui/menubar")),
files: ["registry/ui/menubar.tsx"],
},
"number-field": {
name: "number-field",
type: "ui",
registryDependencies: undefined,
component: lazy(() => import("~/registry/ui/number-field")),
files: ["registry/ui/number-field.tsx"],
},
"pagination": {
name: "pagination",
type: "ui",
Expand Down Expand Up @@ -475,6 +482,20 @@ export const Index: Record<string, any> = {
component: lazy(() => import("~/registry/example/menubar-demo")),
files: ["registry/example/menubar-demo.tsx"],
},
"mode-toggle": {
name: "mode-toggle",
type: "example",
registryDependencies: undefined,
component: lazy(() => import("~/registry/example/mode-toggle")),
files: ["registry/example/mode-toggle.tsx"],
},
"number-field-demo": {
name: "number-field-demo",
type: "example",
registryDependencies: undefined,
component: lazy(() => import("~/registry/example/number-field-demo")),
files: ["registry/example/number-field-demo.tsx"],
},
"pagination-demo": {
name: "pagination-demo",
type: "example",
Expand Down Expand Up @@ -594,11 +615,4 @@ export const Index: Record<string, any> = {
component: lazy(() => import("~/registry/example/tooltip-demo")),
files: ["registry/example/tooltip-demo.tsx"],
},
"mode-toggle": {
name: "mode-toggle",
type: "example",
registryDependencies: undefined,
component: lazy(() => import("~/registry/example/mode-toggle")),
files: ["registry/example/mode-toggle.tsx"],
},
}
4 changes: 4 additions & 0 deletions apps/docs/src/config/docs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,10 @@ export const docsConfig: Config = {
title: "Menubar",
href: "/docs/components/menubar"
},
{
title: "NumberField",
href: "/docs/components/number-field"
},
{
title: "Pagination",
href: "/docs/components/pagination"
Expand Down
28 changes: 28 additions & 0 deletions apps/docs/src/registry/example/number-field-demo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { createSignal } from "solid-js"

import {
NumberField,
NumberFieldDecrementTrigger,
NumberFieldErrorMessage,
NumberFieldIncrementTrigger,
NumberFieldInput
} from "~/registry/ui/number-field"

export default function NumberFieldDemo() {
const [rawValue, setRawValue] = createSignal<number>()

return (
<NumberField
class="flex w-36 flex-col gap-2"
onRawValueChange={setRawValue}
validationState={rawValue() !== 40 ? "invalid" : "valid"}
>
<div class="relative">
<NumberFieldInput />
<NumberFieldIncrementTrigger />
<NumberFieldDecrementTrigger />
</div>
<NumberFieldErrorMessage>Hmm, I prefer 40.</NumberFieldErrorMessage>
</NumberField>
)
}
21 changes: 16 additions & 5 deletions apps/docs/src/registry/registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,12 @@ const ui: Registry = [
type: "ui",
files: ["ui/menubar.tsx"]
},
{
name: "number-field",
type: "ui",
dependencies: ["@kobalte/core"],
files: ["ui/number-field.tsx"]
},
{
name: "pagination",
type: "ui",
Expand Down Expand Up @@ -378,6 +384,16 @@ const examples: Registry = [
type: "example",
files: ["example/menubar-demo.tsx"]
},
{
name: "mode-toggle",
type: "example",
files: ["example/mode-toggle.tsx"]
},
{
name: "number-field-demo",
type: "example",
files: ["example/number-field-demo.tsx"]
},
{
name: "pagination-demo",
type: "example",
Expand Down Expand Up @@ -462,11 +478,6 @@ const examples: Registry = [
name: "tooltip-demo",
type: "example",
files: ["example/tooltip-demo.tsx"]
},
{
name: "mode-toggle",
type: "example",
files: ["example/mode-toggle.tsx"]
}
]

Expand Down
102 changes: 102 additions & 0 deletions apps/docs/src/registry/ui/number-field.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { splitProps, type Component } from "solid-js"

import { NumberField as NumberFieldPrimitive } from "@kobalte/core"
import { TbChevronDown, TbChevronUp } from "solid-icons/tb"

import { cn } from "~/lib/utils"

const NumberField = NumberFieldPrimitive.Root

const NumberFieldLabel: Component<NumberFieldPrimitive.NumberFieldLabelProps> = (props) => {
const [, rest] = splitProps(props, ["class"])
return (
<NumberFieldPrimitive.Label
class={cn(
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
props.class
)}
{...rest}
/>
)
}

const NumberFieldInput: Component<NumberFieldPrimitive.NumberFieldInputProps> = (props) => {
const [, rest] = splitProps(props, ["class"])
return (
<NumberFieldPrimitive.Input
class={cn(
"flex h-10 w-full rounded-md border border-input bg-transparent px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[invalid]:border-error-foreground data-[invalid]:text-error-foreground",
props.class
)}
{...rest}
/>
)
}

const NumberFieldIncrementTrigger: Component<
NumberFieldPrimitive.NumberFieldIncrementTriggerProps
> = (props) => {
const [, rest] = splitProps(props, ["class", "children"])
return (
<NumberFieldPrimitive.IncrementTrigger
class={cn(
"absolute right-1 top-1 inline-flex size-4 items-center justify-center",
props.class
)}
{...rest}
>
{props.children ?? <TbChevronUp />}
</NumberFieldPrimitive.IncrementTrigger>
)
}

const NumberFieldDecrementTrigger: Component<
NumberFieldPrimitive.NumberFieldDecrementTriggerProps
> = (props) => {
const [, rest] = splitProps(props, ["class", "children"])
return (
<NumberFieldPrimitive.DecrementTrigger
class={cn(
"absolute bottom-1 right-1 inline-flex size-4 items-center justify-center",
props.class
)}
{...rest}
>
{props.children ?? <TbChevronDown />}
</NumberFieldPrimitive.DecrementTrigger>
)
}

const NumberFieldDescription: Component<NumberFieldPrimitive.NumberFieldDescriptionProps> = (
props
) => {
const [, rest] = splitProps(props, ["class"])
return (
<NumberFieldPrimitive.Description
class={cn("text-sm text-muted-foreground", props.class)}
{...rest}
/>
)
}

const NumberFieldErrorMessage: Component<NumberFieldPrimitive.NumberFieldErrorMessageProps> = (
props
) => {
const [, rest] = splitProps(props, ["class"])
return (
<NumberFieldPrimitive.ErrorMessage
class={cn("text-sm text-error-foreground", props.class)}
{...rest}
/>
)
}

export {
NumberField,
NumberFieldLabel,
NumberFieldInput,
NumberFieldIncrementTrigger,
NumberFieldDecrementTrigger,
NumberFieldDescription,
NumberFieldErrorMessage
}
65 changes: 65 additions & 0 deletions apps/docs/src/routes/docs/components/number-field.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
---
title: NumberField
description: A number input that allow users to input custom number entries with a keyboard.
kobalte: https://kobalte.dev/docs/core/components/number-field
---

<ComponentPreview name="number-field-demo" />

## Installation

<Tabs defaultValue="cli">

<TabsList>
<TabsTrigger value="cli">CLI</TabsTrigger>
<TabsTrigger value="manual">Manual</TabsTrigger>
</TabsList>

<TabsContent value="cli">

```bash
npx solidui-cli@latest add number-field
```

</TabsContent>

<TabsContent value="manual">
<Steps>

<Step>Install the following dependencies: </Step>

```bash
pnpm install @kobalte/core solid-icons
```

<Step>Copy and paste the following code into your project: </Step>

<ComponentSource name="number-field" />

</Steps>
</TabsContent>

</Tabs>

## Usage

```tsx
import {
NumberField,
NumberFieldDecrementTrigger,
NumberFieldIncrementTrigger,
NumberFieldInput
} from "~/registry/ui/number-field"

export default function NumberFieldDemo() {
return (
<NumberField class="w-36" defaultValue={40}>
<div class="relative">
<NumberFieldInput />
<NumberFieldIncrementTrigger />
<NumberFieldDecrementTrigger />
</div>
</NumberField>
)
}
```
Loading

0 comments on commit 30e267a

Please sign in to comment.