windly-ui 1.0.1 → 1.0.2

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.
Files changed (39) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +57 -33
  3. package/package.json +47 -15
  4. package/ui-library/dist/module.d.mts +3 -0
  5. package/ui-library/dist/module.json +8 -0
  6. package/ui-library/dist/module.mjs +22 -0
  7. package/ui-library/dist/types.d.mts +7 -0
  8. package/ui-library/components/Accordion.vue +0 -118
  9. package/ui-library/components/Avatar.vue +0 -121
  10. package/ui-library/components/Badge.vue +0 -116
  11. package/ui-library/components/Breadcrumbs.vue +0 -138
  12. package/ui-library/components/Button.vue +0 -154
  13. package/ui-library/components/Card.vue +0 -84
  14. package/ui-library/components/Checkbox.vue +0 -148
  15. package/ui-library/components/CodeBlock.vue +0 -99
  16. package/ui-library/components/ColorPicker.vue +0 -302
  17. package/ui-library/components/Date.vue +0 -240
  18. package/ui-library/components/Dialog.vue +0 -187
  19. package/ui-library/components/Divider.vue +0 -78
  20. package/ui-library/components/Drawer.vue +0 -67
  21. package/ui-library/components/Dropdown.vue +0 -248
  22. package/ui-library/components/FileUploader.vue +0 -330
  23. package/ui-library/components/Icon.vue +0 -82
  24. package/ui-library/components/Image.vue +0 -78
  25. package/ui-library/components/Input.vue +0 -531
  26. package/ui-library/components/Radio.vue +0 -356
  27. package/ui-library/components/ScrollArea.vue +0 -43
  28. package/ui-library/components/Select.vue +0 -309
  29. package/ui-library/components/Stepper.vue +0 -361
  30. package/ui-library/components/TabPanel.vue +0 -25
  31. package/ui-library/components/Table.vue +0 -152
  32. package/ui-library/components/Tabs.vue +0 -71
  33. package/ui-library/components/Textarea.vue +0 -233
  34. package/ui-library/components/Toggle.vue +0 -319
  35. package/ui-library/components/Tooltip.vue +0 -200
  36. package/ui-library/components/plugin.ts +0 -8
  37. package/ui-library/components/uiProps.ts +0 -11
  38. package/ui-library/components/useUiClasses.ts +0 -159
  39. package/ui-library/module.ts +0 -20
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Your Name
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 CHANGED
@@ -142,6 +142,30 @@ No unnecessary abstractions or overhead.
142
142
 
143
143
  ## Example Usage
144
144
 
145
+ ```vue
146
+ <template>
147
+ <UICard bordered>
148
+ <template #header>
149
+ <h3 class="text-lg font-semibold">Welcome</h3>
150
+ </template>
151
+
152
+ <p>This is a simple card using the UI library.</p>
153
+ # Tailwind-Based UI Library for Nuxt 4
154
+
155
+ A lightweight, modular UI component library built specifically for Nuxt 4 applications, powered by Tailwind CSS.
156
+
157
+ ## Overview
158
+
159
+ This UI library is designed to accelerate frontend development by providing reusable, customizable, and auto-imported components.
160
+
161
+ ## Key Features
162
+
163
+ - Tailwind-first design: components accept Tailwind utility classes directly.
164
+ - Auto-imported components via Nuxt 4's component system.
165
+ - Lightweight, framework-native Vue 3 components.
166
+
167
+ ## Example Usage
168
+
145
169
  ```vue
146
170
  <template>
147
171
  <UICard bordered>
@@ -158,51 +182,51 @@ No unnecessary abstractions or overhead.
158
182
  </template>
159
183
  ```
160
184
 
161
- ---
185
+ ## Local test and packaging
162
186
 
163
- ## Advanced Example
187
+ 1. Build the module (the `prepare` script runs this automatically on `npm pack` and `npm publish`):
164
188
 
165
- ```vue
166
- <UITabs v-model="activeTab">
167
- <UITabPanel name="first" label="Overview">
168
- <p>Overview content</p>
169
- </UITabPanel>
170
-
171
- <UITabPanel name="second" label="Details">
172
- <p>Details content</p>
173
- </UITabPanel>
174
- </UITabs>
189
+ ```bash
190
+ npm run build
175
191
  ```
176
192
 
177
- ---
193
+ 2. Create a local tarball for testing:
178
194
 
