diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c363f25..b1e1241 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -246,7 +246,7 @@ When adding a new item, you will create/edit these files: | `mc-avatar` | ✅ | ✅ | | `mc-drawer` | ✅ | ✅ | | `mc-hover-card` | ✅ | ✅ | -| `mc-slider` | ✅ | | +| `mc-slider` | ✅ | ✅ | | `mc-carousel` | ✅ | ✅ | # Important Notes diff --git a/packages/web/__registry__/index.tsx b/packages/web/__registry__/index.tsx index 30816aa..5cea5da 100644 --- a/packages/web/__registry__/index.tsx +++ b/packages/web/__registry__/index.tsx @@ -2488,6 +2488,19 @@ export const Index: Record = { }, ], }, + 'mc-slider': { + name: 'mc-slider', + description: 'A slider component for MicroClub UI', + type: 'registry:component', + files: [ + { + path: 'registry/ui/mc-slider.tsx', + content: + 'import * as React from \'react\';\nimport { Slider as SliderPrimitive } from \'@base-ui/react/slider\';\n\nimport { cn } from \'@/lib/utils\';\n\nfunction McSlider({\n className,\n defaultValue,\n value,\n min = 0,\n max = 100,\n showValue = true,\n border = false,\n unity,\n onValueChange,\n ...props\n}: SliderPrimitive.Root.Props & {\n showValue?: boolean;\n border?: boolean;\n unity?: string;\n}) {\n const isControlled = value !== undefined;\n\n const [internalValues, setInternalValues] = React.useState(\n Array.isArray(defaultValue) ? defaultValue : [min]\n );\n\n const values = isControlled ? (Array.isArray(value) ? value : [value]) : internalValues;\n\n return (\n {\n if (!isControlled) {\n setInternalValues(Array.isArray(newValues) ? newValues : [newValues]);\n }\n onValueChange?.(newValues, event);\n }}\n {...props}\n >\n \n \n \n \n\n {values.map((currentValue, index) => (\n \n {showValue && (\n \n {currentValue} {unity}\n \n )}\n \n ))}\n \n \n );\n}\n\nexport { McSlider };\n', + type: 'registry:component', + }, + ], + }, 'mc-carousel': { name: 'mc-carousel', description: 'A carousel component for MicroClub UI', @@ -3029,6 +3042,22 @@ export const Index: Record = { source: 'import { McHoverCard, McHoverCardContent, McHoverCardTrigger } from \'../ui/mc-hover-card\';\n\nexport default function McHoverCardDemo() {\n return (\n
\n \n \n \n \n \n \n
\n );\n}\n', }, + 'mc-slider-demo': { + name: 'mc-slider-demo', + description: 'Demo for MicroClub Slider', + type: 'registry:example', + files: [ + { + path: 'registry/examples/mc-slider-demo.tsx', + content: + 'import { McSlider } from \'@/registry/ui/mc-slider\';\n\nexport default function SliderDemo() {\n return ;\n}\n', + type: 'registry:example', + }, + ], + component: React.lazy(() => import('@/registry/examples/mc-slider-demo.tsx')), + source: + 'import { McSlider } from \'@/registry/ui/mc-slider\';\n\nexport default function SliderDemo() {\n return ;\n}\n', + }, 'mc-carousel-demo': { name: 'mc-carousel-demo', description: 'Demo for MicroClub Carousel', diff --git a/packages/web/__registry__/registry.autogenerated.json b/packages/web/__registry__/registry.autogenerated.json index 7c7d537..878d293 100644 --- a/packages/web/__registry__/registry.autogenerated.json +++ b/packages/web/__registry__/registry.autogenerated.json @@ -2584,6 +2584,19 @@ ], "type": "registry:component" }, + { + "name": "mc-slider", + "title": "MicroClub Slider", + "description": "A slider component for MicroClub UI", + "dependencies": ["@base-ui/react"], + "files": [ + { + "path": "registry/ui/mc-slider.tsx", + "type": "registry:component" + } + ], + "type": "registry:component" + }, { "name": "mc-carousel", "title": "MicroClub Carousel", diff --git a/packages/web/content/docs/components/index.mdx b/packages/web/content/docs/components/index.mdx index 2e0ce5c..d57d4f2 100644 --- a/packages/web/content/docs/components/index.mdx +++ b/packages/web/content/docs/components/index.mdx @@ -105,6 +105,9 @@ description: Explore all available UI components in the MicroClub UI library. A card that appears on hover with additional information. + + A component for selecting a value from a range. + A component for cycling through a series of content. @@ -119,7 +122,4 @@ description: Explore all available UI components in the MicroClub UI library. A component that displays progress of an operation. - - A component for selecting a value from a range. - diff --git a/packages/web/content/docs/components/mc-slider.mdx b/packages/web/content/docs/components/mc-slider.mdx index 23f336c..d988e72 100644 --- a/packages/web/content/docs/components/mc-slider.mdx +++ b/packages/web/content/docs/components/mc-slider.mdx @@ -3,7 +3,123 @@ title: MC Slider description: A component for selecting a value from a range. --- - - This component is coming soon. Check our [contributing guide](/docs/introduction#contributing) for - more information. - +## Overview + +The `mc-slider` is a core component of the MicroClub UI library. Built on the **@base-ui/react** slider primitive, it provides an accessible, keyboard-friendly range control with MicroClub's signature styling via **Tailwind CSS v4**. + +As part of the MicroClub library, this component is designed for **instant deployment into your codebase**, giving you full ownership of its look and behavior. + +## Preview + + + + + + + + + + +## Add to Your Project + +Deploy the `mc-slider` directly to your `components/ui` directory: + + + npx mcoli-ui@latest add mc-slider + pnpm dlx mcoli-ui@latest add mc-slider + yarn dlx mcoli-ui@latest add mc-slider + bunx mcoli-ui@latest add mc-slider + + +### Manual Setup + +If you prefer manual control: + + + +Install the core dependencies + + + npm install @base-ui/react + pnpm add @base-ui/react + yarn add @base-ui/react + bun add @base-ui/react + + +Copy the source code + + + +Adjust import paths + +Ensure your `@/lib/utils` or equivalent classname helper is correctly imported. + + + +## Usage + +```tsx +import { McSlider } from '@/components/ui/mc-slider'; + +export default function Example() { + return ; +} +``` + +## Examples + +### Basic + +```tsx + +``` + +### Range + +```tsx + +``` + +### With value labels + +```tsx + +``` + +### With border and custom label styling + +```tsx + +``` + +### Vertical + +```tsx + +``` + +### Disabled + +```tsx + +``` + +## API Reference + +### Props + +| Prop | Type | Default | Description | +| -------------- | ---------------------------- | -------------- | --------------------------------------------------- | +| `defaultValue` | `number[]` | `undefined` | Initial value(s) for the slider | +| `value` | `number[]` | `undefined` | Controlled value(s) | +| `min` | `number` | `0` | Minimum slider value | +| `max` | `number` | `100` | Maximum slider value | +| `step` | `number` | `1` | Increment step | +| `orientation` | `"horizontal" \| "vertical"` | `"horizontal"` | Slider orientation | +| `disabled` | `boolean` | `false` | Disables interaction | +| `showValue` | `boolean` | `true` | Shows the floating value badge above each thumb | +| `border` | `boolean` | `false` | Adds a bordered value badge style | +| `unity` | `string` | `undefined` | Optional suffix displayed next to the current value | +| `className` | `string` | `undefined` | Additional CSS classes | + +All other props from `@base-ui/react/slider` are supported. diff --git a/packages/web/content/docs/components/meta.json b/packages/web/content/docs/components/meta.json index f389c18..fa19278 100644 --- a/packages/web/content/docs/components/meta.json +++ b/packages/web/content/docs/components/meta.json @@ -35,10 +35,10 @@ "mc-avatar", "mc-drawer", "mc-hover-card", + "mc-slider", "mc-carousel", "---Coming Soon---", "mc-data-table", - "mc-progress", - "mc-slider" + "mc-progress" ] } diff --git a/packages/web/public/r/mc-slider.json b/packages/web/public/r/mc-slider.json new file mode 100644 index 0000000..21a9b63 --- /dev/null +++ b/packages/web/public/r/mc-slider.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "mc-slider", + "title": "MicroClub Slider", + "description": "A slider component for MicroClub UI", + "dependencies": ["@base-ui/react"], + "files": [ + { + "path": "registry/ui/mc-slider.tsx", + "content": "import * as React from 'react';\nimport { Slider as SliderPrimitive } from '@base-ui/react/slider';\n\nimport { cn } from '@/lib/utils';\n\nfunction McSlider({\n className,\n defaultValue,\n value,\n min = 0,\n max = 100,\n showValue = true,\n border = false,\n unity,\n onValueChange,\n ...props\n}: SliderPrimitive.Root.Props & {\n showValue?: boolean;\n border?: boolean;\n unity?: string;\n}) {\n const isControlled = value !== undefined;\n\n const [internalValues, setInternalValues] = React.useState(\n Array.isArray(defaultValue) ? defaultValue : [min]\n );\n\n const values = isControlled ? (Array.isArray(value) ? value : [value]) : internalValues;\n\n return (\n {\n if (!isControlled) {\n setInternalValues(Array.isArray(newValues) ? newValues : [newValues]);\n }\n onValueChange?.(newValues, event);\n }}\n {...props}\n >\n \n \n \n \n\n {values.map((currentValue, index) => (\n \n {showValue && (\n \n {currentValue} {unity}\n \n )}\n \n ))}\n \n \n );\n}\n\nexport { McSlider };\n", + "type": "registry:component" + } + ], + "type": "registry:component" +} diff --git a/packages/web/public/r/registry.json b/packages/web/public/r/registry.json index 7c7d537..878d293 100644 --- a/packages/web/public/r/registry.json +++ b/packages/web/public/r/registry.json @@ -2584,6 +2584,19 @@ ], "type": "registry:component" }, + { + "name": "mc-slider", + "title": "MicroClub Slider", + "description": "A slider component for MicroClub UI", + "dependencies": ["@base-ui/react"], + "files": [ + { + "path": "registry/ui/mc-slider.tsx", + "type": "registry:component" + } + ], + "type": "registry:component" + }, { "name": "mc-carousel", "title": "MicroClub Carousel", diff --git a/packages/web/registry/examples/mc-slider-demo.tsx b/packages/web/registry/examples/mc-slider-demo.tsx new file mode 100644 index 0000000..f7ec23f --- /dev/null +++ b/packages/web/registry/examples/mc-slider-demo.tsx @@ -0,0 +1,5 @@ +import { McSlider } from '@/registry/ui/mc-slider'; + +export default function SliderDemo() { + return ; +} diff --git a/packages/web/registry/registry-examples.ts b/packages/web/registry/registry-examples.ts index 16c173b..56ed573 100644 --- a/packages/web/registry/registry-examples.ts +++ b/packages/web/registry/registry-examples.ts @@ -433,6 +433,19 @@ export const examples: Registry['items'] = [ ], registryDependencies: [`${REGISTRY_URL}/r/mc-hover-card.json`], }, + { + name: 'mc-slider-demo', + type: 'registry:example', + title: 'MicroClub Slider Demo', + description: 'Demo for MicroClub Slider', + files: [ + { + path: 'examples/mc-slider-demo.tsx', + type: 'registry:example', + }, + ], + registryDependencies: [`${REGISTRY_URL}/r/mc-slider.json`], + }, { name: 'mc-carousel-demo', type: 'registry:example', diff --git a/packages/web/registry/registry-ui.ts b/packages/web/registry/registry-ui.ts index 02702ea..3c1d09b 100644 --- a/packages/web/registry/registry-ui.ts +++ b/packages/web/registry/registry-ui.ts @@ -430,6 +430,19 @@ export const ui: Registry['items'] = [ ], dependencies: ['@base-ui/react'], }, + { + name: 'mc-slider', + type: 'registry:component', + title: 'MicroClub Slider', + description: 'A slider component for MicroClub UI', + files: [ + { + path: 'ui/mc-slider.tsx', + type: 'registry:component', + }, + ], + dependencies: ['@base-ui/react'], + }, { name: 'mc-carousel', type: 'registry:component', diff --git a/packages/web/registry/ui/mc-slider.tsx b/packages/web/registry/ui/mc-slider.tsx new file mode 100644 index 0000000..e450645 --- /dev/null +++ b/packages/web/registry/ui/mc-slider.tsx @@ -0,0 +1,82 @@ +import * as React from 'react'; +import { Slider as SliderPrimitive } from '@base-ui/react/slider'; + +import { cn } from '@/lib/utils'; + +function McSlider({ + className, + defaultValue, + value, + min = 0, + max = 100, + showValue = true, + border = false, + unity, + onValueChange, + ...props +}: SliderPrimitive.Root.Props & { + showValue?: boolean; + border?: boolean; + unity?: string; +}) { + const isControlled = value !== undefined; + + const [internalValues, setInternalValues] = React.useState( + Array.isArray(defaultValue) ? defaultValue : [min] + ); + + const values = isControlled ? (Array.isArray(value) ? value : [value]) : internalValues; + + return ( + { + if (!isControlled) { + setInternalValues(Array.isArray(newValues) ? newValues : [newValues]); + } + onValueChange?.(newValues, event); + }} + {...props} + > + + + + + + {values.map((currentValue, index) => ( + + {showValue && ( +
+ {currentValue} {unity} +
+ )} +
+ ))} +
+
+ ); +} + +export { McSlider }; diff --git a/packages/web/stories/McSlider.stories.tsx b/packages/web/stories/McSlider.stories.tsx new file mode 100644 index 0000000..86123b8 --- /dev/null +++ b/packages/web/stories/McSlider.stories.tsx @@ -0,0 +1,43 @@ +import type { Meta, StoryObj } from '@storybook/nextjs'; + +import { McSlider } from '../registry/ui/mc-slider'; + +const meta: Meta = { + title: 'Components/McSlider', + component: McSlider, + argTypes: { + value: { control: 'object' }, + defaultValue: { control: 'object' }, + min: { control: 'number' }, + max: { control: 'number' }, + }, +}; + +export default meta; + +type Story = StoryObj; + +export const Default: Story = { + args: { + defaultValue: [25], + min: 0, + max: 100, + }, +}; + +export const Range: Story = { + args: { + defaultValue: [25, 75], + min: 0, + max: 100, + }, +}; + +export const Disabled: Story = { + args: { + defaultValue: [50], + min: 0, + max: 100, + disabled: true, + }, +};