design-system-dashboard-devmunity 0.3.0 → 1.0.1

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 (57) hide show
  1. package/README.md +33 -31
  2. package/app/app.config.ts +2 -1
  3. package/app/app.vue +21 -3
  4. package/app/assets/css/settings/base.css +33 -33
  5. package/app/assets/css/themes/components/badge.js +7 -0
  6. package/app/assets/css/themes/components/input.js +5 -0
  7. package/app/assets/css/themes/index.js +2 -1
  8. package/app/components/BaseButton.vue +3 -0
  9. package/app/components/Colors.mdx +42 -0
  10. package/app/components/Indroduction.mdx +100 -0
  11. package/app/components/a/button/a-button-avatar-dropdown.stories.ts +83 -0
  12. package/app/components/a/button/a-button-avatar-dropdown.vue +41 -17
  13. package/app/components/a/button/a-button-navigation.stories.ts +66 -0
  14. package/app/components/a/button/a-button-navigation.vue +57 -0
  15. package/app/components/a/card/a-card-inner.stories.ts +89 -0
  16. package/app/components/a/card/a-card-inner.vue +38 -0
  17. package/app/components/a/dropdown/a-dropdown-avatar.stories.ts +160 -0
  18. package/app/components/a/dropdown/a-dropdown-avatar.vue +75 -33
  19. package/app/components/a/pill/a-pill.stories.ts +91 -0
  20. package/app/components/a/pill/a-pill.vue +63 -42
  21. package/app/components/b/badge/b-badge.stories.ts +77 -0
  22. package/app/components/b/badge/b-badge.vue +55 -0
  23. package/app/components/b/card/b-card.stories.ts +120 -0
  24. package/app/components/b/card/b-card.vue +49 -32
  25. package/app/components/b/modal/b-modal.stories.ts +210 -0
  26. package/app/components/b/modal/b-modal.vue +125 -81
  27. package/app/components/c/badge/c-badge-status.stories.ts +72 -0
  28. package/app/components/c/badge/c-badge-status.vue +60 -0
  29. package/app/components/c/modal/c-modal-danger.stories.ts +112 -0
  30. package/app/components/c/modal/c-modal-danger.vue +60 -41
  31. package/app/components/d/action-buttons/d-action-buttons.stories.ts +90 -0
  32. package/app/components/d/action-buttons/d-action-buttons.vue +121 -0
  33. package/app/components/d/card/d-card-header.stories.ts +123 -0
  34. package/app/components/{b/card/b-card-header.vue → d/card/d-card-header.vue} +62 -44
  35. package/app/components/d/upload/d-upload-avatar.stories.ts +66 -0
  36. package/app/components/d/upload/d-upload-avatar.vue +95 -0
  37. package/app/pages/test.vue +27 -16
  38. package/app/types/index.ts +1 -0
  39. package/app/types/semantic-colors.type.ts +3 -0
  40. package/app/utils/util-get-colors-from-css.ts +53 -0
  41. package/nuxt.config.ts +11 -16
  42. package/package.json +84 -68
  43. package/.editorconfig +0 -13
  44. package/.github/workflows/release.yml +0 -36
  45. package/.husky/commit-msg +0 -1
  46. package/.husky/pre-commit +0 -0
  47. package/.prettierrc +0 -24
  48. package/.storybook/main.js +0 -25
  49. package/.storybook/preview.js +0 -13
  50. package/.vscode/settings.json +0 -28
  51. package/CHANGELOG.md +0 -46
  52. package/app/Introduction.mdx +0 -44
  53. package/app/components/a/button/a-button-back.vue +0 -33
  54. package/app/components/b/card/b-card-inner.vue +0 -25
  55. package/app/components/d/d-action-buttons.vue +0 -99
  56. package/commitlint.config.js +0 -8
  57. package/tsconfig.json +0 -8