179
- ## Goals
195
+ ```bash
196
+ npm pack
197
+ ```
180
198
 
181
- * Provide a clean alternative to heavy UI frameworks
182
- * Showcase advanced Vue patterns in a simple way
183
- * Enable rapid prototyping with Tailwind
184
- * Keep the system small, composable, and extensible
199
+ 3. In another project, install the generated tarball (replace the filename with the one produced by `npm pack`):
185
200
 
186
- ---
201
+ ```bash
202
+ npm install /path/to/windly-ui-1.0.2.tgz
203
+ ```
187
204
 
188
- ## Future Improvements
205
+ 4. Add the module to `nuxt.config` in the consuming project:
189
206
 
190
- * Theme system (dark mode, tokens)
191
- * Component playground with live prop editing
192
- * Accessibility improvements (ARIA support)
193
- * Animation utilities
194
- * Plugin system for extending components
207
+ ```ts
208
+ export default defineNuxtConfig({
209
+ modules: [
210
+ 'windly-ui'
211
+ ]
212
+ })
213
+ ```
195
214
 
196
- ---
215
+ ## Publish
216
+
217
+ 1. Bump the version in `package.json`.
218
+ 2. Ensure you are logged into npm: `npm login`.
219
+ 3. Publish:
197
220
 
198
- ## Summary
221
+ ```bash
222
+ npm publish --access public
223
+ ```
199
224
 
200
- This UI library is a developer-focused toolkit for building modern Nuxt applications quickly without sacrificing flexibility or control.
225
+ ## Notes
201
226
 
202
- It combines:
227
+ - The package auto-registers components under the `UI` prefix (for example `UIButton`, `UICard`).
228
+ - The `plugin.ts` also registers the most common components explicitly.
203
229
 
204
- * Vue 3 reactivity
205
- * Tailwind CSS flexibility
206
- * Clean and consistent component design
230
+ ## License
207
231
 
