@mieweb/ui 0.1.0 → 0.2.0
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.
- package/LICENSE +39 -15
- package/README.md +9 -1
- package/dist/brands/index.cjs +7 -7
- package/dist/brands/index.js +2 -2
- package/dist/{chunk-CLNOI5J7.js → chunk-4SMSH4OY.js} +4 -4
- package/dist/chunk-4SMSH4OY.js.map +1 -0
- package/dist/chunk-4T2ZNPTC.js +220 -0
- package/dist/chunk-4T2ZNPTC.js.map +1 -0
- package/dist/chunk-5T3AWNHG.cjs +471 -0
- package/dist/chunk-5T3AWNHG.cjs.map +1 -0
- package/dist/{chunk-LEE3NMNP.cjs → chunk-5UUL5EEO.cjs} +131 -81
- package/dist/chunk-5UUL5EEO.cjs.map +1 -0
- package/dist/chunk-74K3RRU7.cjs +4 -0
- package/dist/{chunk-ZO46CFVN.cjs.map → chunk-74K3RRU7.cjs.map} +1 -1
- package/dist/{chunk-VWXGUNBR.cjs → chunk-AKTUXJPI.cjs} +107 -18
- package/dist/chunk-AKTUXJPI.cjs.map +1 -0
- package/dist/chunk-BV75DAKO.cjs +245 -0
- package/dist/chunk-BV75DAKO.cjs.map +1 -0
- package/dist/{chunk-6DP6RKUA.cjs → chunk-CLJZHS7Y.cjs} +2 -2
- package/dist/{chunk-6DP6RKUA.cjs.map → chunk-CLJZHS7Y.cjs.map} +1 -1
- package/dist/{chunk-NH2JVQ6V.cjs → chunk-I7L6CQXR.cjs} +21 -9
- package/dist/chunk-I7L6CQXR.cjs.map +1 -0
- package/dist/{chunk-BR2XGATJ.cjs → chunk-NNEFAUHV.cjs} +4 -4
- package/dist/chunk-NNEFAUHV.cjs.map +1 -0
- package/dist/{chunk-KJOFWJHV.js → chunk-QSMMFATL.js} +131 -81
- package/dist/chunk-QSMMFATL.js.map +1 -0
- package/dist/{chunk-FIUNOH6W.js → chunk-S4DK5WN6.js} +2 -2
- package/dist/{chunk-FIUNOH6W.js.map → chunk-S4DK5WN6.js.map} +1 -1
- package/dist/chunk-SCV7C55E.cjs +11 -0
- package/dist/chunk-SCV7C55E.cjs.map +1 -0
- package/dist/{chunk-D5IBXXF2.js → chunk-SD44QJIP.js} +20 -8
- package/dist/chunk-SD44QJIP.js.map +1 -0
- package/dist/{chunk-QBWVTJKF.js → chunk-UBRDKNLQ.js} +107 -18
- package/dist/chunk-UBRDKNLQ.js.map +1 -0
- package/dist/chunk-V2DF2GUE.js +3 -0
- package/dist/{chunk-ZQ4XMJH7.js.map → chunk-V2DF2GUE.js.map} +1 -1
- package/dist/chunk-VSQF22GL.js +9 -0
- package/dist/chunk-VSQF22GL.js.map +1 -0
- package/dist/chunk-XVZ4SLQB.js +447 -0
- package/dist/chunk-XVZ4SLQB.js.map +1 -0
- package/dist/components/AudioPlayer/index.cjs +6 -6
- package/dist/components/AudioPlayer/index.d.cts +5 -1
- package/dist/components/AudioPlayer/index.d.ts +5 -1
- package/dist/components/AudioPlayer/index.js +1 -1
- package/dist/components/Modal/index.cjs +11 -10
- package/dist/components/Modal/index.js +3 -2
- package/dist/components/RecordButton/index.cjs +4 -8
- package/dist/components/RecordButton/index.d.cts +57 -44
- package/dist/components/RecordButton/index.d.ts +57 -44
- package/dist/components/RecordButton/index.js +1 -1
- package/dist/components/Select/index.cjs +3 -4
- package/dist/components/Select/index.js +1 -2
- package/dist/components/Slider/index.cjs +25 -0
- package/dist/components/Slider/index.cjs.map +1 -0
- package/dist/components/Slider/index.d.cts +82 -0
- package/dist/components/Slider/index.d.ts +82 -0
- package/dist/components/Slider/index.js +4 -0
- package/dist/components/Slider/index.js.map +1 -0
- package/dist/components/Spinner/index.d.cts +1 -1
- package/dist/components/Spinner/index.d.ts +1 -1
- package/dist/hooks/index.cjs +3 -2
- package/dist/hooks/index.js +2 -1
- package/dist/index.cjs +1899 -1238
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +68 -25
- package/dist/index.d.ts +68 -25
- package/dist/index.js +1793 -1148
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/utils/index.cjs +6 -1
- package/dist/utils/index.d.cts +14 -1
- package/dist/utils/index.d.ts +14 -1
- package/dist/utils/index.js +2 -1
- package/package.json +7 -7
- package/dist/chunk-BR2XGATJ.cjs.map +0 -1
- package/dist/chunk-CLNOI5J7.js.map +0 -1
- package/dist/chunk-D5IBXXF2.js.map +0 -1
- package/dist/chunk-FQ5G7J24.js +0 -297
- package/dist/chunk-FQ5G7J24.js.map +0 -1
- package/dist/chunk-HLW3XD5R.cjs +0 -322
- package/dist/chunk-HLW3XD5R.cjs.map +0 -1
- package/dist/chunk-KJOFWJHV.js.map +0 -1
- package/dist/chunk-LEE3NMNP.cjs.map +0 -1
- package/dist/chunk-NH2JVQ6V.cjs.map +0 -1
- package/dist/chunk-QBWVTJKF.js.map +0 -1
- package/dist/chunk-VWXGUNBR.cjs.map +0 -1
- package/dist/chunk-ZO46CFVN.cjs +0 -4
- package/dist/chunk-ZQ4XMJH7.js +0 -3
package/LICENSE
CHANGED
|
@@ -1,21 +1,45 @@
|
|
|
1
|
-
|
|
1
|
+
@mieweb/ui - Source Available License
|
|
2
2
|
|
|
3
|
-
Copyright (c) 2026
|
|
3
|
+
Copyright (c) 2026 Medical Informatics Engineering, LLC. All rights reserved.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
5
|
+
This software and associated documentation files (the "Software") are the
|
|
6
|
+
proprietary property of Medical Informatics Engineering, LLC. ("MIE").
|
|
11
7
|
|
|
12
|
-
|
|
13
|
-
|
|
8
|
+
OPEN SOURCE / NON-COMMERCIAL USE
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person or organization
|
|
11
|
+
obtaining a copy of this Software, to use, copy, modify, merge, and distribute
|
|
12
|
+
the Software for non-commercial purposes, subject to the following conditions:
|
|
13
|
+
|
|
14
|
+
1. Attribution Required: The above copyright notice, this permission notice,
|
|
15
|
+
and clear attribution to Medical Informatics Engineering, LLC. shall be
|
|
16
|
+
included in all copies or substantial portions of the Software.
|
|
17
|
+
|
|
18
|
+
2. Open Source Requirement: If you distribute the Software or any derivative
|
|
19
|
+
works, you must do so under an open source license approved by the Open
|
|
20
|
+
Source Initiative (OSI) and make the source code publicly available.
|
|
21
|
+
|
|
22
|
+
3. Non-Commercial Use Only: This license does not grant permission to use the
|
|
23
|
+
Software for commercial purposes. "Commercial purposes" means any use
|
|
24
|
+
intended for or directed toward commercial advantage or monetary
|
|
25
|
+
compensation.
|
|
26
|
+
|
|
27
|
+
COMMERCIAL USE
|
|
28
|
+
|
|
29
|
+
For commercial use, including but not limited to:
|
|
30
|
+
- Use in proprietary/closed-source products
|
|
31
|
+
- Use in products or services sold for profit
|
|
32
|
+
- Use by for-profit organizations for internal business operations
|
|
33
|
+
|
|
34
|
+
You must obtain a separate commercial license from Medical Informatics
|
|
35
|
+
Engineering, LLC. Contact: https://www.mieweb.com or helpdesk@mieweb.com
|
|
36
|
+
|
|
37
|
+
NO WARRANTY
|
|
14
38
|
|
|
15
39
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
40
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
18
|
-
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
|
|
21
|
-
SOFTWARE.
|
|
41
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
42
|
+
MEDICAL INFORMATICS ENGINEERING, LLC. OR ITS AFFILIATES BE LIABLE FOR ANY
|
|
43
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
44
|
+
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
|
|
45
|
+
OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
CHANGED
|
@@ -693,4 +693,12 @@ We welcome contributions! Here's how to get started:
|
|
|
693
693
|
|
|
694
694
|
## License
|
|
695
695
|
|
|
696
|
-
|
|
696
|
+
Copyright © 2026 Medical Informatics Engineering, Inc. All rights reserved.
|
|
697
|
+
|
|
698
|
+
This software is **source available** with the following terms:
|
|
699
|
+
|
|
700
|
+
- ✅ **Free for open source projects** - Use, modify, and distribute freely in open source projects with attribution
|
|
701
|
+
- ✅ **Free for non-commercial use** - Personal projects, education, research
|
|
702
|
+
- 💼 **Commercial license required** - For proprietary products or commercial use, contact [licensing@mieweb.com](mailto:licensing@mieweb.com)
|
|
703
|
+
|
|
704
|
+
See the [LICENSE](LICENSE) file for full details.
|
package/dist/brands/index.cjs
CHANGED
|
@@ -1,19 +1,23 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunkCLJZHS7Y_cjs = require('../chunk-CLJZHS7Y.cjs');
|
|
4
|
+
var chunkP52GA3GJ_cjs = require('../chunk-P52GA3GJ.cjs');
|
|
4
5
|
var chunkS6UNPMAS_cjs = require('../chunk-S6UNPMAS.cjs');
|
|
5
6
|
var chunkSWV5E75F_cjs = require('../chunk-SWV5E75F.cjs');
|
|
6
7
|
var chunkZ3TFPXVN_cjs = require('../chunk-Z3TFPXVN.cjs');
|
|
7
8
|
var chunkFFJVCQ5R_cjs = require('../chunk-FFJVCQ5R.cjs');
|
|
8
9
|
var chunk4LNS5QDP_cjs = require('../chunk-4LNS5QDP.cjs');
|
|
9
10
|
var chunkO5HS7ZND_cjs = require('../chunk-O5HS7ZND.cjs');
|
|
10
|
-
var chunkP52GA3GJ_cjs = require('../chunk-P52GA3GJ.cjs');
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
Object.defineProperty(exports, "brands", {
|
|
15
15
|
enumerable: true,
|
|
16
|
-
get: function () { return
|
|
16
|
+
get: function () { return chunkCLJZHS7Y_cjs.brands; }
|
|
17
|
+
});
|
|
18
|
+
Object.defineProperty(exports, "enterpriseHealthBrand", {
|
|
19
|
+
enumerable: true,
|
|
20
|
+
get: function () { return chunkP52GA3GJ_cjs.enterpriseHealthBrand; }
|
|
17
21
|
});
|
|
18
22
|
Object.defineProperty(exports, "miewebBrand", {
|
|
19
23
|
enumerable: true,
|
|
@@ -47,9 +51,5 @@ Object.defineProperty(exports, "defaultBrand", {
|
|
|
47
51
|
enumerable: true,
|
|
48
52
|
get: function () { return chunkO5HS7ZND_cjs.defaultBrand; }
|
|
49
53
|
});
|
|
50
|
-
Object.defineProperty(exports, "enterpriseHealthBrand", {
|
|
51
|
-
enumerable: true,
|
|
52
|
-
get: function () { return chunkP52GA3GJ_cjs.enterpriseHealthBrand; }
|
|
53
|
-
});
|
|
54
54
|
//# sourceMappingURL=index.cjs.map
|
|
55
55
|
//# sourceMappingURL=index.cjs.map
|
package/dist/brands/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
export { brands } from '../chunk-
|
|
1
|
+
export { brands } from '../chunk-S4DK5WN6.js';
|
|
2
|
+
export { enterpriseHealthBrand } from '../chunk-MTZPVOP6.js';
|
|
2
3
|
export { miewebBrand } from '../chunk-UHSPAFY6.js';
|
|
3
4
|
export { wagglelineBrand } from '../chunk-OWPWP46L.js';
|
|
4
5
|
export { webchartBrand } from '../chunk-C6MDPPPL.js';
|
|
5
6
|
export { createBrandPreset, generateBrandCSS, generateTailwindTheme } from '../chunk-SOFX4T7M.js';
|
|
6
7
|
export { bluehiveBrand } from '../chunk-ULOA7WBW.js';
|
|
7
8
|
export { defaultBrand } from '../chunk-4LTN2LEN.js';
|
|
8
|
-
export { enterpriseHealthBrand } from '../chunk-MTZPVOP6.js';
|
|
9
9
|
//# sourceMappingURL=index.js.map
|
|
10
10
|
//# sourceMappingURL=index.js.map
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
+
import { isStorybookDocsMode } from './chunk-VSQF22GL.js';
|
|
1
2
|
import { useRef, useEffect } from 'react';
|
|
2
3
|
|
|
3
|
-
// src/hooks/useFocusTrap.ts
|
|
4
4
|
function useFocusTrap(enabled = true) {
|
|
5
5
|
const containerRef = useRef(null);
|
|
6
6
|
useEffect(() => {
|
|
7
|
-
if (!enabled || !containerRef.current) return;
|
|
7
|
+
if (!enabled || !containerRef.current || isStorybookDocsMode()) return;
|
|
8
8
|
const container = containerRef.current;
|
|
9
9
|
const focusableElements = container.querySelectorAll(
|
|
10
10
|
'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])'
|
|
@@ -34,5 +34,5 @@ function useFocusTrap(enabled = true) {
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
export { useFocusTrap };
|
|
37
|
-
//# sourceMappingURL=chunk-
|
|
38
|
-
//# sourceMappingURL=chunk-
|
|
37
|
+
//# sourceMappingURL=chunk-4SMSH4OY.js.map
|
|
38
|
+
//# sourceMappingURL=chunk-4SMSH4OY.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/hooks/useFocusTrap.ts"],"names":[],"mappings":";;;AAuBO,SAAS,YAAA,CACd,UAAU,IAAA,EACW;AACrB,EAAA,MAAM,YAAA,GAAe,OAAU,IAAI,CAAA;AAEnC,EAAA,SAAA,CAAU,MAAM;AAEd,IAAA,IAAI,CAAC,OAAA,IAAW,CAAC,YAAA,CAAa,OAAA,IAAW,qBAAoB,EAAG;AAEhE,IAAA,MAAM,YAAY,YAAA,CAAa,OAAA;AAC/B,IAAA,MAAM,oBAAoB,SAAA,CAAU,gBAAA;AAAA,MAClC;AAAA,KACF;AAEA,IAAA,IAAI,iBAAA,CAAkB,WAAW,CAAA,EAAG;AAEpC,IAAA,MAAM,YAAA,GAAe,kBAAkB,CAAC,CAAA;AACxC,IAAA,MAAM,WAAA,GAAc,iBAAA,CAAkB,iBAAA,CAAkB,MAAA,GAAS,CAAC,CAAA;AAGlE,IAAA,YAAA,CAAa,KAAA,EAAM;AAEnB,IAAA,MAAM,aAAA,GAAgB,CAAC,KAAA,KAAoC;AACzD,MAAA,IAAI,KAAA,CAAM,QAAQ,KAAA,EAAO;AAEzB,MAAA,IAAI,MAAM,QAAA,EAAU;AAElB,QAAA,IAAI,QAAA,CAAS,kBAAkB,YAAA,EAAc;AAC3C,UAAA,KAAA,CAAM,cAAA,EAAe;AACrB,UAAA,WAAA,CAAY,KAAA,EAAM;AAAA,QACpB;AAAA,MACF,CAAA,MAAO;AAEL,QAAA,IAAI,QAAA,CAAS,kBAAkB,WAAA,EAAa;AAC1C,UAAA,KAAA,CAAM,cAAA,EAAe;AACrB,UAAA,YAAA,CAAa,KAAA,EAAM;AAAA,QACrB;AAAA,MACF;AAAA,IACF,CAAA;AAEA,IAAA,SAAA,CAAU,gBAAA,CAAiB,WAAW,aAAa,CAAA;AACnD,IAAA,OAAO,MAAM,SAAA,CAAU,mBAAA,CAAoB,SAAA,EAAW,aAAa,CAAA;AAAA,EACrE,CAAA,EAAG,CAAC,OAAO,CAAC,CAAA;AAEZ,EAAA,OAAO,YAAA;AACT","file":"chunk-4SMSH4OY.js","sourcesContent":["import { useEffect, useRef, type RefObject } from 'react';\nimport { isStorybookDocsMode } from '../utils/environment';\n\n/**\n * Hook that traps focus within a container element.\n * Essential for accessibility in modals and dialogs.\n *\n * @param enabled - Whether focus trapping is active\n * @returns ref to attach to the container element\n *\n * @example\n * ```tsx\n * function Modal({ isOpen }: { isOpen: boolean }) {\n * const containerRef = useFocusTrap<HTMLDivElement>(isOpen);\n * return (\n * <div ref={containerRef} role=\"dialog\" aria-modal=\"true\">\n * <button>First focusable</button>\n * <button>Last focusable</button>\n * </div>\n * );\n * }\n * ```\n */\nexport function useFocusTrap<T extends HTMLElement>(\n enabled = true\n): RefObject<T | null> {\n const containerRef = useRef<T>(null);\n\n useEffect(() => {\n // Skip focus trap in Storybook docs mode to prevent auto-scroll\n if (!enabled || !containerRef.current || isStorybookDocsMode()) return;\n\n const container = containerRef.current;\n const focusableElements = container.querySelectorAll<HTMLElement>(\n 'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])'\n );\n\n if (focusableElements.length === 0) return;\n\n const firstElement = focusableElements[0];\n const lastElement = focusableElements[focusableElements.length - 1];\n\n // Focus the first element when trap is enabled\n firstElement.focus();\n\n const handleKeyDown = (event: globalThis.KeyboardEvent) => {\n if (event.key !== 'Tab') return;\n\n if (event.shiftKey) {\n // Shift + Tab\n if (document.activeElement === firstElement) {\n event.preventDefault();\n lastElement.focus();\n }\n } else {\n // Tab\n if (document.activeElement === lastElement) {\n event.preventDefault();\n firstElement.focus();\n }\n }\n };\n\n container.addEventListener('keydown', handleKeyDown);\n return () => container.removeEventListener('keydown', handleKeyDown);\n }, [enabled]);\n\n return containerRef;\n}\n"]}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { cn } from './chunk-F3SOEIN2.js';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import { cva } from 'class-variance-authority';
|
|
4
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
5
|
+
|
|
6
|
+
var sliderTrackVariants = cva(
|
|
7
|
+
[
|
|
8
|
+
"relative w-full overflow-hidden rounded-full",
|
|
9
|
+
"bg-neutral-200 dark:bg-neutral-700",
|
|
10
|
+
"cursor-pointer",
|
|
11
|
+
"group-data-[disabled=true]:cursor-not-allowed group-data-[disabled=true]:opacity-50"
|
|
12
|
+
],
|
|
13
|
+
{
|
|
14
|
+
variants: {
|
|
15
|
+
size: {
|
|
16
|
+
sm: "h-1",
|
|
17
|
+
md: "h-2",
|
|
18
|
+
lg: "h-3"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
defaultVariants: {
|
|
22
|
+
size: "md"
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
);
|
|
26
|
+
var sliderRangeVariants = cva(
|
|
27
|
+
["absolute h-full rounded-full transition-all duration-75 ease-out"],
|
|
28
|
+
{
|
|
29
|
+
variants: {
|
|
30
|
+
variant: {
|
|
31
|
+
default: "bg-primary-500",
|
|
32
|
+
success: "bg-green-500",
|
|
33
|
+
warning: "bg-yellow-500",
|
|
34
|
+
danger: "bg-red-500",
|
|
35
|
+
neutral: "bg-neutral-500"
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
defaultVariants: {
|
|
39
|
+
variant: "default"
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
);
|
|
43
|
+
var sliderThumbVariants = cva(
|
|
44
|
+
[
|
|
45
|
+
"absolute top-1/2 -translate-y-1/2 -translate-x-1/2",
|
|
46
|
+
"rounded-full border-2 bg-white",
|
|
47
|
+
"shadow-md transition-shadow duration-150",
|
|
48
|
+
"peer-focus-visible:outline-none peer-focus-visible:ring-2 peer-focus-visible:ring-ring peer-focus-visible:ring-offset-2",
|
|
49
|
+
"peer-hover:shadow-lg",
|
|
50
|
+
"peer-active:shadow-xl peer-active:scale-110",
|
|
51
|
+
"group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50"
|
|
52
|
+
],
|
|
53
|
+
{
|
|
54
|
+
variants: {
|
|
55
|
+
size: {
|
|
56
|
+
sm: "h-3.5 w-3.5",
|
|
57
|
+
md: "h-5 w-5",
|
|
58
|
+
lg: "h-6 w-6"
|
|
59
|
+
},
|
|
60
|
+
variant: {
|
|
61
|
+
default: "border-primary-500",
|
|
62
|
+
success: "border-green-500",
|
|
63
|
+
warning: "border-yellow-500",
|
|
64
|
+
danger: "border-red-500",
|
|
65
|
+
neutral: "border-neutral-500"
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
defaultVariants: {
|
|
69
|
+
size: "md",
|
|
70
|
+
variant: "default"
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
);
|
|
74
|
+
var Slider = React.forwardRef(
|
|
75
|
+
({
|
|
76
|
+
value: controlledValue,
|
|
77
|
+
defaultValue = 0,
|
|
78
|
+
min = 0,
|
|
79
|
+
max = 100,
|
|
80
|
+
step = 1,
|
|
81
|
+
onValueChange,
|
|
82
|
+
onValueCommit,
|
|
83
|
+
disabled = false,
|
|
84
|
+
label,
|
|
85
|
+
showValue = false,
|
|
86
|
+
formatValue,
|
|
87
|
+
description,
|
|
88
|
+
minLabel,
|
|
89
|
+
maxLabel,
|
|
90
|
+
variant,
|
|
91
|
+
size,
|
|
92
|
+
className,
|
|
93
|
+
trackClassName,
|
|
94
|
+
id,
|
|
95
|
+
name,
|
|
96
|
+
"aria-label": ariaLabelProp,
|
|
97
|
+
"aria-labelledby": ariaLabelledByProp
|
|
98
|
+
}, ref) => {
|
|
99
|
+
const hasExplicitLabel = !!label;
|
|
100
|
+
const ariaLabelledBy = ariaLabelledByProp;
|
|
101
|
+
const ariaLabel = ariaLabelProp ?? (!hasExplicitLabel && !ariaLabelledByProp ? "Slider" : void 0);
|
|
102
|
+
const [uncontrolledValue, setUncontrolledValue] = React.useState(defaultValue);
|
|
103
|
+
const isControlled = controlledValue !== void 0;
|
|
104
|
+
const currentValue = isControlled ? controlledValue : uncontrolledValue;
|
|
105
|
+
const clampedValue = Math.min(Math.max(currentValue, min), max);
|
|
106
|
+
const percentage = max !== min ? (clampedValue - min) / (max - min) * 100 : 0;
|
|
107
|
+
const generatedId = React.useId();
|
|
108
|
+
const inputId = id ?? generatedId;
|
|
109
|
+
const handleChange = (e) => {
|
|
110
|
+
const newValue = parseFloat(e.target.value);
|
|
111
|
+
if (!isControlled) {
|
|
112
|
+
setUncontrolledValue(newValue);
|
|
113
|
+
}
|
|
114
|
+
onValueChange?.(newValue);
|
|
115
|
+
};
|
|
116
|
+
const handleCommit = () => {
|
|
117
|
+
onValueCommit?.(clampedValue);
|
|
118
|
+
};
|
|
119
|
+
const displayValue = formatValue ? formatValue(clampedValue) : String(clampedValue);
|
|
120
|
+
return /* @__PURE__ */ jsxs(
|
|
121
|
+
"div",
|
|
122
|
+
{
|
|
123
|
+
className: cn("w-full", className),
|
|
124
|
+
"data-disabled": disabled || void 0,
|
|
125
|
+
children: [
|
|
126
|
+
(label || showValue) && /* @__PURE__ */ jsxs("div", { className: "mb-1.5 flex items-baseline justify-between", children: [
|
|
127
|
+
label && /* @__PURE__ */ jsxs(
|
|
128
|
+
"label",
|
|
129
|
+
{
|
|
130
|
+
htmlFor: inputId,
|
|
131
|
+
className: cn(
|
|
132
|
+
"text-foreground text-sm font-medium",
|
|
133
|
+
disabled && "opacity-50"
|
|
134
|
+
),
|
|
135
|
+
children: [
|
|
136
|
+
label,
|
|
137
|
+
showValue && /* @__PURE__ */ jsx("span", { className: "text-muted-foreground ml-1", children: displayValue })
|
|
138
|
+
]
|
|
139
|
+
}
|
|
140
|
+
),
|
|
141
|
+
!label && showValue && /* @__PURE__ */ jsx("span", { className: "text-muted-foreground text-sm", children: displayValue })
|
|
142
|
+
] }),
|
|
143
|
+
description && /* @__PURE__ */ jsx(
|
|
144
|
+
"p",
|
|
145
|
+
{
|
|
146
|
+
className: cn(
|
|
147
|
+
"text-muted-foreground mb-2 text-xs",
|
|
148
|
+
disabled && "opacity-50"
|
|
149
|
+
),
|
|
150
|
+
children: description
|
|
151
|
+
}
|
|
152
|
+
),
|
|
153
|
+
/* @__PURE__ */ jsxs("div", { className: "group relative", "data-disabled": disabled || void 0, children: [
|
|
154
|
+
/* @__PURE__ */ jsx("div", { className: cn(sliderTrackVariants({ size }), trackClassName), children: /* @__PURE__ */ jsx(
|
|
155
|
+
"div",
|
|
156
|
+
{
|
|
157
|
+
className: sliderRangeVariants({ variant }),
|
|
158
|
+
style: { width: `${percentage}%` }
|
|
159
|
+
}
|
|
160
|
+
) }),
|
|
161
|
+
/* @__PURE__ */ jsx(
|
|
162
|
+
"input",
|
|
163
|
+
{
|
|
164
|
+
ref,
|
|
165
|
+
type: "range",
|
|
166
|
+
className: cn(
|
|
167
|
+
"peer absolute inset-0 h-full w-full cursor-pointer opacity-0",
|
|
168
|
+
disabled && "cursor-not-allowed"
|
|
169
|
+
),
|
|
170
|
+
id: inputId,
|
|
171
|
+
name,
|
|
172
|
+
min,
|
|
173
|
+
max,
|
|
174
|
+
step,
|
|
175
|
+
value: clampedValue,
|
|
176
|
+
onChange: handleChange,
|
|
177
|
+
onMouseUp: handleCommit,
|
|
178
|
+
onTouchEnd: handleCommit,
|
|
179
|
+
onKeyUp: handleCommit,
|
|
180
|
+
onBlur: handleCommit,
|
|
181
|
+
disabled,
|
|
182
|
+
"aria-label": ariaLabel,
|
|
183
|
+
"aria-labelledby": ariaLabelledBy,
|
|
184
|
+
"aria-valuemin": min,
|
|
185
|
+
"aria-valuemax": max,
|
|
186
|
+
"aria-valuenow": clampedValue
|
|
187
|
+
}
|
|
188
|
+
),
|
|
189
|
+
/* @__PURE__ */ jsx(
|
|
190
|
+
"div",
|
|
191
|
+
{
|
|
192
|
+
className: sliderThumbVariants({ size, variant }),
|
|
193
|
+
style: { left: `${percentage}%` },
|
|
194
|
+
"aria-hidden": "true"
|
|
195
|
+
}
|
|
196
|
+
)
|
|
197
|
+
] }),
|
|
198
|
+
(minLabel || maxLabel) && /* @__PURE__ */ jsxs(
|
|
199
|
+
"div",
|
|
200
|
+
{
|
|
201
|
+
className: cn(
|
|
202
|
+
"text-muted-foreground mt-1 flex justify-between text-xs",
|
|
203
|
+
disabled && "opacity-50"
|
|
204
|
+
),
|
|
205
|
+
children: [
|
|
206
|
+
/* @__PURE__ */ jsx("span", { children: minLabel }),
|
|
207
|
+
/* @__PURE__ */ jsx("span", { children: maxLabel })
|
|
208
|
+
]
|
|
209
|
+
}
|
|
210
|
+
)
|
|
211
|
+
]
|
|
212
|
+
}
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
);
|
|
216
|
+
Slider.displayName = "Slider";
|
|
217
|
+
|
|
218
|
+
export { Slider, sliderRangeVariants, sliderThumbVariants, sliderTrackVariants };
|
|
219
|
+
//# sourceMappingURL=chunk-4T2ZNPTC.js.map
|
|
220
|
+
//# sourceMappingURL=chunk-4T2ZNPTC.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/Slider/Slider.tsx"],"names":[],"mappings":"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","file":"chunk-4T2ZNPTC.js","sourcesContent":["import * as React from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { cn } from '../../utils/cn';\n\n// ============================================================================\n// Slider Variants\n// ============================================================================\n\nconst sliderTrackVariants = cva(\n [\n 'relative w-full overflow-hidden rounded-full',\n 'bg-neutral-200 dark:bg-neutral-700',\n 'cursor-pointer',\n 'group-data-[disabled=true]:cursor-not-allowed group-data-[disabled=true]:opacity-50',\n ],\n {\n variants: {\n size: {\n sm: 'h-1',\n md: 'h-2',\n lg: 'h-3',\n },\n },\n defaultVariants: {\n size: 'md',\n },\n }\n);\n\nconst sliderRangeVariants = cva(\n ['absolute h-full rounded-full transition-all duration-75 ease-out'],\n {\n variants: {\n variant: {\n default: 'bg-primary-500',\n success: 'bg-green-500',\n warning: 'bg-yellow-500',\n danger: 'bg-red-500',\n neutral: 'bg-neutral-500',\n },\n },\n defaultVariants: {\n variant: 'default',\n },\n }\n);\n\nconst sliderThumbVariants = cva(\n [\n 'absolute top-1/2 -translate-y-1/2 -translate-x-1/2',\n 'rounded-full border-2 bg-white',\n 'shadow-md transition-shadow duration-150',\n 'peer-focus-visible:outline-none peer-focus-visible:ring-2 peer-focus-visible:ring-ring peer-focus-visible:ring-offset-2',\n 'peer-hover:shadow-lg',\n 'peer-active:shadow-xl peer-active:scale-110',\n 'group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50',\n ],\n {\n variants: {\n size: {\n sm: 'h-3.5 w-3.5',\n md: 'h-5 w-5',\n lg: 'h-6 w-6',\n },\n variant: {\n default: 'border-primary-500',\n success: 'border-green-500',\n warning: 'border-yellow-500',\n danger: 'border-red-500',\n neutral: 'border-neutral-500',\n },\n },\n defaultVariants: {\n size: 'md',\n variant: 'default',\n },\n }\n);\n\n// ============================================================================\n// Slider Component\n// ============================================================================\n\nexport interface SliderProps\n extends\n VariantProps<typeof sliderTrackVariants>,\n VariantProps<typeof sliderRangeVariants> {\n /** Current value (controlled) */\n value?: number;\n /** Default value (uncontrolled) */\n defaultValue?: number;\n /** Minimum value */\n min?: number;\n /** Maximum value */\n max?: number;\n /** Step increment */\n step?: number;\n /** Callback when value changes */\n onValueChange?: (value: number) => void;\n /** Callback when interaction ends (mouseup / touchend) */\n onValueCommit?: (value: number) => void;\n /** Whether the slider is disabled */\n disabled?: boolean;\n /** Label for the slider */\n label?: string;\n /** Show the current value */\n showValue?: boolean;\n /** Format the displayed value */\n formatValue?: (value: number) => string;\n /** Description text below the label */\n description?: string;\n /** Min label displayed below the track (left) */\n minLabel?: string;\n /** Max label displayed below the track (right) */\n maxLabel?: string;\n /** Additional class name for the root container */\n className?: string;\n /** Additional class name for the track */\n trackClassName?: string;\n /** ID for the underlying input */\n id?: string;\n /** Name for form submission */\n name?: string;\n /** Accessible label for the slider */\n 'aria-label'?: string;\n /** ID of the element that labels the slider */\n 'aria-labelledby'?: string;\n}\n\n/**\n * A fully branded, accessible slider/range input component.\n *\n * Uses brand design tokens for colors, border-radius, and sizing.\n * Supports controlled and uncontrolled usage, labels, descriptions,\n * min/max labels, value display, and multiple color variants.\n *\n * @example\n * ```tsx\n * <Slider label=\"Volume\" min={0} max={100} defaultValue={50} />\n * <Slider\n * label=\"Border Radius\"\n * min={0}\n * max={32}\n * value={radius}\n * onValueChange={setRadius}\n * showValue\n * formatValue={(v) => `${v}px`}\n * minLabel=\"Square\"\n * maxLabel=\"Rounded\"\n * />\n * ```\n */\nconst Slider = React.forwardRef<HTMLInputElement, SliderProps>(\n (\n {\n value: controlledValue,\n defaultValue = 0,\n min = 0,\n max = 100,\n step = 1,\n onValueChange,\n onValueCommit,\n disabled = false,\n label,\n showValue = false,\n formatValue,\n description,\n minLabel,\n maxLabel,\n variant,\n size,\n className,\n trackClassName,\n id,\n name,\n 'aria-label': ariaLabelProp,\n 'aria-labelledby': ariaLabelledByProp,\n },\n ref\n ) => {\n const hasExplicitLabel = !!label;\n const ariaLabelledBy = ariaLabelledByProp;\n const ariaLabel =\n ariaLabelProp ??\n (!hasExplicitLabel && !ariaLabelledByProp ? 'Slider' : undefined);\n const [uncontrolledValue, setUncontrolledValue] =\n React.useState(defaultValue);\n const isControlled = controlledValue !== undefined;\n const currentValue = isControlled ? controlledValue : uncontrolledValue;\n\n // Clamp value to min/max\n const clampedValue = Math.min(Math.max(currentValue, min), max);\n\n // Percentage for visual fill\n const percentage =\n max !== min ? ((clampedValue - min) / (max - min)) * 100 : 0;\n\n const generatedId = React.useId();\n const inputId = id ?? generatedId;\n\n const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const newValue = parseFloat(e.target.value);\n if (!isControlled) {\n setUncontrolledValue(newValue);\n }\n onValueChange?.(newValue);\n };\n\n const handleCommit = () => {\n onValueCommit?.(clampedValue);\n };\n\n const displayValue = formatValue\n ? formatValue(clampedValue)\n : String(clampedValue);\n\n return (\n <div\n className={cn('w-full', className)}\n data-disabled={disabled || undefined}\n >\n {/* Label row */}\n {(label || showValue) && (\n <div className=\"mb-1.5 flex items-baseline justify-between\">\n {label && (\n <label\n htmlFor={inputId}\n className={cn(\n 'text-foreground text-sm font-medium',\n disabled && 'opacity-50'\n )}\n >\n {label}\n {showValue && (\n <span className=\"text-muted-foreground ml-1\">\n {displayValue}\n </span>\n )}\n </label>\n )}\n {!label && showValue && (\n <span className=\"text-muted-foreground text-sm\">\n {displayValue}\n </span>\n )}\n </div>\n )}\n\n {/* Description */}\n {description && (\n <p\n className={cn(\n 'text-muted-foreground mb-2 text-xs',\n disabled && 'opacity-50'\n )}\n >\n {description}\n </p>\n )}\n\n {/* Track + Thumb */}\n <div className=\"group relative\" data-disabled={disabled || undefined}>\n {/* Visual track background */}\n <div className={cn(sliderTrackVariants({ size }), trackClassName)}>\n {/* Filled range */}\n <div\n className={sliderRangeVariants({ variant })}\n style={{ width: `${percentage}%` }}\n />\n </div>\n\n {/* Native range input — stretched to fill, made invisible */}\n <input\n ref={ref}\n type=\"range\"\n className={cn(\n 'peer absolute inset-0 h-full w-full cursor-pointer opacity-0',\n disabled && 'cursor-not-allowed'\n )}\n id={inputId}\n name={name}\n min={min}\n max={max}\n step={step}\n value={clampedValue}\n onChange={handleChange}\n onMouseUp={handleCommit}\n onTouchEnd={handleCommit}\n onKeyUp={handleCommit}\n onBlur={handleCommit}\n disabled={disabled}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy}\n aria-valuemin={min}\n aria-valuemax={max}\n aria-valuenow={clampedValue}\n />\n\n {/* Thumb indicator (visual only) */}\n <div\n className={sliderThumbVariants({ size, variant })}\n style={{ left: `${percentage}%` }}\n aria-hidden=\"true\"\n />\n </div>\n\n {/* Min / Max labels */}\n {(minLabel || maxLabel) && (\n <div\n className={cn(\n 'text-muted-foreground mt-1 flex justify-between text-xs',\n disabled && 'opacity-50'\n )}\n >\n <span>{minLabel}</span>\n <span>{maxLabel}</span>\n </div>\n )}\n </div>\n );\n }\n);\n\nSlider.displayName = 'Slider';\n\nexport {\n Slider,\n sliderTrackVariants,\n sliderRangeVariants,\n sliderThumbVariants,\n};\n"]}
|