@@ -0,0 +1,77 @@
1
+ import type { Meta, StoryObj } from '@storybook/vue3'
2
+ import BBadge, { type BadgeColor, type BadgeVariant } from './b-badge.vue'
3
+
4
+ const meta = {
5
+ title: 'Bases/Badge/BBadge',
6
+ component: BBadge,
7
+ parameters: {
8
+ docs: {
9
+ description: {
10
+ component:
11
+ 'A small, rounded label used to display brief information. Typically used to show the status of an item.',
12
+ },
13
+ },
14
+ },
15
+ argTypes: {
16
+ value: {
17
+ control: 'select',
18
+ options: ['active', 'inactive', 'error', ''] satisfies string[],
19
+ },
20
+ // @ts-ignore
21
+ name: {
22
+ name: '[slot name]',
23
+ description: 'Any dynamic slot for the badge',
24
+ },
25
+ },
26
+ } satisfies Meta<typeof BBadge>
27
+
28
+ export default meta
29
+
30
+ type Story = StoryObj<typeof meta>
31
+
32
+ const styles = {
33
+ active: {
34
+ color: 'primary' as BadgeColor,
35
+ variant: 'solid' as BadgeVariant,
36
+ icon: 'heroicons:check-20-solid',
37
+ },
38
+ inactive: { color: 'neutral' as BadgeColor, variant: 'subtle' as BadgeVariant },
39
+ error: {
40
+ color: 'error' as BadgeColor,
41
+ variant: 'soft' as BadgeVariant,
42
+ icon: 'heroicons:x-circle-20-solid',
43
+ },
44
+ }
45
+
46
+ export const Default: Story = {
47
+ args: {
48
+ label: 'Default Style',
49
+ value: 'unknown',
50
+ styles,
51
+ defaultStyle: { color: 'neutral' as BadgeColor, variant: 'outline' as BadgeVariant },
52
+ },
53
+ }
54
+
55
+ export const Active: Story = {
56
+ args: {
57
+ ...Default.args,
58
+ label: 'Active Item',
59
+ value: 'active',
60
+ },
61
+ }
62
+
63
+ export const Inactive: Story = {
64
+ args: {
65
+ ...Default.args,
66
+ label: 'Inactive Item',
67
+ value: 'inactive',
68
+ },
69
+ }
70
+
71
+ export const ErrorState: Story = {
72
+ args: {
73
+ ...Default.args,
74
+ label: 'Error Occurred',
75
+ value: 'error',
76
+ },
77
+ }
@@ -0,0 +1,55 @@
1
+ <script lang="ts" setup>
2
+ import type { UBadge } from '#components'
3
+ import { badge } from '~/assets/css/themes'
4
+
5
+ /**
6
+ * Badge style configuration object
7
+ */
8
+ export type BadgeSize = InstanceType<typeof UBadge>['$props']['size']
9
+ export type BadgeColor = InstanceType<typeof UBadge>['$props']['color']
10
+ export type BadgeVariant = InstanceType<typeof UBadge>['$props']['variant']
11
+
12
+ interface BadgeStyles {
13
+ [key: string]: Partial<InstanceType<typeof UBadge>['$props']> & { [key: string]: any }
14
+ }
15
+
16
+ interface Props {
17
+ /**
18
+ * The label text to display on the badge
19
+ */
20
+ label: string
21
+ /**
22
+ * The active style variant key of the `styles` object
23
+ */
24
+ value?: string
25
+ /**
26
+ * Custom style settings for different badge variants. The options are derived from the props expected by `UBadge`
27
+ */
28
+ styles?: BadgeStyles
29
+ /**
30
+ * Default style configuration for the badge
31
+ */
32
+ defaultStyle?: BadgeStyles[string]
33
+ }
34
+
35
+ const props = withDefaults(defineProps<Props>(), {
36
+ value: '',
37
+ styles: () => ({}),
38
+ defaultStyle: () => badge,
39
+ })
40
+
41
+ defineSlots<{
42
+ /**
43
+ * Any dynamic slots for the badge content
44
+ */
45
+ [name: string]: any
46
+ }>()
47
+ </script>
48
+
49
+ <template>
50
+ <UBadge :label="label" v-bind="styles[value] ?? props.defaultStyle">
51
+ <template v-for="(_, name) in $slots" v-slot:[name]="slotData">
52
+ <slot :name="name" v-bind="slotData" />
53
+ </template>
54
+ </UBadge>
55
+ </template>
@@ -0,0 +1,120 @@
1
+ import type { Meta, StoryObj } from '@storybook/vue3'
2
+ import BCard, { type CardVariant } from './b-card.vue'
3
+ import ACardInner from '@/components/a/card/a-card-inner.vue'
4
+
5
+ const meta = {
6
+ title: 'Bases/Card/BCard',
7
+ component: BCard,
8
+ parameters: {
9
+ docs: {
10
+ description: {
11
+ component:
12
+ 'A base card component that serves as a fundamental design element for grouping and organizing content. Its style is based on a border with rounded corners and is structured into distinct header, body, and footer sections.',
13
+ },
14
+ },
15
+ },
16
+ argTypes: {
17
+ as: {
18
+ control: 'text',
19
+ },
20
+ variant: {
21
+ control: 'select',
22
+ options: ['outline', 'solid', 'soft', 'ghost'] satisfies CardVariant[],
23
+ },
24
+ hasBodyExpanded: {
25
+ control: 'boolean',
26
+ },
27
+ hasFooterDivider: {
28
+ control: 'boolean',
29
+ },
30
+ },
31
+ } satisfies Meta<typeof BCard>
32
+
33
+ export default meta
34
+
35
+ type Story = StoryObj<typeof meta>
36
+
37
+ const template = `
38
+ <BCard v-bind="args">
39
+ <template #header>
40
+ <ACardInner class="font-bold">Header</ACardInner>
41
+ </template>
42
+ <ACardInner>Body content goes here. It can be long.</ACardInner>
43
+ <template #footer>
44
+ <ACardInner class="text-sm text-gray-500">Footer content</ACardInner>
45
+ </template>
46
+ </BCard>
47
+ `
48
+
49
+ export const Default: Story = {
50
+ args: {
51
+ variant: 'outline',
52
+ hasBodyExpanded: false,
53
+ hasFooterDivider: true,
54
+ },
55
+ render: (args) => ({
56
+ components: { BCard, ACardInner },
57
+ setup() {
58
+ return { args }
59
+ },
60
+ template,
61
+ }),
62
+ }
63
+
64
+ export const Solid: Story = {
65
+ args: {
66
+ ...Default.args,
67
+ variant: 'solid',
68
+ },
69
+ render: (args) => ({
70
+ components: { BCard, ACardInner },
71
+ setup() {
72
+ return { args }
73
+ },
74
+ template,
75
+ }),
76
+ }
77
+
78
+ export const WithExpandedBody: Story = {
79
+ args: {
80
+ ...Default.args,
81
+ hasBodyExpanded: true,
82
+ },
83
+ render: (args) => ({
84
+ components: { BCard, ACardInner },
85
+ setup() {
86
+ return { args }
87
+ },
88
+ template,
89
+ }),
90
+ }
91
+
92
+ export const WithoutFooterDivider: Story = {
93
+ args: {
94
+ variant: 'outline',
95
+ hasBodyExpanded: true,
96
+ hasFooterDivider: true,
97
+ class: 'h-64', // Force height to show expansion
98
+ },
99
+ render: (args) => ({
100
+ components: { BCard, ACardInner },
101
+ setup() {
102
+ return { args }
103
+ },
104
+ template: `
105
+ <div class="h-64">
106
+ <BCard v-bind="args">
107
+ <template #header>
108
+ <ACardInner>Fixed Header</ACardInner>
109
+ </template>
110
+ <ACardInner>
111
+ <p v-for="i in 10" :key="i">Scrollable content line {{ i }}</p>
112
+ </ACardInner>
113
+ <template #footer>
114
+ <ACardInner>Fixed Footer</ACardInner>
115
+ </template>
116
+ </BCard>
117
+ </div>
118
+ `,
119
+ }),
120
+ }
@@ -1,39 +1,61 @@
1
- <script lang="jsx" setup>
1
+ <script lang="ts" setup>
2
2
  import { computed } from 'vue'