208
- Build fast, customize freely, and stay in control.
232
+ MIT
package/package.json CHANGED
@@ -1,27 +1,59 @@
1
1
  {
2
2
  "name": "windly-ui",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
+ "description": "A Nuxt 4 module providing a small Tailwind-based UI component library",
4
5
  "type": "module",
5
- "private": false,
6
-
6
+ "main": "./ui-library/dist/module.mjs",
7
+ "exports": {
8
+ ".": "./ui-library/dist/module.mjs"
9
+ },
7
10
  "files": [
8
- "ui-library",
9
- "README.md"
11
+ "ui-library/dist",
12
+ "README.md",
13
+ "LICENSE"
10
14
  ],
11
-
12
15
  "scripts": {
13
- "build": "nuxt build",
14
- "dev": "nuxt dev",
15
- "generate": "nuxt generate",
16
- "preview": "nuxt preview",
17
- "postinstall": "nuxt prepare"
16
+ "build": "nuxt-module-build ui-library",
17
+ "prepare": "npm run build",
18
+ "prepublishOnly": "npm run build",
19
+ "pack": "npm pack",
20
+ "pack:local": "npm run build && npm pack --pack-destination ./pack",
21
+ "lint": "echo \"no lint configured\" && exit 0"
22
+ },
23
+ "repository": {
24
+ "type": "git",
25
+ "url": "git+https://github.com/your-username/windly-ui.git"
26
+ },
27
+ "keywords": [
28
+ "nuxt-module",
29
+ "nuxt",
30
+ "vue",
31
+ "tailwind",
32
+ "ui-library"
33
+ ],
34
+ "author": "Your Name <you@example.com>",
35
+ "license": "MIT",
36
+ "bugs": {
37
+ "url": "https://github.com/your-username/windly-ui/issues"
18
38
  },
19
- "dependencies": {
39
+ "homepage": "https://github.com/your-username/windly-ui#readme",
40
+ "peerDependencies": {
20
41
  "nuxt": "^4.2.2",
21
- "vue": "^3.5.26",
22
- "vue-router": "^4.6.4"
42
+ "vue": "^3.5.26"
23
43
  },
24
44
  "devDependencies": {
45
+ "@nuxt/module-builder": "^latest",
25
46
  "@nuxtjs/tailwindcss": "^6.14.0"
47
+ },
48
+ "typesVersions": {
49
+ "*": {
50
+ "*": ["./ui-library/dist/*"]
51
+ }
52
+ },
53
+ "engines": {
54
+ "node": ">=16"
55
+ },
56
+ "publishConfig": {
57
+ "access": "public"
26
58
  }
27
- }
59
+ }
@@ -0,0 +1,3 @@
1
+ declare const _default: any;
2
+
3
+ export { _default as default };
@@ -0,0 +1,8 @@
1
+ {
2
+ "name": "ui-library",
3
+ "configKey": "uiLibrary",
4
+ "builder": {
5
+ "@nuxt/module-builder": "1.0.2",
6
+ "unbuild": "3.6.1"
7
+ }
8
+ }
@@ -0,0 +1,22 @@
1
+ import { defineNuxtModule, createResolver, addComponentsDir } from '@nuxt/kit';
2
+
3
+ const module$1 = defineNuxtModule({
4
+ meta: {
5
+ name: "ui-library",
6
+ configKey: "uiLibrary"
7
+ },
8
+ setup(_, nuxt) {
9
+ const resolver = createResolver(import.meta.url);
10
+ addComponentsDir({
11
+ path: resolver.resolve("./components"),
12
+ prefix: "UI",
13
+ // This will prefix components with <UIButton />, <UICard /> etc.
14
+ pathPrefix: false,
15
+ // Don't include folder names as prefix
16
+ extensions: ["vue"],
17
+ watch: nuxt.options.dev
18
+ });
19
+ }
20
+ });
21
+
22
+ export { module$1 as default };
@@ -0,0 +1,7 @@
1
+ import type { NuxtModule } from '@nuxt/schema'
2
+
3
+ import type { default as Module } from './module.mjs'
4
+
5
+ export type ModuleOptions = typeof Module extends NuxtModule<infer O> ? Partial<O> : Record<string, any>
6
+
7
+ export { default } from './module.mjs'
@@ -1,118 +0,0 @@
1
- <script setup lang="ts">
2
- import { ref, computed, watch } from 'vue'
3
-
4
- type TriggerType = 'click' | 'hover'
5
- type ToggleSide = 'left' | 'right'
6
-
7
- const props = withDefaults(
8
- defineProps<{
9
- title?: string
10
- caption?: string
11
- modelValue?: boolean
12
- defaultOpen?: boolean
13
- trigger?: TriggerType
14
- disabled?: boolean
15
- dense?: boolean
16
- toggleSide?: ToggleSide
17
- margin?: boolean
18
- }>(),
19
- {
20
- defaultOpen: false,
21
- trigger: 'click',
22
- disabled: false,
23
- dense: false,
24
- toggleSide: 'right',
25
- margin: true
26
- }
27
- )
28
-
29
- const emit = defineEmits < {
30
- (e: 'update:modelValue', value: boolean): void
31
- }> ()
32
-
33
- const isControlled = computed(() => props.modelValue !== undefined)
34
- const isOpen = ref < boolean > (props.defaultOpen ?? false)
35
-
36
- watch(
37
- () => props.modelValue,
38
- val => {
39
- if (isControlled.value && typeof val === 'boolean') {
40
- isOpen.value = val
41
- }
42
- },
43
- { immediate: true }
44
- )
45
-
46
- const setOpen = (val: boolean) => {
47
- if (props.disabled) return
48
- isOpen.value = val
49
- emit('update:modelValue', val)
50
- }
51
-
52
- const toggle = () => setOpen(!isOpen.value)
53
-
54
- const headerEvents = computed<Record<string, (() => void) | undefined>>(() => {
55
- if (props.trigger === 'hover') {
56
- return {
57
- mouseenter: () => setOpen(true),
58
- mouseleave: () => setOpen(false),
59
- click: undefined
60
- }
61
- }
62
-
63
- return {
64
- mouseenter: undefined,
65
- mouseleave: undefined,
66
- click: toggle
67
- }
68
- })
69
-
70
- </script>
71
-
72
- <template>
73
- <div class="flex flex-col rounded-md border transition-opacity" :class="[
74
- dense ? 'text-sm' : '',
75
- disabled ? 'opacity-50 pointer-events-none' : ''
76
- ]">
77
-
78
- <!-- Header -->
79
- <div v-on="headerEvents" class="flex items-center cursor-pointer select-none gap-3" :class="[
80
- margin
81
- ? (dense ? 'p-2' : 'p-3')
82
- : (dense ? 'py-2' : 'py-3'),
83
- isOpen ? 'border-b' : ''
84
- ]">
85
-
86
- <!-- Toggle (left) -->
87
- <UIButton v-if="toggleSide === 'left'" icon="chevron_right" flat dense class="transition-transform"
88
- :class="isOpen ? 'rotate-90' : ''" />
89
-
90
- <!-- Header content -->
91
- <div class="flex-1">
92
- <slot name="header">
93
- <h3 class="font-semibold">
94
- {{ title }}
95
- </h3>
96
- <p v-if="caption" class="text-gray-500 text-xs">
97
- {{ caption }}
98
- </p>
99
- </slot>
100
- </div>
101
-
102
- <!-- Toggle (right) -->
103
- <UIButton v-if="toggleSide === 'right'" icon="chevron_right" flat dense class="transition-transform"
104
- :class="isOpen ? 'rotate-90' : ''" />
105
- </div>
106
-
107
- <!-- Content -->
108
- <transition enter-active-class="transition-all duration-300 ease-out"
109
- leave-active-class="transition-all duration-200 ease-in" enter-from-class="opacity-0 max-h-0"
110
- enter-to-class="opacity-100 max-h-[1000px]" leave-from-class="opacity-100 max-h-[1000px]"
111
- leave-to-class="opacity-0 max-h-0">
112
- <div v-show="isOpen" class="overflow-hidden pb-3" :class="margin ? 'px-3' : 'px-none'">
113
- <slot />
114
- </div>
115
- </transition>
116
-
117
- </div>
118
- </template>
@@ -1,121 +0,0 @@
1
- <script setup lang="ts">
2
- defineOptions({
3
- inheritAttrs: false,
4
- })
5
-
6
- import { computed } from 'vue'
7
- import { useUiClasses } from './useUiClasses'
8
- import { uiProps } from "./uiProps";
9
-
10
- const props = defineProps({
11
- image: { type: String, default: '' },
12
- altText: { type: String, default: 'Avatar' },
13
- initials: { type: String, default: '' },
14
- borderColor: { type: String, default: '#fff' },
15
- borderThickness: { type: Number, default: 2 },
16
- shadow: { type: Boolean, default: false },
17
- ...uiProps
18
- })
19
-
20
- // Use composable
21
- const {
22
- // props
23
- contentClass,
24
- contentStyle,
25
- textColor,
26
- color,
27
- size,
28
- // computed
29
- colorClass,
30
- computedContentClass,
31
- isColorDark,
32
- isHex,
33
- isTailwindColor
34
- } = useUiClasses(props)
35
-
36
- const sizeClass = computed(() => {
37
- const map = {
38
- sm: 'avatar-sm',
39
- md: 'avatar-md',
40
- lg: 'avatar-lg'
41
- } as const
42
-
43
- return map[size.value as keyof typeof map]
44
- })
45
-
46
- const shadowClass = computed(() =>
47
- props.shadow ? 'shadow-lg' : ''
48
- )
49
-
50
- const avatarClass = computed(() => {
51
- return [
52
- computedContentClass.value,
53
- sizeClass.value,
54
- shadowClass.value,
55
- isTailwindColor(textColor.value) ? `text-${textColor.value}` : `text-${isColorDark(color.value) ? 'white' : 'gray-700'}`,
56
- isTailwindColor(color.value) ? `bg-${color.value}` : ""
57
- ].filter((c) => !["", "!"].includes(c))
58
- })
59
-
60
- const avatarStyle = computed(() => ({
61
- backgroundColor: isHex(color.value) && color.value,
62
- color: isHex(textColor.value) && textColor.value,
63
- borderColor: props.borderColor,
64
- borderWidth: `${props.borderThickness}px`,
65
- borderStyle: 'solid',
66
- ...contentStyle.value
67
- }))
68
- </script>
69
-
70
- <template>
71
- <div class="avatar" :class="avatarClass" :style="avatarStyle">
72
- <img v-if="image" :src="image" :alt="altText" class="avatar-image" />
73
- <span v-else class="avatar-initials">
74
- {{ initials || '..' }}
75
- </span>
76
- </div>
77
- </template>
78
-
79
- <style scoped>
80
- .avatar {
81
- display: flex;
82
- justify-content: center;
83
- align-items: center;
84
- border-radius: 50%;
85
- overflow: hidden;
86
- position: relative;
87
- text-align: center;
88
- }
89
-
90
- .avatar-sm {
91
- width: 40px;
92
- height: 40px;
93
- font-size: 0.875rem;
94
- }
95
-
96
- .avatar-md {
97
- width: 60px;
98
- height: 60px;
99
- font-size: 1rem;
100
- }
101
-
102
- .avatar-lg {
103
- width: 80px;
104
- height: 80px;
105
- font-size: 1.25rem;
106
- }
107
-
108
- .avatar-image {
109
- width: 100%;
110
- height: 100%;
111
- object-fit: cover;
112
- }
113
-
114
- .avatar-initials {
115
- font-weight: bold;
116
- }
117
-
118
- .shadow-lg {
119
- box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
120
- }
121
- </style>
@@ -1,116 +0,0 @@
1
- <script setup lang="ts">
2
- defineOptions({
3
- inheritAttrs: false,
4
- })
5
-
6
- import { useSlots, computed } from 'vue'
7
- import { useUiClasses } from './useUiClasses'
8
- import { uiProps } from "./uiProps";
9
-
10
- const slots = useSlots()
11
-
12
- const props = defineProps({
13
- content: { type: String, default: '' },
14
- icon: { type: String, default: '' },
15
- position: { type: String, default: 'superscript' },
16
- show: { type: Boolean, default: true },
17
- ...uiProps
18
- })
19
-
20
- // Use composable
21
- const {
22
- // props
23
- contentClass,
24
- contentStyle,
25
- color,
26
- size,
27
- rounded,
28
- // computed
29
- colorClass,
30
- computedContentClass,
31
- isColorDark,
32
- isHex,
33
- isTailwindColor
34
- } = useUiClasses(props)
35
-
36
- const hasSlots = computed(() => {
37
- return !!slots.default?.()
38
- })
39
-
40
- const sizeClass = computed(() => {
41
- const map = {
42
- xs: 'w-auto min-w-2 h-2.5 text-[7px]',
43
- sm: 'w-auto min-w-3 h-3 text-[10px]',
44
- md: 'w-auto min-w-4 h-4 text-xs',
45
- lg: 'w-auto min-w-6 h-6 text-sm'
46
- } as const
47
-
48
- return map[size.value as keyof typeof map]
49
- })
50
-
51
- const roundedClass = computed(() => {
52
- const map = {
53
- xs: 'rounded-xs',
54
- sm: 'rounded-sm',
55
- md: 'rounded',
56
- lg: 'rounded-lg',
57
- full: 'rounded-full'
58
- } as const
59
-
60
- return map[rounded.value as keyof typeof map]
61
- })
62
-
63
- const positionClasses = computed(() =>
64
- props.position === 'superscript' && hasSlots.value
65
- ? 'absolute top-0 right-0 transform translate-x-1/2 -translate-y-1/2'
66
- : ''
67
- )
68
-
69
- const badgeClass = computed(() => {
70
- return [
71
- 'inline-flex items-center justify-center font-bold',
72
- computedContentClass.value,
73
- sizeClass.value,
74
- roundedClass.value,
75
- positionClasses.value,
76
- isColorDark(color.value) ? 'text-white' : 'text-gray-700',
77
- isTailwindColor(color.value) ? `bg-${color.value}` : ""
78
- ].filter((c) => !["", "!"].includes(c))
79
- })
80
-
81
- const badgeStyle = computed(() => {
82
- const style = contentStyle.value
83
- return {
84
- backgroundColor: isHex(color.value) ? color.value : undefined,
85
- ...(typeof style === 'object' && style ? style : {})
86
- }
87
- })
88
- </script>
89
-
90
- <template>
91
- <div class="flex">
92
- <div v-if="hasSlots" class="relative">
93
- <slot />
94
- <span v-if="show" :class="[
95
- badgeClass
96
- ]" :style="[badgeStyle]">
97
- <span v-if="icon" class="p-1 material-icons">{{ icon }}</span>
98
- <span v-if="content" class="p-1">{{ content }}</span>
99
- </span>
100
- </div>
101
- <div v-else>
102
- <span v-if="show" :class="[
103
- badgeClass
104
- ]" :style="[badgeStyle]">
105
- <span v-if="icon" class="p-1 material-icons">{{ icon }}</span>
106
- <span v-if="content" class="p-1">{{ content }}</span>
107
- </span>
108
- </div>
109
- </div>
110
- </template>
111
-
112
- <style scoped>
113
- .material-icons {
114
- font-size: 1em;
115
- }
116
- </style>