orio-ui 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 +237 -0
- package/dist/module.cjs +5 -0
- package/dist/module.d.mts +3 -0
- package/dist/module.d.ts +3 -0
- package/dist/module.json +12 -0
- package/dist/module.mjs +16 -0
- package/dist/runtime/assets/css/animation.css +1 -0
- package/dist/runtime/assets/css/colors.css +1 -0
- package/dist/runtime/assets/css/cool-gradient-hover.css +23 -0
- package/dist/runtime/assets/css/main.css +1 -0
- package/dist/runtime/assets/css/scroll.css +1 -0
- package/dist/runtime/components/Button.vue +102 -0
- package/dist/runtime/components/CheckBox.vue +93 -0
- package/dist/runtime/components/ControlElement.vue +39 -0
- package/dist/runtime/components/DashedContainer.vue +59 -0
- package/dist/runtime/components/DatePicker.vue +30 -0
- package/dist/runtime/components/DateRangePicker.vue +73 -0
- package/dist/runtime/components/EmptyState.vue +81 -0
- package/dist/runtime/components/Icon.vue +40 -0
- package/dist/runtime/components/Input.vue +48 -0
- package/dist/runtime/components/LoadingSpinner.vue +6 -0
- package/dist/runtime/components/Modal.vue +69 -0
- package/dist/runtime/components/Popover.vue +249 -0
- package/dist/runtime/components/Selector.vue +208 -0
- package/dist/runtime/components/Tag.vue +21 -0
- package/dist/runtime/components/Textarea.vue +53 -0
- package/dist/runtime/components/view/Dates.vue +59 -0
- package/dist/runtime/components/view/Separator.vue +26 -0
- package/dist/runtime/components/view/Text.vue +79 -0
- package/dist/runtime/composables/index.d.ts +4 -0
- package/dist/runtime/composables/index.js +4 -0
- package/dist/runtime/composables/useApi.d.ts +10 -0
- package/dist/runtime/composables/useApi.js +9 -0
- package/dist/runtime/composables/useFuzzySearch.d.ts +10 -0
- package/dist/runtime/composables/useFuzzySearch.js +22 -0
- package/dist/runtime/composables/useModal.d.ts +15 -0
- package/dist/runtime/composables/useModal.js +28 -0
- package/dist/runtime/composables/useTheme.d.ts +6 -0
- package/dist/runtime/composables/useTheme.js +23 -0
- package/dist/runtime/index.d.ts +20 -0
- package/dist/runtime/index.js +20 -0
- package/dist/runtime/utils/icon-registry.d.ts +2 -0
- package/dist/runtime/utils/icon-registry.js +26 -0
- package/dist/types.d.mts +7 -0
- package/dist/types.d.ts +7 -0
- package/nuxt.config.ts +38 -0
- package/package.json +99 -0
- package/src/module.ts +16 -0
- package/src/runtime/assets/css/animation.css +88 -0
- package/src/runtime/assets/css/colors.css +142 -0
- package/src/runtime/assets/css/cool-gradient-hover.scss +33 -0
- package/src/runtime/assets/css/main.css +11 -0
- package/src/runtime/assets/css/scroll.css +46 -0
- package/src/runtime/components/Button.vue +110 -0
- package/src/runtime/components/CheckBox.vue +103 -0
- package/src/runtime/components/ControlElement.vue +42 -0
- package/src/runtime/components/DashedContainer.vue +60 -0
- package/src/runtime/components/DatePicker.vue +84 -0
- package/src/runtime/components/DateRangePicker.vue +74 -0
- package/src/runtime/components/EmptyState.vue +87 -0
- package/src/runtime/components/Icon.vue +51 -0
- package/src/runtime/components/Input.vue +54 -0
- package/src/runtime/components/LoadingSpinner.vue +6 -0
- package/src/runtime/components/Modal.vue +111 -0
- package/src/runtime/components/Popover.vue +249 -0
- package/src/runtime/components/Selector.vue +224 -0
- package/src/runtime/components/Tag.vue +45 -0
- package/src/runtime/components/Textarea.vue +59 -0
- package/src/runtime/components/view/Dates.vue +61 -0
- package/src/runtime/components/view/Separator.vue +30 -0
- package/src/runtime/components/view/Text.vue +83 -0
- package/src/runtime/composables/index.ts +4 -0
- package/src/runtime/composables/useApi.ts +26 -0
- package/src/runtime/composables/useFuzzySearch.ts +51 -0
- package/src/runtime/composables/useModal.ts +47 -0
- package/src/runtime/composables/useTheme.ts +31 -0
- package/src/runtime/index.ts +25 -0
- package/src/runtime/utils/icon-registry.ts +41 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 oriondor
|
|
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,237 @@
|
|
|
1
|
+
# Orio UI
|
|
2
|
+
|
|
3
|
+
A delightful, lightweight component library for Nuxt 3 applications. Built with TypeScript, fully tested, and designed for modern web development.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/orio-ui)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
|
|
8
|
+
## Features
|
|
9
|
+
|
|
10
|
+
✨ **18 Components** - Beautiful, accessible components ready to use
|
|
11
|
+
🎨 **Themeable** - 5 built-in accent themes with light/dark mode support
|
|
12
|
+
🚀 **Auto-imported** - Works seamlessly with Nuxt's auto-import system
|
|
13
|
+
📦 **Tree-shakeable** - Only bundle what you use
|
|
14
|
+
🎯 **TypeScript** - Fully typed for great developer experience
|
|
15
|
+
🧪 **Tested** - 71+ unit tests for reliability
|
|
16
|
+
📱 **Responsive** - Mobile-first design approach
|
|
17
|
+
♿ **Accessible** - ARIA-compliant components
|
|
18
|
+
|
|
19
|
+
## Quick Start
|
|
20
|
+
|
|
21
|
+
### Installation
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm install orio-ui
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Setup
|
|
28
|
+
|
|
29
|
+
Add Orio UI to your `nuxt.config.ts`:
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
export default defineNuxtConfig({
|
|
33
|
+
extends: ["orio-ui"],
|
|
34
|
+
});
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
That's it! All components and composables are now auto-imported.
|
|
38
|
+
|
|
39
|
+
### Usage
|
|
40
|
+
|
|
41
|
+
```vue
|
|
42
|
+
<template>
|
|
43
|
+
<div>
|
|
44
|
+
<orio-button type="primary" @click="handleClick"> Click Me </orio-button>
|
|
45
|
+
|
|
46
|
+
<orio-input v-model="email" label="Email" placeholder="you@example.com" />
|
|
47
|
+
|
|
48
|
+
<orio-view-text type="title" size="large">
|
|
49
|
+
Welcome to Orio UI
|
|
50
|
+
</orio-view-text>
|
|
51
|
+
</div>
|
|
52
|
+
</template>
|
|
53
|
+
|
|
54
|
+
<script setup>
|
|
55
|
+
const email = ref("");
|
|
56
|
+
|
|
57
|
+
// Composables are auto-imported too!
|
|
58
|
+
const { theme, setTheme } = useTheme();
|
|
59
|
+
|
|
60
|
+
function handleClick() {
|
|
61
|
+
setTheme("ocean");
|
|
62
|
+
}
|
|
63
|
+
</script>
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## What's Included
|
|
67
|
+
|
|
68
|
+
### Components (18)
|
|
69
|
+
|
|
70
|
+
#### Form Controls
|
|
71
|
+
|
|
72
|
+
- **Input** - Text input with label support
|
|
73
|
+
- **Textarea** - Multi-line text input
|
|
74
|
+
- **CheckBox** - Custom checkbox with icon states
|
|
75
|
+
- **DatePicker** - Date selection with month/year options
|
|
76
|
+
- **DateRangePicker** - Start and end date selection
|
|
77
|
+
- **Selector** - Generic dropdown selector (single/multi-select)
|
|
78
|
+
- **Tag** - Styled tag/badge component
|
|
79
|
+
|
|
80
|
+
#### Interactive
|
|
81
|
+
|
|
82
|
+
- **Button** - Primary, secondary, subdued variants with loading/icon support
|
|
83
|
+
- **Modal** - Animated modal with origin morphing
|
|
84
|
+
- **Popover** - Positioned popover with smart placement
|
|
85
|
+
|
|
86
|
+
#### Display
|
|
87
|
+
|
|
88
|
+
- **Icon** - SVG icon system with 12 bundled icons
|
|
89
|
+
- **LoadingSpinner** - Animated loading indicator
|
|
90
|
+
- **EmptyState** - Empty state placeholder
|
|
91
|
+
- **DashedContainer** - Dashed border container with icon
|
|
92
|
+
- **ControlElement** - Form control wrapper
|
|
93
|
+
|
|
94
|
+
#### View
|
|
95
|
+
|
|
96
|
+
- **Text** - Typography component with variants
|
|
97
|
+
- **Dates** - Date range display formatter
|
|
98
|
+
- **Separator** - Visual divider
|
|
99
|
+
|
|
100
|
+
### Composables (4)
|
|
101
|
+
|
|
102
|
+
- **useTheme** - Theme and color mode management
|
|
103
|
+
- **useModal** - Modal state with animation origin tracking
|
|
104
|
+
- **useFuzzySearch** - Fuzzy search powered by Fuse.js
|
|
105
|
+
- **useApi** - Type-safe API request wrapper
|
|
106
|
+
|
|
107
|
+
### Theming
|
|
108
|
+
|
|
109
|
+
Built-in themes:
|
|
110
|
+
|
|
111
|
+
- **Navy** (default) - Professional blue
|
|
112
|
+
- **Ocean** - Fresh cyan
|
|
113
|
+
- **Sunset** - Warm orange
|
|
114
|
+
- **Forest** - Natural green
|
|
115
|
+
- **Purple** - Creative purple
|
|
116
|
+
|
|
117
|
+
All themes support light and dark modes. Fully customizable via CSS variables.
|
|
118
|
+
|
|
119
|
+
```vue
|
|
120
|
+
<script setup>
|
|
121
|
+
const { setTheme, setMode } = useTheme();
|
|
122
|
+
|
|
123
|
+
setTheme("ocean");
|
|
124
|
+
setMode("dark");
|
|
125
|
+
</script>
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Documentation
|
|
129
|
+
|
|
130
|
+
Full documentation:
|
|
131
|
+
|
|
132
|
+
- [Getting Started Guide](./docs/getting-started.md)
|
|
133
|
+
- [Theming Guide](./docs/theming.md)
|
|
134
|
+
- [Component Documentation](./docs/components/)
|
|
135
|
+
- [Composable Documentation](./docs/composables/)
|
|
136
|
+
- [Utils Documentation](./docs/utils/)
|
|
137
|
+
|
|
138
|
+
## Development
|
|
139
|
+
|
|
140
|
+
### Setup Development Environment
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
# Clone the repository
|
|
144
|
+
git clone https://github.com/oriondor/orio-ui.git
|
|
145
|
+
cd orio-ui
|
|
146
|
+
|
|
147
|
+
# Install dependencies
|
|
148
|
+
npm install
|
|
149
|
+
|
|
150
|
+
# Run documentation site
|
|
151
|
+
npm run dev
|
|
152
|
+
|
|
153
|
+
# Run tests
|
|
154
|
+
npm test
|
|
155
|
+
|
|
156
|
+
# Build library
|
|
157
|
+
npm run build
|
|
158
|
+
|
|
159
|
+
# Run documentation
|
|
160
|
+
npm run docs:dev
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Project Structure
|
|
164
|
+
|
|
165
|
+
```
|
|
166
|
+
orio-ui/
|
|
167
|
+
├── src/runtime/
|
|
168
|
+
│ ├── components/ # 18 Vue components
|
|
169
|
+
│ ├── composables/ # 4 composables
|
|
170
|
+
│ ├── assets/css/ # Theme CSS files
|
|
171
|
+
│ └── utils/ # Icon registry
|
|
172
|
+
├── tests/ # Vitest unit tests
|
|
173
|
+
├── docs/ # VitePress documentation
|
|
174
|
+
└── nuxt.config.ts # Nuxt Layer configuration
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### Running Tests
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
# Run all tests
|
|
181
|
+
npm test
|
|
182
|
+
|
|
183
|
+
# Run tests once
|
|
184
|
+
npm run test:unit
|
|
185
|
+
|
|
186
|
+
# Watch mode
|
|
187
|
+
npm run test:watch
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## TypeScript Support
|
|
191
|
+
|
|
192
|
+
Orio UI is written in TypeScript and provides full type definitions:
|
|
193
|
+
|
|
194
|
+
```typescript
|
|
195
|
+
import type { TextTypes, TagStyle } from "orio-ui/composables";
|
|
196
|
+
import type { OriginRect, ModalProps } from "orio-ui/composables";
|
|
197
|
+
import type { IconName } from "orio-ui/composables";
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
## Browser Support
|
|
201
|
+
|
|
202
|
+
- Chrome (latest)
|
|
203
|
+
- Firefox (latest)
|
|
204
|
+
- Safari (latest)
|
|
205
|
+
- Edge (latest)
|
|
206
|
+
|
|
207
|
+
## Contributing
|
|
208
|
+
|
|
209
|
+
Contributions are welcome! Please read our contributing guidelines before submitting PRs.
|
|
210
|
+
|
|
211
|
+
1. Fork the repository
|
|
212
|
+
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
|
|
213
|
+
3. Commit your changes (`git commit -m 'Add amazing feature'`)
|
|
214
|
+
4. Push to the branch (`git push origin feature/amazing-feature`)
|
|
215
|
+
5. Open a Pull Request
|
|
216
|
+
|
|
217
|
+
## License
|
|
218
|
+
|
|
219
|
+
MIT © [oriondor](https://github.com/oriondor)
|
|
220
|
+
|
|
221
|
+
## Changelog
|
|
222
|
+
|
|
223
|
+
See [CHANGELOG.md](./CHANGELOG.md) for version history.
|
|
224
|
+
|
|
225
|
+
## Credits
|
|
226
|
+
|
|
227
|
+
Built with:
|
|
228
|
+
|
|
229
|
+
- [Nuxt 3](https://nuxt.com/) - Vue framework
|
|
230
|
+
- [VueUse](https://vueuse.org/) - Vue composables collection
|
|
231
|
+
- [Fuse.js](https://fusejs.io/) - Fuzzy search library
|
|
232
|
+
- [VitePress](https://vitepress.dev/) - Documentation
|
|
233
|
+
- [Vitest](https://vitest.dev/) - Testing framework
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
**Made with ❤️ for the Nuxt community**
|
package/dist/module.cjs
ADDED
package/dist/module.d.ts
ADDED
package/dist/module.json
ADDED
package/dist/module.mjs
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { defineNuxtModule } from '@nuxt/kit';
|
|
2
|
+
|
|
3
|
+
const module = defineNuxtModule({
|
|
4
|
+
meta: {
|
|
5
|
+
name: "orio-ui",
|
|
6
|
+
configKey: "orioUi",
|
|
7
|
+
compatibility: {
|
|
8
|
+
nuxt: "^3.0.0 || ^4.0.0"
|
|
9
|
+
}
|
|
10
|
+
},
|
|
11
|
+
defaults: {},
|
|
12
|
+
setup() {
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
export { module as default };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
:root{--motion-duration-short:0.05s;--motion-duration-medium:0.15s;--motion-distance-small:0.15rem;--motion-distance-medium:2rem;--motion-distance-large:1rem;--motion-ease-default:ease-in;--motion-ease-smooth:cubic-bezier(0.22,1,0.36,1)}.animate-fade-enter-active,.animate-fade-leave-active{transition:opacity var(--motion-duration-short) var(--motion-ease-default),transform var(--motion-duration-short) var(--motion-ease-default)}.animate-fade-enter-from,.animate-fade-leave-to{opacity:0;transform:translateY(calc(var(--motion-distance-small)*-1))}.animate-fade-slide-appear-active,.animate-fade-slide-enter-active,.animate-fade-slide-leave-active{transition:opacity var(--motion-duration-medium) var(--motion-ease-smooth),transform var(--motion-duration-medium) var(--motion-ease-smooth);transition-delay:calc(var(--index, 0)*.08s)}.animate-fade-slide-appear-from,.animate-fade-slide-enter-from{opacity:0;transform:translateY(var(--motion-distance-medium))}.animate-fade-slide-leave-to{opacity:0;transform:translateY(calc(var(--motion-distance-large)*-1))}.animate-fade-slide-move{transition:transform var(--motion-duration-medium) ease}.animate-fade-reverse-enter-active,.animate-fade-reverse-leave-active{transition:opacity var(--motion-duration-short) var(--motion-ease-default),transform var(--motion-duration-short) var(--motion-ease-default)}.animate-fade-reverse-enter-from,.animate-fade-reverse-leave-to{opacity:0;transform:translateY(var(--motion-distance-small))}.animate-fade-slide-reverse-appear-active,.animate-fade-slide-reverse-enter-active,.animate-fade-slide-reverse-leave-active{transition:opacity var(--motion-duration-medium) var(--motion-ease-smooth),transform var(--motion-duration-medium) var(--motion-ease-smooth);transition-delay:calc(var(--index, 0)*.08s)}.animate-fade-slide-reverse-appear-from,.animate-fade-slide-reverse-enter-from{opacity:0;transform:translateY(calc(var(--motion-distance-medium)*-1))}.animate-fade-slide-reverse-leave-to{opacity:0;transform:translateY(var(--motion-distance-large))}.animate-fade-slide-reverse-move{transition:transform var(--motion-duration-medium) ease}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
:root{--color-bg:#fff;--color-surface:#f7f8fa;--color-text:#0e1116;--color-muted:#626a78;--color-border:#bfbfc2;--color-accent:#1f3a8a;--color-accent-ink:#fff;--color-accent-soft:#eef2ff;--color-accent-border:#c7d2fe;--color-accent-hover:color-mix(in srgb,var(--color-accent) 85%,#000 15%);--color-accent-active:color-mix(in srgb,var(--color-accent) 75%,#000 25%)}:root[data-mode=light]{--color-bg:#fff;--color-surface:#f7f8fa;--color-text:#0e1116;--color-muted:#626a78;--color-border:#bfbfc2}:root[data-mode=dark]{--color-bg:#0e1116;--color-surface:#1a1d23;--color-text:#fff;--color-muted:#a0a7b5;--color-border:#2e333d;--color-accent-ink:#fff;--color-accent-soft:color-mix(in srgb,var(--color-accent) 12%,#0e1116);--color-accent-border:color-mix(in srgb,var(--color-accent) 40%,#0e1116);--color-accent-hover:color-mix(in srgb,var(--color-accent) 30%,#fff 70%);--color-accent-active:color-mix(in srgb,var(--color-accent) 80%,#fff 25%)}:root[data-theme=navy]{--color-accent:#333c4d;--color-accent-soft:#f0f5ff;--color-accent-border:#bdcdef}:root[data-theme=teal]{--color-accent:#147b71;--color-accent-soft:#e7f8f6;--color-accent-border:#a6d9d4}:root[data-theme=forest]{--color-accent:#235c42;--color-accent-soft:#e9f7f0;--color-accent-border:#a2cdb9}:root[data-theme=wine]{--color-accent:#862737;--color-accent-soft:#fbeff1;--color-accent-border:#dbb3ba}:root[data-theme=royal]{--color-accent:#293da3;--color-accent-soft:#eef0fb;--color-accent-border:#b3badb}:root{--color-red:#dc2626;--color-red-ink:#fff;--color-red-soft:#fef2f2;--color-red-border:#fecaca;--color-red-hover:color-mix(in srgb,var(--color-red) 85%,#000 15%);--color-red-active:color-mix(in srgb,var(--color-red) 75%,#000 25%);--color-orange:#ea580c;--color-orange-ink:#fff;--color-orange-soft:#fff7ed;--color-orange-border:#fed7aa;--color-orange-hover:color-mix(in srgb,var(--color-orange) 85%,#000 15%);--color-orange-active:color-mix(in srgb,var(--color-orange) 75%,#000 25%);--color-yellow:#ca8a04;--color-yellow-ink:#fff;--color-yellow-soft:#fefce8;--color-yellow-border:#fef08a;--color-yellow-hover:color-mix(in srgb,var(--color-yellow) 85%,#000 15%);--color-yellow-active:color-mix(in srgb,var(--color-yellow) 75%,#000 25%);--color-green:#15803d;--color-green-ink:#fff;--color-green-soft:#f0fdf4;--color-green-border:#bbf7d0;--color-green-hover:color-mix(in srgb,var(--color-green) 85%,#000 15%);--color-green-active:color-mix(in srgb,var(--color-green) 75%,#000 25%);--color-teal:#0d9488;--color-teal-ink:#fff;--color-teal-soft:#f0fdfa;--color-teal-border:#99f6e4;--color-teal-hover:color-mix(in srgb,var(--color-teal) 85%,#000 15%);--color-teal-active:color-mix(in srgb,var(--color-teal) 75%,#000 25%);--color-blue:#2563eb;--color-blue-ink:#fff;--color-blue-soft:#eff6ff;--color-blue-border:#bfdbfe;--color-blue-hover:color-mix(in srgb,var(--color-blue) 85%,#000 15%);--color-blue-active:color-mix(in srgb,var(--color-blue) 75%,#000 25%);--color-purple:#7e22ce;--color-purple-ink:#fff;--color-purple-soft:#faf5ff;--color-purple-border:#e9d5ff;--color-purple-hover:color-mix(in srgb,var(--color-purple) 85%,#000 15%);--color-purple-active:color-mix(in srgb,var(--color-purple) 75%,#000 25%);--color-pink:#db2777;--color-pink-ink:#fff;--color-pink-soft:#fdf2f8;--color-pink-border:#fbcfe8;--color-pink-hover:color-mix(in srgb,var(--color-pink) 85%,#000 15%);--color-pink-active:color-mix(in srgb,var(--color-pink) 75%,#000 25%)}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
.gradient-hover {
|
|
2
|
+
--gh-color: #fff;
|
|
3
|
+
--gh-angle: 120deg;
|
|
4
|
+
--gh-peek-x: 95%;
|
|
5
|
+
--gh-mix-base: black;
|
|
6
|
+
background-image: linear-gradient(var(--gh-angle), color-mix(in srgb, var(--gh-color) 90%, var(--gh-mix-base)) 0%, var(--gh-color) 40%, color-mix(in srgb, var(--gh-color) 90%, var(--gh-mix-base)) 80%);
|
|
7
|
+
background-repeat: no-repeat;
|
|
8
|
+
background-size: 0% 0%;
|
|
9
|
+
background-position: 0% 0%;
|
|
10
|
+
transition: background-position 0.8s ease, border-color 0.2s ease;
|
|
11
|
+
will-change: background-size, background-position;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.gradient-hover:hover {
|
|
15
|
+
background-size: 200% 100%;
|
|
16
|
+
background-position: var(--gh-peek-x) 0;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/* Dark mode: now theme-aware */
|
|
20
|
+
:root[data-mode=dark] .gradient-hover {
|
|
21
|
+
--gh-color: var(--color-accent); /* switch from static white to theme accent */
|
|
22
|
+
--gh-mix-base: var(--color-bg); /* blend into dark background */
|
|
23
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@import "./colors.css";@import "./animation.css";@import "./scroll.css";*,:after,:before{box-sizing:border-box}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
::-webkit-scrollbar{height:12px;width:12px}::-webkit-scrollbar-track{background:var(--color-surface);border-left:1px solid var(--color-border)}::-webkit-scrollbar-thumb{background-color:var(--color-accent-border);border:3px solid var(--color-surface);border-radius:8px;-webkit-transition:background-color .2s ease;transition:background-color .2s ease}::-webkit-scrollbar-thumb:hover{background-color:var(--color-accent)}::-webkit-scrollbar-corner{background:var(--color-surface)}*{scrollbar-color:var(--color-accent-border) var(--color-surface);scrollbar-width:thin}:root[data-mode=dark] ::-webkit-scrollbar-track{background:var(--color-bg);border-left:1px solid var(--color-border)}:root[data-mode=dark] ::-webkit-scrollbar-thumb{background-color:var(--color-accent-border);border:3px solid var(--color-bg)}:root[data-mode=dark] *{scrollbar-color:var(--color-accent-border) var(--color-bg)}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed, toRefs, useAttrs, useSlots } from 'vue';
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
type?: 'primary' | 'secondary' | 'subdued';
|
|
6
|
+
icon?: string;
|
|
7
|
+
loading?: boolean;
|
|
8
|
+
disabled?: boolean;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
12
|
+
type: 'primary',
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
const { loading, disabled } = toRefs(props);
|
|
16
|
+
|
|
17
|
+
const attrs = useAttrs();
|
|
18
|
+
const slots = useSlots();
|
|
19
|
+
|
|
20
|
+
const isIconOnly = computed(() => {
|
|
21
|
+
const hasIcon = !!props.icon || !!slots.icon;
|
|
22
|
+
const hasDefault = !!slots.default;
|
|
23
|
+
return hasIcon && !hasDefault;
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const emit = defineEmits<{
|
|
27
|
+
(e: 'click', event: PointerEvent): void;
|
|
28
|
+
}>();
|
|
29
|
+
|
|
30
|
+
function click(event: PointerEvent) {
|
|
31
|
+
if (loading.value || disabled.value) return;
|
|
32
|
+
emit('click', event);
|
|
33
|
+
}
|
|
34
|
+
</script>
|
|
35
|
+
|
|
36
|
+
<template>
|
|
37
|
+
<orio-control-element>
|
|
38
|
+
<button
|
|
39
|
+
v-bind="attrs"
|
|
40
|
+
:class="[type, 'gradient-hover', { 'icon-only': isIconOnly }]"
|
|
41
|
+
@click="click"
|
|
42
|
+
>
|
|
43
|
+
<orio-loading-spinner v-if="loading" />
|
|
44
|
+
<slot v-else name="icon">
|
|
45
|
+
<orio-icon v-if="icon" :name="icon" />
|
|
46
|
+
</slot>
|
|
47
|
+
<slot />
|
|
48
|
+
</button>
|
|
49
|
+
</orio-control-element>
|
|
50
|
+
</template>
|
|
51
|
+
|
|
52
|
+
<style scoped>
|
|
53
|
+
button {
|
|
54
|
+
background-color: var(--color-accent);
|
|
55
|
+
color: var(--color-accent-ink);
|
|
56
|
+
border: 1px solid transparent;
|
|
57
|
+
border-radius: 4px;
|
|
58
|
+
padding: 8px 16px;
|
|
59
|
+
cursor: pointer;
|
|
60
|
+
display: inline-flex;
|
|
61
|
+
align-items: center;
|
|
62
|
+
gap: 0.5rem;
|
|
63
|
+
user-select: none;
|
|
64
|
+
}
|
|
65
|
+
button.icon-only {
|
|
66
|
+
padding: 0;
|
|
67
|
+
border-radius: 50%;
|
|
68
|
+
}
|
|
69
|
+
button:disabled {
|
|
70
|
+
background-color: var(--color-accent-soft);
|
|
71
|
+
color: var(--color-muted);
|
|
72
|
+
border-color: var(--color-accent-border);
|
|
73
|
+
cursor: not-allowed;
|
|
74
|
+
}
|
|
75
|
+
button:active {
|
|
76
|
+
border: 1px solid var(--color-accent-border);
|
|
77
|
+
}
|
|
78
|
+
button.primary {
|
|
79
|
+
--gh-color: var(--color-accent);
|
|
80
|
+
}
|
|
81
|
+
button.secondary {
|
|
82
|
+
background-color: transparent;
|
|
83
|
+
border: 1px solid var(--color-accent);
|
|
84
|
+
color: var(--color-accent);
|
|
85
|
+
}
|
|
86
|
+
button.secondary:hover {
|
|
87
|
+
color: var(--color-accent-hover);
|
|
88
|
+
}
|
|
89
|
+
button.subdued {
|
|
90
|
+
background-color: transparent;
|
|
91
|
+
color: var(--color-accent);
|
|
92
|
+
}
|
|
93
|
+
button.subdued:hover {
|
|
94
|
+
color: var(--color-accent-hover);
|
|
95
|
+
}
|
|
96
|
+
button:active {
|
|
97
|
+
border-color: var(--color-accent-border) !important;
|
|
98
|
+
}
|
|
99
|
+
button:hover {
|
|
100
|
+
color: var(--color-accent-ink);
|
|
101
|
+
}
|
|
102
|
+
</style>
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
const modelValue = defineModel<boolean>({ required: false });
|
|
3
|
+
|
|
4
|
+
defineProps<{
|
|
5
|
+
checkedIcon?: string; // optional: pass icon name for checked state
|
|
6
|
+
uncheckedIcon?: string; // optional: pass icon name for unchecked state
|
|
7
|
+
}>();
|
|
8
|
+
</script>
|
|
9
|
+
|
|
10
|
+
<template>
|
|
11
|
+
<orio-control-element class="checkbox">
|
|
12
|
+
<label class="checkbox-label">
|
|
13
|
+
<input
|
|
14
|
+
v-model="modelValue"
|
|
15
|
+
type="checkbox"
|
|
16
|
+
class="checkbox-input"
|
|
17
|
+
tabindex="-1"
|
|
18
|
+
/>
|
|
19
|
+
<span
|
|
20
|
+
class="checkbox-box"
|
|
21
|
+
:class="{
|
|
22
|
+
defaultChecked: !checkedIcon,
|
|
23
|
+
defaultUnchecked: !uncheckedIcon,
|
|
24
|
+
}"
|
|
25
|
+
>
|
|
26
|
+
<slot name="icon" :checked="modelValue">
|
|
27
|
+
<orio-icon v-if="modelValue && checkedIcon" :name="checkedIcon" />
|
|
28
|
+
<orio-icon
|
|
29
|
+
v-else-if="!modelValue && uncheckedIcon"
|
|
30
|
+
:name="uncheckedIcon"
|
|
31
|
+
/>
|
|
32
|
+
</slot>
|
|
33
|
+
</span>
|
|
34
|
+
<slot />
|
|
35
|
+
</label>
|
|
36
|
+
</orio-control-element>
|
|
37
|
+
</template>
|
|
38
|
+
|
|
39
|
+
<style scoped>
|
|
40
|
+
.checkbox {
|
|
41
|
+
--box-size: 1rem;
|
|
42
|
+
}
|
|
43
|
+
.checkbox-label {
|
|
44
|
+
position: relative;
|
|
45
|
+
user-select: none;
|
|
46
|
+
display: inline-flex;
|
|
47
|
+
align-items: center;
|
|
48
|
+
gap: 0.4rem;
|
|
49
|
+
cursor: pointer;
|
|
50
|
+
font-size: 0.9rem;
|
|
51
|
+
color: var(--color-text);
|
|
52
|
+
}
|
|
53
|
+
.checkbox-input {
|
|
54
|
+
position: absolute;
|
|
55
|
+
inset: 0;
|
|
56
|
+
width: var(--box-size);
|
|
57
|
+
height: 1rem;
|
|
58
|
+
margin: 0;
|
|
59
|
+
opacity: 0;
|
|
60
|
+
}
|
|
61
|
+
.checkbox-box {
|
|
62
|
+
width: var(--box-size);
|
|
63
|
+
height: var(--box-size);
|
|
64
|
+
border: 2px solid var(--color-border);
|
|
65
|
+
border-radius: 4px;
|
|
66
|
+
background-color: var(--color-bg);
|
|
67
|
+
display: inline-flex;
|
|
68
|
+
align-items: center;
|
|
69
|
+
justify-content: center;
|
|
70
|
+
transition: background-color 0.2s ease, border-color 0.2s ease;
|
|
71
|
+
}
|
|
72
|
+
.checkbox .checkbox-input:checked + .checkbox-box {
|
|
73
|
+
background-color: var(--color-accent);
|
|
74
|
+
border-color: var(--color-accent);
|
|
75
|
+
}
|
|
76
|
+
.checkbox .checkbox-input:checked + .checkbox-box.defaultChecked::after {
|
|
77
|
+
content: "";
|
|
78
|
+
width: 0.3rem;
|
|
79
|
+
height: 0.6rem;
|
|
80
|
+
position: relative;
|
|
81
|
+
bottom: 0.1rem;
|
|
82
|
+
border: solid var(--color-accent-ink);
|
|
83
|
+
border-width: 0 2px 2px 0;
|
|
84
|
+
transform: rotate(45deg);
|
|
85
|
+
}
|
|
86
|
+
.checkbox-label:hover .checkbox-box {
|
|
87
|
+
border-color: var(--color-accent);
|
|
88
|
+
}
|
|
89
|
+
.checkbox .checkbox-input:focus-visible + .checkbox-box {
|
|
90
|
+
outline: 2px solid var(--color-accent);
|
|
91
|
+
outline-offset: 2px;
|
|
92
|
+
}
|
|
93
|
+
</style>
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
export interface ControlProps {
|
|
3
|
+
/**
|
|
4
|
+
* Minimal will reset margin and remove border and box shadow from every element inside the slot
|
|
5
|
+
*/
|
|
6
|
+
appearance?: "normal" | "minimal";
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
withDefaults(defineProps<ControlProps>(), {
|
|
10
|
+
appearance: "normal",
|
|
11
|
+
});
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
<template>
|
|
15
|
+
<div class="control" :class="[appearance]">
|
|
16
|
+
<label v-if="$attrs.label" class="control-label">{{ $attrs.label }}</label>
|
|
17
|
+
<div class="slot-wrapper" v-bind="$attrs">
|
|
18
|
+
<slot />
|
|
19
|
+
</div>
|
|
20
|
+
</div>
|
|
21
|
+
</template>
|
|
22
|
+
|
|
23
|
+
<style scoped>
|
|
24
|
+
.control {
|
|
25
|
+
margin: 0.5rem;
|
|
26
|
+
}
|
|
27
|
+
.control .control-label {
|
|
28
|
+
user-select: none;
|
|
29
|
+
}
|
|
30
|
+
.control.minimal {
|
|
31
|
+
margin: 0;
|
|
32
|
+
}
|
|
33
|
+
.control.minimal .slot-wrapper :first-child {
|
|
34
|
+
border: 0;
|
|
35
|
+
}
|
|
36
|
+
.control.minimal .slot-wrapper :first-child:focus {
|
|
37
|
+
box-shadow: none;
|
|
38
|
+
}
|
|
39
|
+
</style>
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed } from 'vue';
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
icon?: string;
|
|
6
|
+
text?: string;
|
|
7
|
+
size?: 'small' | 'medium' | 'large';
|
|
8
|
+
}
|
|
9
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
10
|
+
size: 'medium',
|
|
11
|
+
});
|
|
12
|
+
defineEmits(['click']);
|
|
13
|
+
|
|
14
|
+
const iconSize = computed(() => {
|
|
15
|
+
switch (props.size) {
|
|
16
|
+
case 'small':
|
|
17
|
+
return '2rem';
|
|
18
|
+
case 'large':
|
|
19
|
+
return '5rem';
|
|
20
|
+
default:
|
|
21
|
+
return '3rem';
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
</script>
|
|
25
|
+
<template>
|
|
26
|
+
<div class="dashed-container gradient-hover" @click="$emit('click')">
|
|
27
|
+
<orio-icon
|
|
28
|
+
v-if="icon"
|
|
29
|
+
:name="icon"
|
|
30
|
+
class="icon-class"
|
|
31
|
+
:size="iconSize"
|
|
32
|
+
/>
|
|
33
|
+
<span v-if="text" class="text-class" :size>{{ text }}</span>
|
|
34
|
+
</div>
|
|
35
|
+
</template>
|
|
36
|
+
|
|
37
|
+
<style scoped>
|
|
38
|
+
.dashed-container {
|
|
39
|
+
cursor: pointer;
|
|
40
|
+
border: 3px dashed var(--color-border);
|
|
41
|
+
border-radius: 4px;
|
|
42
|
+
padding: 0.5rem;
|
|
43
|
+
display: flex;
|
|
44
|
+
flex-direction: column;
|
|
45
|
+
justify-content: center;
|
|
46
|
+
align-items: center;
|
|
47
|
+
}
|
|
48
|
+
.dashed-container:hover .text-class {
|
|
49
|
+
color: var(--color-accent-hover);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.icon-class {
|
|
53
|
+
color: var(--color-muted);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.text-class {
|
|
57
|
+
color: var(--color-accent);
|
|
58
|
+
}
|
|
59
|
+
</style>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed } from 'vue';
|
|
3
|
+
import { nanoid } from 'nanoid';
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
month?: boolean;
|
|
7
|
+
}
|
|
8
|
+
defineProps<Props>();
|
|
9
|
+
|
|
10
|
+
const date = defineModel<string | null | undefined>('date', {
|
|
11
|
+
required: true,
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
const randomName = computed(() => `date-${nanoid(8)}`);
|
|
15
|
+
</script>
|
|
16
|
+
|
|
17
|
+
<template>
|
|
18
|
+
<orio-control-element class="date-picker" v-bind="$attrs">
|
|
19
|
+
<input
|
|
20
|
+
v-model="date"
|
|
21
|
+
:type="month ? 'month' : 'date'"
|
|
22
|
+
class="date-input"
|
|
23
|
+
:name="randomName"
|
|
24
|
+
/>
|
|
25
|
+
</orio-control-element>
|
|
26
|
+
</template>
|
|
27
|
+
|
|
28
|
+
<style scoped>
|
|
29
|
+
.date-picker *{cursor:pointer;width:100%}.date-picker-label{color:var(--color-text);display:inline-flex;flex-direction:column;font-size:.9rem;gap:.25rem}.date-input{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:var(--color-bg);border:1px solid var(--color-border);border-radius:4px;box-sizing:border-box;color:var(--color-text);font-size:.95rem;padding:.4rem .6rem;transition:border-color .2s ease,box-shadow .2s ease}.date-input:focus,.date-input:hover{border-color:var(--color-accent)}.date-input:focus{box-shadow:0 0 0 2px var(--color-accent-soft);outline:none}.date-input:disabled{background-color:var(--color-surface);color:var(--color-muted);cursor:not-allowed}.date-input::-webkit-calendar-picker-indicator{cursor:pointer;filter:invert(36%) sepia(65%) saturate(325%) hue-rotate(180deg);opacity:.7;-webkit-transition:opacity .2s ease;transition:opacity .2s ease}.date-input::-webkit-calendar-picker-indicator:hover{opacity:1}
|
|
30
|
+
</style>
|