floaty-widget 0.1.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 +21 -0
- package/README.md +198 -0
- package/dist/floaty-widget.js +384 -0
- package/dist/floaty-widget.umd.cjs +22 -0
- package/dist/style.css +1 -0
- package/package.json +41 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 el_jijuna
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
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:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
# Floaty Widget
|
|
2
|
+
|
|
3
|
+
A beautiful, draggable, and collapsible floating widget library built with React 19, TypeScript, and Vite.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
✨ **Draggable Header** - Click and drag the header to move the component anywhere on the screen
|
|
8
|
+
📍 **Pin/Unpin** - Lock the component in place with the pin button to prevent dragging
|
|
9
|
+
🔽 **Expand/Collapse** - Smooth animations when toggling content visibility
|
|
10
|
+
🎨 **Customizable** - Flexible styling with CSS variables and inline styles
|
|
11
|
+
📱 **Responsive** - Works seamlessly on different screen sizes
|
|
12
|
+
♿ **Accessible** - Built with ARIA labels and semantic HTML
|
|
13
|
+
🚀 **Optimized** - Tree-shaking enabled, minimal bundle size
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install floaty-widget
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Usage
|
|
22
|
+
|
|
23
|
+
```tsx
|
|
24
|
+
import { Floaty } from 'floaty-widget';
|
|
25
|
+
import 'floaty-widget/dist/style.css';
|
|
26
|
+
|
|
27
|
+
export function App() {
|
|
28
|
+
return (
|
|
29
|
+
<Floaty title="My Panel">
|
|
30
|
+
<p>Your content here</p>
|
|
31
|
+
</Floaty>
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Props
|
|
37
|
+
|
|
38
|
+
All props are optional.
|
|
39
|
+
|
|
40
|
+
```tsx
|
|
41
|
+
interface FloatyProps {
|
|
42
|
+
/** Content to display inside the floaty body */
|
|
43
|
+
children?: ReactNode;
|
|
44
|
+
|
|
45
|
+
/** Header title text */
|
|
46
|
+
title?: string;
|
|
47
|
+
|
|
48
|
+
/** Additional inline styles */
|
|
49
|
+
style?: CSSProperties;
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Default Values
|
|
54
|
+
|
|
55
|
+
- `children`: `'Content'`
|
|
56
|
+
- `title`: `'Floaty'`
|
|
57
|
+
- `style`: `{}`
|
|
58
|
+
|
|
59
|
+
## Examples
|
|
60
|
+
|
|
61
|
+
### Basic Usage
|
|
62
|
+
|
|
63
|
+
```tsx
|
|
64
|
+
<Floaty title="Dashboard">
|
|
65
|
+
<div>
|
|
66
|
+
<h3>Welcome!</h3>
|
|
67
|
+
<p>This is a floating panel.</p>
|
|
68
|
+
</div>
|
|
69
|
+
</Floaty>
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### With Custom Styling
|
|
73
|
+
|
|
74
|
+
```tsx
|
|
75
|
+
<Floaty
|
|
76
|
+
title="Settings"
|
|
77
|
+
style={{ width: '400px' }}
|
|
78
|
+
>
|
|
79
|
+
<form>
|
|
80
|
+
{/* Your form content */}
|
|
81
|
+
</form>
|
|
82
|
+
</Floaty>
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### With Complex Content
|
|
86
|
+
|
|
87
|
+
```tsx
|
|
88
|
+
<Floaty title="User Info">
|
|
89
|
+
<div>
|
|
90
|
+
<img src="avatar.jpg" alt="User" />
|
|
91
|
+
<h4>John Doe</h4>
|
|
92
|
+
<p>john@example.com</p>
|
|
93
|
+
<button>View Profile</button>
|
|
94
|
+
</div>
|
|
95
|
+
</Floaty>
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Features in Detail
|
|
99
|
+
|
|
100
|
+
### 🖱️ Dragging
|
|
101
|
+
|
|
102
|
+
Click and hold the header to drag the component around. The component will stay within viewport boundaries automatically.
|
|
103
|
+
|
|
104
|
+
### 📍 Pin/Unpin
|
|
105
|
+
|
|
106
|
+
Click the pin icon (📍) in the header to lock the component. When pinned (📌), the component cannot be dragged but can still be collapsed/expanded.
|
|
107
|
+
|
|
108
|
+
### ➖ Expand/Collapse
|
|
109
|
+
|
|
110
|
+
Click the chevron icon in the top-right to toggle content visibility. The animation is smooth with spring easing.
|
|
111
|
+
|
|
112
|
+
## Styling
|
|
113
|
+
|
|
114
|
+
The component uses CSS variables that you can customize:
|
|
115
|
+
|
|
116
|
+
```css
|
|
117
|
+
:root {
|
|
118
|
+
--primary-color: #4f46e5;
|
|
119
|
+
--primary-hover: #4338ca;
|
|
120
|
+
--border-color: #e5e7eb;
|
|
121
|
+
--shadow: 0 10px 25px rgba(0, 0, 0, 0.1);
|
|
122
|
+
--transition: all 0.3s ease;
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Development
|
|
127
|
+
|
|
128
|
+
### Setup
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
npm install
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Development Server
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
npm run dev
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Storybook
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
npm run storybook
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Open [http://localhost:6006](http://localhost:6006) to view the component stories.
|
|
147
|
+
|
|
148
|
+
### Build
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
npm run build
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Generates:
|
|
155
|
+
- ES module: `dist/floaty-widget.js`
|
|
156
|
+
- UMD bundle: `dist/floaty-widget.umd.cjs`
|
|
157
|
+
- CSS: `dist/style.css`
|
|
158
|
+
|
|
159
|
+
## Browser Support
|
|
160
|
+
|
|
161
|
+
- Chrome (latest)
|
|
162
|
+
- Firefox (latest)
|
|
163
|
+
- Safari (latest)
|
|
164
|
+
- Edge (latest)
|
|
165
|
+
|
|
166
|
+
## Performance
|
|
167
|
+
|
|
168
|
+
- **Optimized dragging** with global event listeners to prevent jank
|
|
169
|
+
- **Tree-shaking enabled** for smaller bundle sizes
|
|
170
|
+
- **CSS animations** for smooth expand/collapse transitions
|
|
171
|
+
- **Viewport constraints** to prevent layout shifts
|
|
172
|
+
|
|
173
|
+
## Accessibility
|
|
174
|
+
|
|
175
|
+
- ♿ ARIA labels on interactive elements
|
|
176
|
+
- ⌨️ Keyboard accessible buttons
|
|
177
|
+
- 📱 Touch-friendly hit targets
|
|
178
|
+
- 🎯 Semantic HTML structure
|
|
179
|
+
|
|
180
|
+
## TypeScript Support
|
|
181
|
+
|
|
182
|
+
Full TypeScript support with exported types:
|
|
183
|
+
|
|
184
|
+
```tsx
|
|
185
|
+
import { Floaty, FloatyProps } from 'floaty-widget';
|
|
186
|
+
|
|
187
|
+
const MyComponent: React.FC<FloatyProps> = (props) => {
|
|
188
|
+
return <Floaty {...props} />;
|
|
189
|
+
};
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## License
|
|
193
|
+
|
|
194
|
+
MIT © 2024
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
**Built with** ❤️ using React 19, TypeScript, and Vite
|
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
import re, { useState as M, useRef as V, useEffect as te } from "react";
|
|
2
|
+
var D = { exports: {} }, x = {};
|
|
3
|
+
/**
|
|
4
|
+
* @license React
|
|
5
|
+
* react-jsx-runtime.production.js
|
|
6
|
+
*
|
|
7
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
8
|
+
*
|
|
9
|
+
* This source code is licensed under the MIT license found in the
|
|
10
|
+
* LICENSE file in the root directory of this source tree.
|
|
11
|
+
*/
|
|
12
|
+
var z;
|
|
13
|
+
function ne() {
|
|
14
|
+
if (z) return x;
|
|
15
|
+
z = 1;
|
|
16
|
+
var s = Symbol.for("react.transitional.element"), h = Symbol.for("react.fragment");
|
|
17
|
+
function v(c, l, a) {
|
|
18
|
+
var E = null;
|
|
19
|
+
if (a !== void 0 && (E = "" + a), l.key !== void 0 && (E = "" + l.key), "key" in l) {
|
|
20
|
+
a = {};
|
|
21
|
+
for (var d in l)
|
|
22
|
+
d !== "key" && (a[d] = l[d]);
|
|
23
|
+
} else a = l;
|
|
24
|
+
return l = a.ref, {
|
|
25
|
+
$$typeof: s,
|
|
26
|
+
type: c,
|
|
27
|
+
key: E,
|
|
28
|
+
ref: l !== void 0 ? l : null,
|
|
29
|
+
props: a
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
return x.Fragment = h, x.jsx = v, x.jsxs = v, x;
|
|
33
|
+
}
|
|
34
|
+
var y = {};
|
|
35
|
+
/**
|
|
36
|
+
* @license React
|
|
37
|
+
* react-jsx-runtime.development.js
|
|
38
|
+
*
|
|
39
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
40
|
+
*
|
|
41
|
+
* This source code is licensed under the MIT license found in the
|
|
42
|
+
* LICENSE file in the root directory of this source tree.
|
|
43
|
+
*/
|
|
44
|
+
var q;
|
|
45
|
+
function oe() {
|
|
46
|
+
return q || (q = 1, process.env.NODE_ENV !== "production" && function() {
|
|
47
|
+
function s(e) {
|
|
48
|
+
if (e == null) return null;
|
|
49
|
+
if (typeof e == "function")
|
|
50
|
+
return e.$$typeof === Q ? null : e.displayName || e.name || null;
|
|
51
|
+
if (typeof e == "string") return e;
|
|
52
|
+
switch (e) {
|
|
53
|
+
case b:
|
|
54
|
+
return "Fragment";
|
|
55
|
+
case S:
|
|
56
|
+
return "Profiler";
|
|
57
|
+
case T:
|
|
58
|
+
return "StrictMode";
|
|
59
|
+
case G:
|
|
60
|
+
return "Suspense";
|
|
61
|
+
case J:
|
|
62
|
+
return "SuspenseList";
|
|
63
|
+
case Z:
|
|
64
|
+
return "Activity";
|
|
65
|
+
}
|
|
66
|
+
if (typeof e == "object")
|
|
67
|
+
switch (typeof e.tag == "number" && console.error(
|
|
68
|
+
"Received an unexpected object in getComponentNameFromType(). This is likely a bug in React. Please file an issue."
|
|
69
|
+
), e.$$typeof) {
|
|
70
|
+
case g:
|
|
71
|
+
return "Portal";
|
|
72
|
+
case j:
|
|
73
|
+
return e.displayName || "Context";
|
|
74
|
+
case O:
|
|
75
|
+
return (e._context.displayName || "Context") + ".Consumer";
|
|
76
|
+
case B:
|
|
77
|
+
var r = e.render;
|
|
78
|
+
return e = e.displayName, e || (e = r.displayName || r.name || "", e = e !== "" ? "ForwardRef(" + e + ")" : "ForwardRef"), e;
|
|
79
|
+
case H:
|
|
80
|
+
return r = e.displayName || null, r !== null ? r : s(e.type) || "Memo";
|
|
81
|
+
case A:
|
|
82
|
+
r = e._payload, e = e._init;
|
|
83
|
+
try {
|
|
84
|
+
return s(e(r));
|
|
85
|
+
} catch {
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
function h(e) {
|
|
91
|
+
return "" + e;
|
|
92
|
+
}
|
|
93
|
+
function v(e) {
|
|
94
|
+
try {
|
|
95
|
+
h(e);
|
|
96
|
+
var r = !1;
|
|
97
|
+
} catch {
|
|
98
|
+
r = !0;
|
|
99
|
+
}
|
|
100
|
+
if (r) {
|
|
101
|
+
r = console;
|
|
102
|
+
var t = r.error, n = typeof Symbol == "function" && Symbol.toStringTag && e[Symbol.toStringTag] || e.constructor.name || "Object";
|
|
103
|
+
return t.call(
|
|
104
|
+
r,
|
|
105
|
+
"The provided key is an unsupported type %s. This value must be coerced to a string before using it here.",
|
|
106
|
+
n
|
|
107
|
+
), h(e);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
function c(e) {
|
|
111
|
+
if (e === b) return "<>";
|
|
112
|
+
if (typeof e == "object" && e !== null && e.$$typeof === A)
|
|
113
|
+
return "<...>";
|
|
114
|
+
try {
|
|
115
|
+
var r = s(e);
|
|
116
|
+
return r ? "<" + r + ">" : "<...>";
|
|
117
|
+
} catch {
|
|
118
|
+
return "<...>";
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
function l() {
|
|
122
|
+
var e = C.A;
|
|
123
|
+
return e === null ? null : e.getOwner();
|
|
124
|
+
}
|
|
125
|
+
function a() {
|
|
126
|
+
return Error("react-stack-top-frame");
|
|
127
|
+
}
|
|
128
|
+
function E(e) {
|
|
129
|
+
if (I.call(e, "key")) {
|
|
130
|
+
var r = Object.getOwnPropertyDescriptor(e, "key").get;
|
|
131
|
+
if (r && r.isReactWarning) return !1;
|
|
132
|
+
}
|
|
133
|
+
return e.key !== void 0;
|
|
134
|
+
}
|
|
135
|
+
function d(e, r) {
|
|
136
|
+
function t() {
|
|
137
|
+
L || (L = !0, console.error(
|
|
138
|
+
"%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://react.dev/link/special-props)",
|
|
139
|
+
r
|
|
140
|
+
));
|
|
141
|
+
}
|
|
142
|
+
t.isReactWarning = !0, Object.defineProperty(e, "key", {
|
|
143
|
+
get: t,
|
|
144
|
+
configurable: !0
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
function P() {
|
|
148
|
+
var e = s(this.type);
|
|
149
|
+
return F[e] || (F[e] = !0, console.error(
|
|
150
|
+
"Accessing element.ref was removed in React 19. ref is now a regular prop. It will be removed from the JSX Element type in a future release."
|
|
151
|
+
)), e = this.props.ref, e !== void 0 ? e : null;
|
|
152
|
+
}
|
|
153
|
+
function R(e, r, t, n, w, Y) {
|
|
154
|
+
var o = t.ref;
|
|
155
|
+
return e = {
|
|
156
|
+
$$typeof: p,
|
|
157
|
+
type: e,
|
|
158
|
+
key: r,
|
|
159
|
+
props: t,
|
|
160
|
+
_owner: n
|
|
161
|
+
}, (o !== void 0 ? o : null) !== null ? Object.defineProperty(e, "ref", {
|
|
162
|
+
enumerable: !1,
|
|
163
|
+
get: P
|
|
164
|
+
}) : Object.defineProperty(e, "ref", { enumerable: !1, value: null }), e._store = {}, Object.defineProperty(e._store, "validated", {
|
|
165
|
+
configurable: !1,
|
|
166
|
+
enumerable: !1,
|
|
167
|
+
writable: !0,
|
|
168
|
+
value: 0
|
|
169
|
+
}), Object.defineProperty(e, "_debugInfo", {
|
|
170
|
+
configurable: !1,
|
|
171
|
+
enumerable: !1,
|
|
172
|
+
writable: !0,
|
|
173
|
+
value: null
|
|
174
|
+
}), Object.defineProperty(e, "_debugStack", {
|
|
175
|
+
configurable: !1,
|
|
176
|
+
enumerable: !1,
|
|
177
|
+
writable: !0,
|
|
178
|
+
value: w
|
|
179
|
+
}), Object.defineProperty(e, "_debugTask", {
|
|
180
|
+
configurable: !1,
|
|
181
|
+
enumerable: !1,
|
|
182
|
+
writable: !0,
|
|
183
|
+
value: Y
|
|
184
|
+
}), Object.freeze && (Object.freeze(e.props), Object.freeze(e)), e;
|
|
185
|
+
}
|
|
186
|
+
function f(e, r, t, n, w, Y) {
|
|
187
|
+
var o = r.children;
|
|
188
|
+
if (o !== void 0)
|
|
189
|
+
if (n)
|
|
190
|
+
if (K(o)) {
|
|
191
|
+
for (n = 0; n < o.length; n++)
|
|
192
|
+
k(o[n]);
|
|
193
|
+
Object.freeze && Object.freeze(o);
|
|
194
|
+
} else
|
|
195
|
+
console.error(
|
|
196
|
+
"React.jsx: Static children should always be an array. You are likely explicitly calling React.jsxs or React.jsxDEV. Use the Babel transform instead."
|
|
197
|
+
);
|
|
198
|
+
else k(o);
|
|
199
|
+
if (I.call(r, "key")) {
|
|
200
|
+
o = s(e);
|
|
201
|
+
var _ = Object.keys(r).filter(function(ee) {
|
|
202
|
+
return ee !== "key";
|
|
203
|
+
});
|
|
204
|
+
n = 0 < _.length ? "{key: someKey, " + _.join(": ..., ") + ": ...}" : "{key: someKey}", X[o + n] || (_ = 0 < _.length ? "{" + _.join(": ..., ") + ": ...}" : "{}", console.error(
|
|
205
|
+
`A props object containing a "key" prop is being spread into JSX:
|
|
206
|
+
let props = %s;
|
|
207
|
+
<%s {...props} />
|
|
208
|
+
React keys must be passed directly to JSX without using spread:
|
|
209
|
+
let props = %s;
|
|
210
|
+
<%s key={someKey} {...props} />`,
|
|
211
|
+
n,
|
|
212
|
+
o,
|
|
213
|
+
_,
|
|
214
|
+
o
|
|
215
|
+
), X[o + n] = !0);
|
|
216
|
+
}
|
|
217
|
+
if (o = null, t !== void 0 && (v(t), o = "" + t), E(r) && (v(r.key), o = "" + r.key), "key" in r) {
|
|
218
|
+
t = {};
|
|
219
|
+
for (var $ in r)
|
|
220
|
+
$ !== "key" && (t[$] = r[$]);
|
|
221
|
+
} else t = r;
|
|
222
|
+
return o && d(
|
|
223
|
+
t,
|
|
224
|
+
typeof e == "function" ? e.displayName || e.name || "Unknown" : e
|
|
225
|
+
), R(
|
|
226
|
+
e,
|
|
227
|
+
o,
|
|
228
|
+
t,
|
|
229
|
+
l(),
|
|
230
|
+
w,
|
|
231
|
+
Y
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
function k(e) {
|
|
235
|
+
m(e) ? e._store && (e._store.validated = 1) : typeof e == "object" && e !== null && e.$$typeof === A && (e._payload.status === "fulfilled" ? m(e._payload.value) && e._payload.value._store && (e._payload.value._store.validated = 1) : e._store && (e._store.validated = 1));
|
|
236
|
+
}
|
|
237
|
+
function m(e) {
|
|
238
|
+
return typeof e == "object" && e !== null && e.$$typeof === p;
|
|
239
|
+
}
|
|
240
|
+
var u = re, p = Symbol.for("react.transitional.element"), g = Symbol.for("react.portal"), b = Symbol.for("react.fragment"), T = Symbol.for("react.strict_mode"), S = Symbol.for("react.profiler"), O = Symbol.for("react.consumer"), j = Symbol.for("react.context"), B = Symbol.for("react.forward_ref"), G = Symbol.for("react.suspense"), J = Symbol.for("react.suspense_list"), H = Symbol.for("react.memo"), A = Symbol.for("react.lazy"), Z = Symbol.for("react.activity"), Q = Symbol.for("react.client.reference"), C = u.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE, I = Object.prototype.hasOwnProperty, K = Array.isArray, N = console.createTask ? console.createTask : function() {
|
|
241
|
+
return null;
|
|
242
|
+
};
|
|
243
|
+
u = {
|
|
244
|
+
react_stack_bottom_frame: function(e) {
|
|
245
|
+
return e();
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
var L, F = {}, W = u.react_stack_bottom_frame.bind(
|
|
249
|
+
u,
|
|
250
|
+
a
|
|
251
|
+
)(), U = N(c(a)), X = {};
|
|
252
|
+
y.Fragment = b, y.jsx = function(e, r, t) {
|
|
253
|
+
var n = 1e4 > C.recentlyCreatedOwnerStacks++;
|
|
254
|
+
return f(
|
|
255
|
+
e,
|
|
256
|
+
r,
|
|
257
|
+
t,
|
|
258
|
+
!1,
|
|
259
|
+
n ? Error("react-stack-top-frame") : W,
|
|
260
|
+
n ? N(c(e)) : U
|
|
261
|
+
);
|
|
262
|
+
}, y.jsxs = function(e, r, t) {
|
|
263
|
+
var n = 1e4 > C.recentlyCreatedOwnerStacks++;
|
|
264
|
+
return f(
|
|
265
|
+
e,
|
|
266
|
+
r,
|
|
267
|
+
t,
|
|
268
|
+
!0,
|
|
269
|
+
n ? Error("react-stack-top-frame") : W,
|
|
270
|
+
n ? N(c(e)) : U
|
|
271
|
+
);
|
|
272
|
+
};
|
|
273
|
+
}()), y;
|
|
274
|
+
}
|
|
275
|
+
process.env.NODE_ENV === "production" ? D.exports = ne() : D.exports = oe();
|
|
276
|
+
var i = D.exports;
|
|
277
|
+
const ae = ({ pinned: s }) => /* @__PURE__ */ i.jsx(
|
|
278
|
+
"svg",
|
|
279
|
+
{
|
|
280
|
+
width: "16",
|
|
281
|
+
height: "16",
|
|
282
|
+
viewBox: "0 0 24 24",
|
|
283
|
+
fill: "none",
|
|
284
|
+
stroke: "currentColor",
|
|
285
|
+
strokeWidth: "2",
|
|
286
|
+
strokeLinecap: "round",
|
|
287
|
+
strokeLinejoin: "round",
|
|
288
|
+
children: s ? /* @__PURE__ */ i.jsx("path", { d: "M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2m0 18c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8m3-13h-2v4h-2v-4h-2v2h4v2h-4v2h6v-6" }) : /* @__PURE__ */ i.jsx("path", { d: "M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2m0 18c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8m3.5-9h-7v2h7v-2" })
|
|
289
|
+
}
|
|
290
|
+
), se = ({ collapsed: s }) => /* @__PURE__ */ i.jsx(
|
|
291
|
+
"svg",
|
|
292
|
+
{
|
|
293
|
+
width: "16",
|
|
294
|
+
height: "16",
|
|
295
|
+
viewBox: "0 0 24 24",
|
|
296
|
+
fill: "none",
|
|
297
|
+
stroke: "currentColor",
|
|
298
|
+
strokeWidth: "2.5",
|
|
299
|
+
strokeLinecap: "round",
|
|
300
|
+
strokeLinejoin: "round",
|
|
301
|
+
className: `chevron ${s ? "collapsed" : ""}`,
|
|
302
|
+
children: /* @__PURE__ */ i.jsx("polyline", { points: "6 9 12 15 18 9" })
|
|
303
|
+
}
|
|
304
|
+
), ie = ({
|
|
305
|
+
children: s = "Content",
|
|
306
|
+
title: h = "Floaty",
|
|
307
|
+
style: v = {}
|
|
308
|
+
}) => {
|
|
309
|
+
const [c, l] = M(!1), [a, E] = M(!1), [d, P] = M({ x: 100, y: 100 }), R = V(null), f = V({ isDragging: !1, offsetX: 0, offsetY: 0 }), k = (m) => {
|
|
310
|
+
var p;
|
|
311
|
+
if (a) return;
|
|
312
|
+
const u = (p = R.current) == null ? void 0 : p.getBoundingClientRect();
|
|
313
|
+
u && (f.current = {
|
|
314
|
+
isDragging: !0,
|
|
315
|
+
offsetX: m.clientX - u.left,
|
|
316
|
+
offsetY: m.clientY - u.top
|
|
317
|
+
});
|
|
318
|
+
};
|
|
319
|
+
return te(() => {
|
|
320
|
+
const m = (p) => {
|
|
321
|
+
var j;
|
|
322
|
+
if (!f.current.isDragging) return;
|
|
323
|
+
const g = (j = R.current) == null ? void 0 : j.getBoundingClientRect();
|
|
324
|
+
if (!g) return;
|
|
325
|
+
let b = p.clientX - f.current.offsetX, T = p.clientY - f.current.offsetY;
|
|
326
|
+
const S = window.innerWidth - g.width, O = window.innerHeight - g.height;
|
|
327
|
+
b = Math.max(0, Math.min(b, S)), T = Math.max(0, Math.min(T, O)), P({
|
|
328
|
+
x: b,
|
|
329
|
+
y: T
|
|
330
|
+
});
|
|
331
|
+
}, u = () => {
|
|
332
|
+
f.current.isDragging = !1;
|
|
333
|
+
};
|
|
334
|
+
return globalThis.addEventListener("mousemove", m), globalThis.addEventListener("mouseup", u), () => {
|
|
335
|
+
globalThis.removeEventListener("mousemove", m), globalThis.removeEventListener("mouseup", u);
|
|
336
|
+
};
|
|
337
|
+
}, []), /* @__PURE__ */ i.jsxs(
|
|
338
|
+
"div",
|
|
339
|
+
{
|
|
340
|
+
ref: R,
|
|
341
|
+
className: `floaty ${a ? "pinned" : ""} ${c ? "collapsed" : ""} ${f.current.isDragging ? "dragging" : ""}`,
|
|
342
|
+
style: {
|
|
343
|
+
transform: `translate(${d.x}px, ${d.y}px)`,
|
|
344
|
+
...v
|
|
345
|
+
},
|
|
346
|
+
children: [
|
|
347
|
+
/* @__PURE__ */ i.jsxs(
|
|
348
|
+
"header",
|
|
349
|
+
{
|
|
350
|
+
className: `floaty-header ${a ? "pinned" : ""}`,
|
|
351
|
+
onMouseDown: k,
|
|
352
|
+
children: [
|
|
353
|
+
/* @__PURE__ */ i.jsx(
|
|
354
|
+
"button",
|
|
355
|
+
{
|
|
356
|
+
className: "floaty-button floaty-button--pin",
|
|
357
|
+
onClick: () => E(!a),
|
|
358
|
+
title: a ? "Unpin" : "Pin",
|
|
359
|
+
"aria-label": a ? "Unpin floaty" : "Pin floaty",
|
|
360
|
+
children: /* @__PURE__ */ i.jsx(ae, { pinned: a })
|
|
361
|
+
}
|
|
362
|
+
),
|
|
363
|
+
/* @__PURE__ */ i.jsx("span", { className: "floaty-title", children: h }),
|
|
364
|
+
/* @__PURE__ */ i.jsx(
|
|
365
|
+
"button",
|
|
366
|
+
{
|
|
367
|
+
className: "floaty-button floaty-button--expand",
|
|
368
|
+
onClick: () => l(!c),
|
|
369
|
+
title: c ? "Expand" : "Collapse",
|
|
370
|
+
"aria-label": c ? "Expand floaty" : "Collapse floaty",
|
|
371
|
+
children: /* @__PURE__ */ i.jsx(se, { collapsed: c })
|
|
372
|
+
}
|
|
373
|
+
)
|
|
374
|
+
]
|
|
375
|
+
}
|
|
376
|
+
),
|
|
377
|
+
!c && /* @__PURE__ */ i.jsx("div", { className: "floaty-body", children: s })
|
|
378
|
+
]
|
|
379
|
+
}
|
|
380
|
+
);
|
|
381
|
+
};
|
|
382
|
+
export {
|
|
383
|
+
ie as Floaty
|
|
384
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
(function(E,u){typeof exports=="object"&&typeof module<"u"?u(exports,require("react")):typeof define=="function"&&define.amd?define(["exports","react"],u):(E=typeof globalThis<"u"?globalThis:E||self,u(E.FloatyWidget={},E.React))})(this,function(E,u){"use strict";var O={exports:{}},T={};/**
|
|
2
|
+
* @license React
|
|
3
|
+
* react-jsx-runtime.production.js
|
|
4
|
+
*
|
|
5
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
6
|
+
*
|
|
7
|
+
* This source code is licensed under the MIT license found in the
|
|
8
|
+
* LICENSE file in the root directory of this source tree.
|
|
9
|
+
*/var F;function G(){if(F)return T;F=1;var l=Symbol.for("react.transitional.element"),R=Symbol.for("react.fragment");function h(c,i,a){var b=null;if(a!==void 0&&(b=""+a),i.key!==void 0&&(b=""+i.key),"key"in i){a={};for(var m in i)m!=="key"&&(a[m]=i[m])}else a=i;return i=a.ref,{$$typeof:l,type:c,key:b,ref:i!==void 0?i:null,props:a}}return T.Fragment=R,T.jsx=h,T.jsxs=h,T}var y={};/**
|
|
10
|
+
* @license React
|
|
11
|
+
* react-jsx-runtime.development.js
|
|
12
|
+
*
|
|
13
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
14
|
+
*
|
|
15
|
+
* This source code is licensed under the MIT license found in the
|
|
16
|
+
* LICENSE file in the root directory of this source tree.
|
|
17
|
+
*/var W;function J(){return W||(W=1,process.env.NODE_ENV!=="production"&&function(){function l(e){if(e==null)return null;if(typeof e=="function")return e.$$typeof===ne?null:e.displayName||e.name||null;if(typeof e=="string")return e;switch(e){case _:return"Fragment";case C:return"Profiler";case j:return"StrictMode";case K:return"Suspense";case ee:return"SuspenseList";case re:return"Activity"}if(typeof e=="object")switch(typeof e.tag=="number"&&console.error("Received an unexpected object in getComponentNameFromType(). This is likely a bug in React. Please file an issue."),e.$$typeof){case k:return"Portal";case S:return e.displayName||"Context";case N:return(e._context.displayName||"Context")+".Consumer";case q:var t=e.render;return e=e.displayName,e||(e=t.displayName||t.name||"",e=e!==""?"ForwardRef("+e+")":"ForwardRef"),e;case te:return t=e.displayName||null,t!==null?t:l(e.type)||"Memo";case Y:t=e._payload,e=e._init;try{return l(e(t))}catch{}}return null}function R(e){return""+e}function h(e){try{R(e);var t=!1}catch{t=!0}if(t){t=console;var r=t.error,n=typeof Symbol=="function"&&Symbol.toStringTag&&e[Symbol.toStringTag]||e.constructor.name||"Object";return r.call(t,"The provided key is an unsupported type %s. This value must be coerced to a string before using it here.",n),R(e)}}function c(e){if(e===_)return"<>";if(typeof e=="object"&&e!==null&&e.$$typeof===Y)return"<...>";try{var t=l(e);return t?"<"+t+">":"<...>"}catch{return"<...>"}}function i(){var e=M.A;return e===null?null:e.getOwner()}function a(){return Error("react-stack-top-frame")}function b(e){if(U.call(e,"key")){var t=Object.getOwnPropertyDescriptor(e,"key").get;if(t&&t.isReactWarning)return!1}return e.key!==void 0}function m(e,t){function r(){X||(X=!0,console.error("%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://react.dev/link/special-props)",t))}r.isReactWarning=!0,Object.defineProperty(e,"key",{get:r,configurable:!0})}function A(){var e=l(this.type);return $[e]||($[e]=!0,console.error("Accessing element.ref was removed in React 19. ref is now a regular prop. It will be removed from the JSX Element type in a future release.")),e=this.props.ref,e!==void 0?e:null}function x(e,t,r,n,P,I){var o=r.ref;return e={$$typeof:v,type:e,key:t,props:r,_owner:n},(o!==void 0?o:null)!==null?Object.defineProperty(e,"ref",{enumerable:!1,get:A}):Object.defineProperty(e,"ref",{enumerable:!1,value:null}),e._store={},Object.defineProperty(e._store,"validated",{configurable:!1,enumerable:!1,writable:!0,value:0}),Object.defineProperty(e,"_debugInfo",{configurable:!1,enumerable:!1,writable:!0,value:null}),Object.defineProperty(e,"_debugStack",{configurable:!1,enumerable:!1,writable:!0,value:P}),Object.defineProperty(e,"_debugTask",{configurable:!1,enumerable:!1,writable:!0,value:I}),Object.freeze&&(Object.freeze(e.props),Object.freeze(e)),e}function d(e,t,r,n,P,I){var o=t.children;if(o!==void 0)if(n)if(oe(o)){for(n=0;n<o.length;n++)w(o[n]);Object.freeze&&Object.freeze(o)}else console.error("React.jsx: Static children should always be an array. You are likely explicitly calling React.jsxs or React.jsxDEV. Use the Babel transform instead.");else w(o);if(U.call(t,"key")){o=l(e);var g=Object.keys(t).filter(function(ae){return ae!=="key"});n=0<g.length?"{key: someKey, "+g.join(": ..., ")+": ...}":"{key: someKey}",B[o+n]||(g=0<g.length?"{"+g.join(": ..., ")+": ...}":"{}",console.error(`A props object containing a "key" prop is being spread into JSX:
|
|
18
|
+
let props = %s;
|
|
19
|
+
<%s {...props} />
|
|
20
|
+
React keys must be passed directly to JSX without using spread:
|
|
21
|
+
let props = %s;
|
|
22
|
+
<%s key={someKey} {...props} />`,n,o,g,o),B[o+n]=!0)}if(o=null,r!==void 0&&(h(r),o=""+r),b(t)&&(h(t.key),o=""+t.key),"key"in t){r={};for(var L in t)L!=="key"&&(r[L]=t[L])}else r=t;return o&&m(r,typeof e=="function"?e.displayName||e.name||"Unknown":e),x(e,o,r,i(),P,I)}function w(e){p(e)?e._store&&(e._store.validated=1):typeof e=="object"&&e!==null&&e.$$typeof===Y&&(e._payload.status==="fulfilled"?p(e._payload.value)&&e._payload.value._store&&(e._payload.value._store.validated=1):e._store&&(e._store.validated=1))}function p(e){return typeof e=="object"&&e!==null&&e.$$typeof===v}var f=u,v=Symbol.for("react.transitional.element"),k=Symbol.for("react.portal"),_=Symbol.for("react.fragment"),j=Symbol.for("react.strict_mode"),C=Symbol.for("react.profiler"),N=Symbol.for("react.consumer"),S=Symbol.for("react.context"),q=Symbol.for("react.forward_ref"),K=Symbol.for("react.suspense"),ee=Symbol.for("react.suspense_list"),te=Symbol.for("react.memo"),Y=Symbol.for("react.lazy"),re=Symbol.for("react.activity"),ne=Symbol.for("react.client.reference"),M=f.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,U=Object.prototype.hasOwnProperty,oe=Array.isArray,D=console.createTask?console.createTask:function(){return null};f={react_stack_bottom_frame:function(e){return e()}};var X,$={},V=f.react_stack_bottom_frame.bind(f,a)(),z=D(c(a)),B={};y.Fragment=_,y.jsx=function(e,t,r){var n=1e4>M.recentlyCreatedOwnerStacks++;return d(e,t,r,!1,n?Error("react-stack-top-frame"):V,n?D(c(e)):z)},y.jsxs=function(e,t,r){var n=1e4>M.recentlyCreatedOwnerStacks++;return d(e,t,r,!0,n?Error("react-stack-top-frame"):V,n?D(c(e)):z)}}()),y}process.env.NODE_ENV==="production"?O.exports=G():O.exports=J();var s=O.exports;const H=({pinned:l})=>s.jsx("svg",{width:"16",height:"16",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:l?s.jsx("path",{d:"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2m0 18c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8m3-13h-2v4h-2v-4h-2v2h4v2h-4v2h6v-6"}):s.jsx("path",{d:"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2m0 18c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8m3.5-9h-7v2h7v-2"})}),Z=({collapsed:l})=>s.jsx("svg",{width:"16",height:"16",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2.5",strokeLinecap:"round",strokeLinejoin:"round",className:`chevron ${l?"collapsed":""}`,children:s.jsx("polyline",{points:"6 9 12 15 18 9"})}),Q=({children:l="Content",title:R="Floaty",style:h={}})=>{const[c,i]=u.useState(!1),[a,b]=u.useState(!1),[m,A]=u.useState({x:100,y:100}),x=u.useRef(null),d=u.useRef({isDragging:!1,offsetX:0,offsetY:0}),w=p=>{var v;if(a)return;const f=(v=x.current)==null?void 0:v.getBoundingClientRect();f&&(d.current={isDragging:!0,offsetX:p.clientX-f.left,offsetY:p.clientY-f.top})};return u.useEffect(()=>{const p=v=>{var S;if(!d.current.isDragging)return;const k=(S=x.current)==null?void 0:S.getBoundingClientRect();if(!k)return;let _=v.clientX-d.current.offsetX,j=v.clientY-d.current.offsetY;const C=window.innerWidth-k.width,N=window.innerHeight-k.height;_=Math.max(0,Math.min(_,C)),j=Math.max(0,Math.min(j,N)),A({x:_,y:j})},f=()=>{d.current.isDragging=!1};return globalThis.addEventListener("mousemove",p),globalThis.addEventListener("mouseup",f),()=>{globalThis.removeEventListener("mousemove",p),globalThis.removeEventListener("mouseup",f)}},[]),s.jsxs("div",{ref:x,className:`floaty ${a?"pinned":""} ${c?"collapsed":""} ${d.current.isDragging?"dragging":""}`,style:{transform:`translate(${m.x}px, ${m.y}px)`,...h},children:[s.jsxs("header",{className:`floaty-header ${a?"pinned":""}`,onMouseDown:w,children:[s.jsx("button",{className:"floaty-button floaty-button--pin",onClick:()=>b(!a),title:a?"Unpin":"Pin","aria-label":a?"Unpin floaty":"Pin floaty",children:s.jsx(H,{pinned:a})}),s.jsx("span",{className:"floaty-title",children:R}),s.jsx("button",{className:"floaty-button floaty-button--expand",onClick:()=>i(!c),title:c?"Expand":"Collapse","aria-label":c?"Expand floaty":"Collapse floaty",children:s.jsx(Z,{collapsed:c})})]}),!c&&s.jsx("div",{className:"floaty-body",children:l})]})};E.Floaty=Q,Object.defineProperty(E,Symbol.toStringTag,{value:"Module"})});
|
package/dist/style.css
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.floaty{--primary-color: #4f46e5;--primary-hover: #4338ca;--border-color: #e5e7eb;--shadow: 0 10px 25px rgba(0, 0, 0, .1);--transition: all .3s ease;--font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;position:fixed;width:320px;background:#fff;border:1px solid var(--border-color);border-radius:8px;box-shadow:var(--shadow);z-index:1000;-webkit-user-select:none;user-select:none;font-family:var(--font-family)}.floaty:not(.dragging){transition:var(--transition)}.floaty.dragging{box-shadow:0 20px 35px #0003;opacity:.95}.floaty.pinned{opacity:1;border-color:var(--primary-color)}.floaty.collapsed{min-width:auto}.floaty-header{display:flex;align-items:center;gap:8px;padding:12px 16px;background:linear-gradient(135deg,var(--primary-color) 0%,var(--primary-hover) 100%);color:#fff;font-weight:600;font-size:14px;border-radius:7px 7px 0 0;cursor:grab;transition:border-radius .3s ease;-webkit-user-select:none;user-select:none}.floaty.collapsed .floaty-header{border-radius:7px}.floaty-header:active{cursor:grabbing}.floaty-header.pinned{background:linear-gradient(135deg,#10b981,#059669)}.floaty-button{background:none;border:none;color:#fff;cursor:pointer;padding:4px 8px;border-radius:4px;transition:var(--transition);display:flex;align-items:center;justify-content:center;flex-shrink:0}.floaty-button svg{width:16px;height:16px;stroke:currentColor;fill:none}.floaty-button:hover{background:#fff3;transform:scale(1.1)}.floaty-button:active{transform:scale(.95)}.floaty-button--pin{order:-1}.floaty-button--expand{order:1;margin-left:auto}.floaty-title{flex:1;text-align:center;order:0}.floaty-body{padding:16px;background:#f9fafb;border-radius:0 0 7px 7px;font-size:14px;line-height:1.6;color:#374151;max-height:500px;overflow:hidden;opacity:1;transform-origin:top;animation:expandIn .3s cubic-bezier(.34,1.56,.64,1)}.floaty.collapsed .floaty-body{animation:collapseOut .3s cubic-bezier(.34,1.56,.64,1) forwards}@keyframes expandIn{0%{opacity:0;max-height:0;transform:scaleY(.8)}to{opacity:1;max-height:500px;transform:scaleY(1)}}@keyframes collapseOut{0%{opacity:1;max-height:500px;transform:scaleY(1)}to{opacity:0;max-height:0;transform:scaleY(.8)}}.chevron{display:inline-flex;transition:transform .3s cubic-bezier(.34,1.56,.64,1)}.chevron.collapsed{transform:rotate(180deg)}@media (max-width: 480px){.floaty{min-width:200px}.floaty-header{padding:10px 12px;font-size:13px}.floaty-body{padding:12px;max-height:300px}}
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "floaty-widget",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "A floating component with drag, collapse/expand, and pin functionality",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"module": "dist/index.es.js",
|
|
8
|
+
"types": "dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": "./dist/index.es.js",
|
|
12
|
+
"require": "./dist/index.js",
|
|
13
|
+
"types": "./dist/index.d.ts"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"dev": "vite",
|
|
21
|
+
"build": "vite build",
|
|
22
|
+
"preview": "vite preview",
|
|
23
|
+
"storybook": "storybook dev -p 6006",
|
|
24
|
+
"build-storybook": "storybook build"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"react": "^19.0.0",
|
|
28
|
+
"react-dom": "^19.0.0"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@storybook/addon-docs": "^10.3.4",
|
|
32
|
+
"@storybook/react": "^10.3.4",
|
|
33
|
+
"@storybook/react-vite": "^10.3.4",
|
|
34
|
+
"@types/react": "^19.0.0",
|
|
35
|
+
"@types/react-dom": "^19.0.0",
|
|
36
|
+
"@vitejs/plugin-react": "^4.3.0",
|
|
37
|
+
"typescript": "^5.3.0",
|
|
38
|
+
"vite": "^5.0.0",
|
|
39
|
+
"storybook": "^10.3.4"
|
|
40
|
+
}
|
|
41
|
+
}
|