Skip to content

Commit d11b522

Browse files
authored
v1 (#4)
1 parent d9677a2 commit d11b522

File tree

12 files changed

+1422
-519
lines changed

12 files changed

+1422
-519
lines changed

README.md

Lines changed: 73 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@
1111

1212
# React Fancy Switch
1313

14-
Simple React Fancy Switch Component without framer-motion. its customizable component that provides an elegant and interactive way to switch between multiple options. It's designed to be flexible, accessible, and easy to integrate into your React applications.
14+
React Fancy Switch is a customizable React component that provides an elegant and interactive way to switch between multiple options. It's designed to be flexible, accessible, and easy to integrate into your React applications, all without requiring framer-motion.
1515

1616
## Features
1717

18-
- Supports both string and object-based options
18+
- Supports both primitive (string/number) and object-based options
1919
- Customizable styling for radio buttons and highlighter
2020
- Keyboard navigation support
2121
- Accessible design with proper ARIA attributes
@@ -33,7 +33,7 @@ npm install @omit/react-fancy-switch
3333

3434
Here are examples of how to use the FancySwitch component with different types of option arrays:
3535

36-
### 1. Array of Strings
36+
### 1. Array of Primitives (Strings or Numbers)
3737

3838
```jsx
3939
import React, { useState } from 'react'
@@ -117,23 +117,26 @@ const CustomObjectExample = () => {
117117

118118
### Props
119119

120-
| Prop | Type | Default | Description |
121-
| -------------------------- | ------------------------------ | --------- | ---------------------------------------------------------------- |
122-
| `options` | `OptionType[]` | Required | An array of options to display. Can be strings or objects. |
123-
| `value` | `OptionValue` | - | The currently selected value. |
124-
| `onChange` | `(value: OptionValue) => void` | - | Callback function called when the selection changes. |
125-
| `valueKey` | `string` | `'value'` | The key to use for the option's value when using object options. |
126-
| `labelKey` | `string` | `'label'` | The key to use for the option's label when using object options. |
127-
| `radioClassName` | `string` | - | CSS class name for the radio button elements. |
128-
| `highlighterClassName` | `string` | - | CSS class name for the highlighter element. |
129-
| `highlighterIncludeMargin` | `boolean` | `false` | Whether to include margins in highlighter size calculations. |
120+
| Prop | Type | Default | Description |
121+
| -------------------------- | ------------------------------ | ------------ | ---------------------------------------------------------------- |
122+
| `options` | `OptionType[]` | Required | An array of options to display. Can be primitives or objects. |
123+
| `value` | `OptionValue` | - | The currently selected value. |
124+
| `onChange` | `(value: OptionValue) => void` | - | Callback function called when the selection changes. |
125+
| `valueKey` | `string` | `'value'` | The key to use for the option's value when using object options. |
126+
| `labelKey` | `string` | `'label'` | The key to use for the option's label when using object options. |
127+
| `disabledKey` | `string` | `'disabled'` | The key to use for the option's disabled state (object options). |
128+
| `radioClassName` | `string` | - | CSS class name for the radio button elements. |
129+
| `highlighterClassName` | `string` | - | CSS class name for the highlighter element. |
130+
| `highlighterIncludeMargin` | `boolean` | `false` | Whether to include margins in highlighter size calculations. |
131+
| `highlighterStyle` | `React.CSSProperties` | - | Custom styles for the highlighter element. |
132+
| `disabledOptions` | `OptionValue[]` | `[]` | An array of values for options that should be disabled. |
130133

131134
Additional HTML attributes for the container div can be passed as props and will be spread onto the root element.
132135

133136
### Types
134137

135138
```typescript
136-
type OptionValue = string | number
139+
type OptionValue = string | number | boolean
137140
interface OptionObject {
138141
[key: string]: OptionValue
139142
}
@@ -142,32 +145,79 @@ type OptionType = OptionValue | OptionObject
142145
143146
## Styling
144147
145-
The FancySwitch component provides CSS class hooks for styling:
148+
The FancySwitch component provides several ways to customize its appearance:
146149
147-
- Use the `className` prop to style the container div.
148-
- Use the `radioClassName` prop to style individual radio button elements.
149-
- Use the `highlighterClassName` prop to style the moving highlighter element.
150+
1. Use the `className` prop to style the container div.
151+
2. Use the `radioClassName` prop to style individual radio buttons.
152+
3. Use the `highlighterClassName` prop to style the highlighter element.
153+
4. Use the `highlighterStyle` prop to apply custom inline styles to the highlighter.
150154
151155
Example:
152156
153-
```tsx
157+
```jsx
154158
<FancySwitch
155159
className="flex rounded-full bg-muted p-2"
156160
highlighterClassName="bg-primary rounded-full"
157-
radioClassName={cn(
158-
'relative mx-2 flex h-9 cursor-pointer items-center justify-center rounded-full px-3.5 text-sm font-medium transition-colors focus:outline-none data-[checked]:text-primary-foreground'
159-
)}
161+
radioClassName="relative mx-2 flex h-9 cursor-pointer items-center justify-center rounded-full px-3.5 text-sm font-medium transition-colors focus:outline-none data-[checked]:text-primary-foreground"
160162
highlighterIncludeMargin={true}
163+
highlighterStyle={{ backgroundColor: 'blue', borderRadius: '8px' }}
161164
/>
162165
```
163166
164167
## Accessibility
165168
166169
FancySwitch is built with accessibility in mind:
167170
168-
- Proper `role` and `aria-` attributes are used.
171+
- It uses proper ARIA attributes for screen readers.
169172
- Keyboard navigation is supported (arrow keys to move between options).
170173
- Focus management is handled automatically.
174+
- There's a visually hidden live region that announces the selected option.
175+
176+
## Advanced Usage
177+
178+
### Disabling Options
179+
180+
You can disable specific options using the `disabledOptions` prop:
181+
182+
```jsx
183+
<FancySwitch
184+
options={options}
185+
value={selectedOption}
186+
onChange={setSelectedOption}
187+
disabledOptions={['option2']}
188+
/>
189+
```
190+
191+
## TypeScript Support
192+
193+
FancySwitch is written in TypeScript and provides type definitions. The component is generic, allowing you to specify the type of your options:
194+
195+
```typescript
196+
import FancySwitch from '@omit/react-fancy-switch'
197+
198+
type MyOptionType = {
199+
id: number
200+
name: string
201+
isDisabled: boolean
202+
}
203+
204+
const options: MyOptionType[] = [
205+
{ id: 1, name: 'Option 1', isDisabled: false },
206+
{ id: 2, name: 'Option 2', isDisabled: true },
207+
{ id: 3, name: 'Option 3', isDisabled: false }
208+
]
209+
210+
;<FancySwitch<MyOptionType>
211+
options={options}
212+
valueKey="id"
213+
labelKey="name"
214+
disabledKey="isDisabled"
215+
value={selectedOption}
216+
onChange={setSelectedOption}
217+
/>
218+
```
219+
220+
This ensures type safety when working with custom option types.
171221

172222
## Other Projects
173223

packages/react-fancy-switch/eslint.config.js

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,50 @@ import js from '@eslint/js'
22
import globals from 'globals'
33
import reactHooks from 'eslint-plugin-react-hooks'
44
import reactRefresh from 'eslint-plugin-react-refresh'
5+
import reactImport from 'eslint-plugin-import'
56
import tseslint from 'typescript-eslint'
67

78
export default tseslint.config({
8-
extends: [js.configs.recommended, ...tseslint.configs.recommended],
9+
extends: [
10+
js.configs.recommended,
11+
...tseslint.configs.recommended,
12+
'plugin:import/typescript'
13+
],
914
files: ['**/*.{ts,tsx}'],
1015
ignores: ['dist'],
1116
languageOptions: {
1217
ecmaVersion: 2020,
13-
globals: globals.browser,
18+
globals: globals.browser
1419
},
1520
plugins: {
1621
'react-hooks': reactHooks,
1722
'react-refresh': reactRefresh,
23+
import: reactImport
1824
},
1925
rules: {
2026
...reactHooks.configs.recommended.rules,
2127
'react-refresh/only-export-components': [
2228
'warn',
23-
{ allowConstantExport: true },
29+
{ allowConstantExport: true }
2430
],
31+
'@typescript-eslint/no-explicit-any': 'off',
32+
'@typescript-eslint/consistent-type-imports': [
33+
'error',
34+
{
35+
prefer: 'type-imports',
36+
disallowTypeAnnotations: false
37+
}
38+
],
39+
'@typescript-eslint/no-import-type-side-effects': 'error'
2540
},
41+
settings: {
42+
'import/parsers': {
43+
'@typescript-eslint/parser': ['.ts', '.tsx']
44+
},
45+
'import/resolver': {
46+
typescript: {
47+
alwaysTryTypes: true
48+
}
49+
}
50+
}
2651
})

packages/react-fancy-switch/package.json

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@omit/react-fancy-switch",
33
"private": false,
4-
"version": "0.1.3",
4+
"version": "1.0.0",
55
"type": "module",
66
"license": "MIT",
77
"description": "A fancy switch component",
@@ -44,18 +44,19 @@
4444
},
4545
"devDependencies": {
4646
"@eslint/js": "^9.12.0",
47-
"@types/node": "^22.7.5",
47+
"@types/node": "^22.7.6",
4848
"@types/react": "^18.3.11",
49-
"@types/react-dom": "^18.3.0",
49+
"@types/react-dom": "^18.3.1",
5050
"@vitejs/plugin-react": "^4.3.2",
5151
"eslint": "^9.12.0",
52+
"eslint-plugin-import": "^2.31.0",
5253
"eslint-plugin-react-hooks": "5.1.0-rc-fb9a90fa48-20240614",
5354
"eslint-plugin-react-refresh": "^0.4.12",
5455
"globals": "^15.11.0",
5556
"typescript": "^5.6.3",
56-
"typescript-eslint": "^8.8.1",
57-
"vite": "^5.4.8",
58-
"vite-plugin-dts": "^4.2.3"
57+
"typescript-eslint": "^8.10.0",
58+
"vite": "^5.4.9",
59+
"vite-plugin-dts": "^4.2.4"
5960
},
6061
"peerDependencies": {
6162
"react": "^18.3.1",

0 commit comments

Comments
 (0)