3
3
  import { twMerge } from 'tailwind-merge'
4
+ import type { UCard } from '#components'
4
5
 
5
- const props = defineProps({
6
- as: {
7
- type: String,
8
- default: 'div',
9
- required: false,
10
- },
11
- variant: {
12
- default: 'outline',
13
- type: String,
14
- required: false,
15
- },
16
- hasBodyExpanded: {
17
- type: Boolean,
18
- default: false,
19
- required: false,
20
- },
21
- hasFooterDivider: {
22
- type: Boolean,
23
- default: true,
24
- required: false,
25
- },
26
- ui: {
27
- type: Object,
28
- default: null,
29
- required: false,
30
- },
6
+ // Types
7
+ export type CardVariant = InstanceType<typeof UCard>['$props']['variant']
8
+
9
+ interface Props {
10
+ /**
11
+ * The HTML tag to render the card as
12
+ * @default 'div'
13
+ */
14
+ as?: string
15
+ /**
16
+ * The visual variant of the card
17
+ * @default 'outline'
18
+ */
19
+ variant?: CardVariant
20
+ /**
21
+ * Whether the card body should expand to fill available space
22
+ * @default false
23
+ */
24
+ hasBodyExpanded?: boolean
25
+ /**
26
+ * Whether to show a divider above the footer
27
+ * @default true
28
+ */
29
+ hasFooterDivider?: boolean
30
+ }
31
+
32
+ const props = withDefaults(defineProps<Props>(), {
33
+ as: 'article',
34
+ variant: 'outline',
35
+ hasBodyExpanded: false,
36
+ hasFooterDivider: true,
31
37
  })
32
38
 
39
+ defineSlots<{
40
+ /**
41
+ * The default slot in which all the content
42
+ */
43
+ default(): any
44
+ /**
45
+ * The header slot
46
+ */
47
+ header(): any
48
+ /**
49
+ * The footer slot
50
+ */
51
+ footer(): any
52
+ }>()
53
+
33
54
  // Computed
34
55
 
35
56
  const uiStyles = computed(() => {
36
57
  const baseStyles = {
58
+ root: '',
37
59
  header: 'p-0 sm:p-0',
38
60
  body: 'p-0 sm:p-0',
39
61
  footer: 'p-0 sm:p-0 sm:px-0',
@@ -51,11 +73,6 @@ const uiStyles = computed(() => {
51
73
  baseStyles.body = twMerge(baseStyles.body, 'border-none')
52
74
  }
53
75
 
54
- // Apply custom ui prop (overrides previous styles)
55
- if (props.ui) {
56
- Object.assign(baseStyles, props.ui)
57
- }
58
-
59
76
  return baseStyles
60
77
  })
61
78
  </script>
@@ -0,0 +1,210 @@
1
+ import { reactive } from 'vue'
2
+ import type { Meta, StoryObj } from '@storybook/vue3'
3
+ import type { SemanticColors } from '@/types'
4
+ import { fn } from '@storybook/test'
5
+ import { semanticColors } from '@/utils/util-get-colors-from-css'
6
+ import UButton from '@nuxt/ui/components/Button.vue'
7
+ import UForm from '@nuxt/ui/components/Form.vue'
8
+ import UFormField from '@nuxt/ui/components/FormField.vue'
9
+ import UInput from '@nuxt/ui/components/Input.vue'
10
+ import BModal from './b-modal.vue'
11
+
12
+ const meta = {
13
+ title: 'Bases/Modal/BModal',
14
+ component: BModal,
15
+ parameters: {
16
+ docs: {
17
+ description: {
18
+ component:
19
+ 'Base modal component. Its structure consists of a blank body, and a header and footer with a predefined design. It can contain text, any other element in the body, or a form, for which the schema and state can be defined.',
20
+ },
21
+ },
22
+ },
23
+ argTypes: {
24
+ primaryButtonColor: {
25
+ control: 'select',
26
+ options: Object.keys(semanticColors) as SemanticColors[],
27
+ },
28
+ 'onOn-click-primary-button': {
29
+ table: { disable: true },
30
+ },
31
+ 'onOn-click-secondary-button': {
32
+ table: { disable: true },
33
+ },
34
+ 'onOn-submit': {
35
+ table: { disable: true },
36
+ },
37
+ modelValue: {
38
+ control: 'boolean',
39
+ name: 'isOpen',
40
+ description: 'Whether the modal is open',
41
+ table: {
42
+ category: 'v-model',
43
+ },
44
+ },
45
+ },
46
+ args: {
47
+ 'onOn-click-primary-button': fn(),
48
+ 'onOn-click-secondary-button': fn(),
49
+ 'onOn-submit': fn(),
50
+ },
51
+ render: (args) =>
52
+ ({
53
+ components: { BModal, UButton },
54
+ setup() {
55
+ return { args }
56
+ },
57
+ template: `
58
+ <div>
59
+ <UButton label="Open Modal" @click="args.modelValue = true" />
60
+ <BModal v-bind="args" v-model="args.modelValue" />
61
+ </div>
62
+ `,
63
+ }) as any,
64
+ } satisfies Meta<typeof BModal>
65
+
66
+ export default meta
67
+
68
+ type Story = StoryObj<typeof meta>
69
+
70
+ export const Default: Story = {
71
+ args: {
72
+ title: 'Modal Title',
73
+ description: 'This is a description of the modal.',
74
+ modelValue: false,
75
+ primaryButtonText: 'Save',
76
+ secondaryButtonText: 'Cancel',
77
+ primaryButtonColor: 'primary',
78
+ },
79
+ }
80
+
81
+ export const WithTextContent: Story = {
82
+ args: {
83
+ ...Default.args,
84
+ title: 'Text Content',
85
+ description: 'Modal with simple text content',
86
+ text: 'This is the body text content passed via the "text" prop.',
87
+ modelValue: false,
88
+ },
89
+ }
90
+
91
+ export const WithSlot: Story = {
92
+ args: {
93
+ ...Default.args,
94
+ title: 'With Slot',
95
+ description: 'Modal with custom content',
96
+ modelValue: false,
97
+ },
98
+ render: (args) =>
99
+ ({
100
+ components: { BModal, UButton },
101
+ setup() {
102
+ return { args }
103
+ },
104
+ template: `
105
+ <div>
106
+ <UButton label="Open Modal" @click="args.modelValue = true" />
107
+ <BModal v-bind="args" v-model="args.modelValue">
108
+ <div class="space-y-4">
109
+ <p class="text-sm text-gray-600">
110
+ This is a custom content area where you can place any elements,
111
+ such as forms, images, or additional text.
112
+ </p>
113
+ <div class="p-4 bg-blue-50 border border-blue-200 rounded-lg">
114
+ <span class="text-blue-700 font-medium">Note:</span>
115
+ Custom slots allow for maximum flexibility in modal design.
116
+ </div>
117
+ </div>
118
+ </BModal>
119
+ </div>
120
+ `,
121
+ }) as any,
122
+ }
123
+
124
+ export const WithForm: Story = {
125
+ args: {
126
+ ...Default.args,
127
+ title: 'With Form',
128
+ description: 'Modal with form',
129
+ modelValue: false,
130
+ class: 'w-74',
131
+ },
132
+ render: (args) =>
133
+ ({
134
+ components: {
135
+ BModal,
136
+ UButton: UButton as any,
137
+ UForm: UForm as any,
138
+ UFormField: UFormField as any,
139
+ UInput: UInput as any,
140
+ },
141
+ setup() {
142
+ const schema = {
143
+ email: {
144
+ type: 'string',
145
+ required: true,
146
+ format: 'email',
147
+ },
148
+ password: {
149
+ type: 'string',
150
+ required: true,
151
+ format: 'password',
152
+ },
153
+ }
154
+ const state = reactive({
155
+ email: '',
156
+ password: '',
157
+ })
158
+ return { args, schema, state }
159
+ },
160
+ template: `
161
+ <div>
162
+ <UButton label="Open Modal" @click="args.modelValue = true" />
163
+ <BModal v-bind="args" v-model="args.modelValue">
164
+ <UForm :schema="schema" :state="state" class="space-y-4 w-full">
165
+ <UFormField label="Email" name="email" class="w-full">
166
+ <UInput v-model="state.email" type="email" />
167
+ </UFormField>
168
+
169
+ <UFormField label="Password" name="password" class="w-full">
170
+ <UInput v-model="state.password" type="password" />
171
+ </UFormField>
172
+ </UForm>
173
+ </BModal>
174
+ </div>
175
+ `,
176
+ }) as any,
177
+ }
178
+
179
+ export const ButtonsBlock: Story = {
180
+ args: {
181
+ ...Default.args,
182
+ title: 'Block Buttons',
183
+ description: 'Buttons are full width',
184
+ hasButtonsBlock: true,
185
+ modelValue: false,
186
+ text: 'Check the buttons below.',
187
+ },
188
+ }
189
+
190
+ export const NoFooter: Story = {
191
+ args: {
192
+ ...Default.args,
193
+ title: 'No Footer',
194
+ description: 'This modal has no footer',
195
+ hasFooter: false,
196
+ modelValue: false,
197
+ text: 'Footer is hidden.',
198
+ },
199
+ }
200
+
201
+ export const DisabledPrimary: Story = {
202
+ args: {
203
+ ...Default.args,
204
+ title: 'Disabled Action',
205
+ description: 'Primary button is disabled',
206
+ isPrimaryButtonDisabled: true,
207
+ modelValue: false,
208
+ text: 'You cannot save right now.',
209
+ },
210
+ }