design-system-dashboard-devmunity 0.4.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 (53) 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/themes/components/input.js +5 -0
  5. package/app/assets/css/themes/index.js +1 -0
  6. package/app/components/BaseButton.vue +3 -0
  7. package/app/components/Colors.mdx +42 -0
  8. package/app/components/Indroduction.mdx +100 -0
  9. package/app/components/a/button/a-button-avatar-dropdown.stories.ts +83 -0
  10. package/app/components/a/button/a-button-avatar-dropdown.vue +41 -17
  11. package/app/components/a/button/a-button-navigation.stories.ts +66 -0
  12. package/app/components/a/button/a-button-navigation.vue +57 -0
  13. package/app/components/a/card/a-card-inner.stories.ts +89 -0
  14. package/app/components/a/card/a-card-inner.vue +26 -13
  15. package/app/components/a/dropdown/a-dropdown-avatar.stories.ts +160 -0
  16. package/app/components/a/dropdown/a-dropdown-avatar.vue +75 -33
  17. package/app/components/a/pill/a-pill.stories.ts +91 -0
  18. package/app/components/a/pill/a-pill.vue +63 -42
  19. package/app/components/b/badge/b-badge.stories.ts +77 -0
  20. package/app/components/b/badge/b-badge.vue +43 -19
  21. package/app/components/b/card/b-card.stories.ts +120 -0
  22. package/app/components/b/card/b-card.vue +49 -32
  23. package/app/components/b/modal/b-modal.stories.ts +210 -0
  24. package/app/components/b/modal/b-modal.vue +125 -81
  25. package/app/components/c/badge/c-badge-status.stories.ts +72 -0
  26. package/app/components/c/badge/c-badge-status.vue +36 -15
  27. package/app/components/c/modal/c-modal-danger.stories.ts +112 -0
  28. package/app/components/c/modal/c-modal-danger.vue +60 -41
  29. package/app/components/d/action-buttons/d-action-buttons.stories.ts +90 -0
  30. package/app/components/d/action-buttons/d-action-buttons.vue +121 -0
  31. package/app/components/d/card/d-card-header.stories.ts +123 -0
  32. package/app/components/d/card/d-card-header.vue +59 -41
  33. package/app/components/d/upload/d-upload-avatar.stories.ts +66 -0
  34. package/app/components/d/upload/d-upload-avatar.vue +49 -30
  35. package/app/types/index.ts +1 -0
  36. package/app/types/semantic-colors.type.ts +3 -0
  37. package/app/utils/util-get-colors-from-css.ts +53 -0
  38. package/nuxt.config.ts +11 -16
  39. package/package.json +84 -68
  40. package/.editorconfig +0 -13
  41. package/.github/workflows/release.yml +0 -36
  42. package/.husky/commit-msg +0 -1
  43. package/.husky/pre-commit +0 -0
  44. package/.prettierrc +0 -24
  45. package/.storybook/main.js +0 -25
  46. package/.storybook/preview.js +0 -13
  47. package/.vscode/settings.json +0 -28
  48. package/CHANGELOG.md +0 -55
  49. package/app/Introduction.mdx +0 -44
  50. package/app/components/a/button/a-button-back.vue +0 -33
  51. package/app/components/d/d-action-buttons.vue +0 -99
  52. package/commitlint.config.js +0 -8
  53. package/tsconfig.json +0 -8
@@ -0,0 +1,89 @@
1
+ import type { Meta, StoryObj } from '@storybook/vue3'
2
+ import ACardInner from './a-card-inner.vue'
3
+ import BCard from '@/components/b/card/b-card.vue'
4
+
5
+ const meta: Meta<typeof ACardInner> = {
6
+ title: 'Atoms/Card/ACardInner',
7
+ component: ACardInner,
8
+ tags: ['autodocs'],
9
+ parameters: {
10
+ docs: {
11
+ description: {
12
+ component:
13
+ 'A simple component that allows you to apply standardized padding in all directions to separate content from its edges.',
14
+ },
15
+ },
16
+ },
17
+ argTypes: {
18
+ class: {
19
+ control: 'text',
20
+ description: 'CSS classes to apply to the card',
21
+ },
22
+ as: {
23
+ control: 'select',
24
+ options: ['div', 'section', 'article', 'main', 'aside'],
25
+ description: 'HTML element to render as',
26
+ },
27
+ },
28
+ }
29
+
30
+ type Story = StoryObj<typeof meta>
31
+ export default meta
32
+
33
+ export const Default: Story = {
34
+ args: {
35
+ as: 'div',
36
+ },
37
+ render: (args) => ({
38
+ components: { ACardInner },
39
+ setup() {
40
+ return { args }
41
+ },
42
+ template: '<a-card-inner v-bind="args">Default Content</a-card-inner>',
43
+ }),
44
+ }
45
+
46
+ export const CustomClass: Story = {
47
+ args: {
48
+ as: 'section',
49
+ class: 'bg-neutral-100 p-4 rounded-lg border border-neutral-200',
50
+ },
51
+ render: (args) => ({
52
+ components: { ACardInner },
53
+ setup() {
54
+ return { args }
55
+ },
56
+ template: '<a-card-inner v-bind="args">Content with custom styling</a-card-inner>',
57
+ }),
58
+ }
59
+
60
+ export const WithBCardComponent: Story = {
61
+ name: 'With BCard component',
62
+ args: {
63
+ as: 'section',
64
+ },
65
+ render: (args) => ({
66
+ components: { ACardInner, BCard },
67
+ setup() {
68
+ return { args }
69
+ },
70
+ template: `
71
+ <b-card>
72
+ <template #header>
73
+ <a-card-inner>
74
+ <h2>BCard Header</h2>
75
+ </a-card-inner>
76
+ </template>
77
+
78
+ <a-card-inner>
79
+ <p>BCard Inner content</p>
80
+ </a-card-inner>
81
+
82
+ <template #footer>
83
+ <a-card-inner>
84
+ <p>BCard Footer</p>
85
+ </a-card-inner>
86
+ </template>
87
+ </b-card>`,
88
+ }),
89
+ }
@@ -1,20 +1,33 @@
1
- <script setup>
2
- import { twMerge } from 'tailwind-merge'
1
+ <script setup lang="ts">
2
+ import type { Component } from 'vue'
3
+ import { twMerge, type ClassNameValue } from 'tailwind-merge'
3
4
  import { card } from '~/assets/css/themes/index.js'
4
5
 
5
- const props = defineProps({
6
- class: {
7
- type: [String, Object, Array],
8
- default: '',
9
- required: false,
10
- },
11
- as: {
12
- type: String,
13
- default: 'div',
14
- required: false,
15
- },
6
+ interface Props {
7
+ /**
8
+ * The class to apply to the card
9
+ * @type ClassNameValue
10
+ */
11
+ class?: ClassNameValue
12
+ /**
13
+ * The component to render the card as
14
+ * @type string | Component
15
+ */
16
+ as?: string | Component
17
+ }
18
+
19
+ const props = withDefaults(defineProps<Props>(), {
20
+ class: '',
21
+ as: 'div',
16
22
  })
17
23
 
24
+ defineSlots<{
25
+ /**
26
+ * The default slot in which all the content
27
+ */
28
+ default(): any
29
+ }>()
30
+
18
31
  const cardClasses = computed(() => twMerge(card.slots.body, props.class))
19
32
  </script>
20
33
 
@@ -0,0 +1,160 @@
1
+ import type { Meta, StoryObj } from '@storybook/vue3'
2
+ import ADropdownAvatar, { type DropdownItem } from './a-dropdown-avatar.vue'
3
+ import UButton from '@nuxt/ui/components/Button.vue'
4
+ import UAvatar from '@nuxt/ui/components/Avatar.vue'
5
+ import AButtonAvatarDropdown from '@/components/a/button/a-button-avatar-dropdown.vue'
6
+
7
+ const meta = {
8
+ title: 'Atoms/Dropdown/ADropdownAvatar',
9
+ component: ADropdownAvatar,
10
+ parameters: {
11
+ docs: {
12
+ description: {
13
+ component:
14
+ 'A specialized dropdown menu for displaying user information. Typically used in conjunction with the [AButtonAvatarDropdown](?path=/docs/atoms-button-abuttonavatardropdown--docs) component.',
15
+ },
16
+ },
17
+ },
18
+ render: (args) => ({
19
+ components: { ADropdownAvatar, UButton, UAvatar, AButtonAvatarDropdown },
20
+ setup() {
21
+ return { args }
22
+ },
23
+ template: `
24
+ <ADropdownAvatar v-bind="args">
25
+ <UButton icon="i-lucide-menu" color="neutral" variant="outline" />
26
+ </ADropdownAvatar>
27
+ `,
28
+ }),
29
+ argTypes: {
30
+ userName: {
31
+ control: 'text',
32
+ },
33
+ userEmail: {
34
+ control: 'text',
35
+ },
36
+ userTo: {
37
+ control: 'text',
38
+ },
39
+ items: {
40
+ control: 'object',
41
+ },
42
+ },
43
+ } satisfies Meta<typeof ADropdownAvatar>
44
+
45
+ export default meta
46
+
47
+ type Story = StoryObj<typeof meta>
48
+
49
+ const items: DropdownItem[][] = [
50
+ [{ label: 'Profile', icon: 'heroicons:user' }],
51
+ [{ label: 'Settings', icon: 'heroicons:cog-6-tooth' }],
52
+ ]
53
+
54
+ export const Default: Story = {
55
+ args: {
56
+ userName: 'John Doe',
57
+ userEmail: 'john@example.com',
58
+ items,
59
+ },
60
+ }
61
+
62
+ export const WithAButtonAvatarDropdownComponent: Story = {
63
+ name: 'With AButtonAvatarDropdown Component',
64
+ render: (args) => ({
65
+ components: { ADropdownAvatar, AButtonAvatarDropdown },
66
+ setup() {
67
+ return { args }
68
+ },
69
+ template: `
70
+ <ADropdownAvatar v-bind="args">
71
+ <span>
72
+ <AButtonAvatarDropdown
73
+ label="Custom Trigger"
74
+ color="success"
75
+ trailing-icon="i-heroicons-chevron-down"
76
+ src="https://avatars.githubusercontent.com/u/739984?v=4"
77
+ />
78
+ </span>
79
+ </ADropdownAvatar>
80
+ `,
81
+ }),
82
+ args: {
83
+ userName: 'Admin',
84
+ userEmail: 'admin@sys.com',
85
+ userTo: '/profile',
86
+ items: [
87
+ ...items,
88
+ [
89
+ {
90
+ label: 'Logout',
91
+ icon: 'heroicons:arrow-left-on-rectangle',
92
+ color: 'error',
93
+ trailingIcon: 'heroicons:arrow-left-on-rectangle',
94
+ },
95
+ ],
96
+ ],
97
+ },
98
+ }
99
+
100
+ export const Minimal: Story = {
101
+ args: {
102
+ userName: 'Jane',
103
+ items: [[{ label: 'Logout', icon: 'heroicons:arrow-left-on-rectangle' }]],
104
+ },
105
+ }
106
+
107
+ export const WithNavigation: Story = {
108
+ args: {
109
+ userName: 'Admin user',
110
+ userEmail: 'admin@system.com',
111
+ userTo: '/profile',
112
+ items,
113
+ },
114
+ }
115
+
116
+ export const CustomTrigger: Story = {
117
+ render: (args) => ({
118
+ components: { ADropdownAvatar, UButton },
119
+ setup() {
120
+ return { args }
121
+ },
122
+ template: `
123
+ <ADropdownAvatar v-bind="args">
124
+ <UButton label="Custom Trigger" color="primary" trailing-icon="i-heroicons-chevron-down" />
125
+ </ADropdownAvatar>
126
+ `,
127
+ }),
128
+ args: {
129
+ userName: 'Custom User',
130
+ items,
131
+ },
132
+ }
133
+
134
+ export const CustomUserSlot: Story = {
135
+ render: (args) => ({
136
+ components: { ADropdownAvatar, UAvatar },
137
+ setup() {
138
+ return { args }
139
+ },
140
+ template: `
141
+ <ADropdownAvatar v-bind="args">
142
+ <UAvatar src="https://avatars.githubusercontent.com/u/739984?v=4" alt="Avatar" size="sm" />
143
+ <template #user="{ item }">
144
+ <div class="flex items-center gap-2 p-1">
145
+ <UAvatar :src="'https://ui.nuxt.com/avatars/' + item.userName + '.png'" size="xs" />
146
+ <div class="text-start">
147
+ <p class="text-sm font-bold text-primary-600">Admin: {{ item.userName }}</p>
148
+ <p class="text-xs text-gray-500">{{ item.userEmail }}</p>
149
+ </div>
150
+ </div>
151
+ </template>
152
+ </ADropdownAvatar>
153
+ `,
154
+ }),
155
+ args: {
156
+ userName: 'benjamincanac',
157
+ userEmail: 'ben@nuxt.com',
158
+ items,
159
+ },
160
+ }
@@ -1,44 +1,86 @@
1
- <script lang="jsx" setup>
2
- const props = defineProps({
3
- items: {
4
- type: Array,
5
- default: () => [],
6
- required: true,
7
- },
8
- userName: {
9
- type: String,
10
- default: '',
11
- required: true,
12
- },
13
- userEmail: {
14
- type: String,
15
- default: '',
16
- required: false,
17
- },
18
- userTo: {
19
- type: String,
20
- default: '',
21
- required: false,
22
- },
1
+ <script lang="ts" setup>
2
+ import type { UDropdownMenu } from '#components'
3
+
4
+ export interface DropdownItem {
5
+ userName?: string
6
+ userEmail?: string
7
+ slot?: string
8
+ onSelect?: () => void
9
+ // label?: string
10
+ // icon?: string
11
+ // to?: string
12
+ [key: string]: any
13
+ }
14
+
15
+ interface Props {
16
+ /**
17
+ * Array of dropdown items to display, grouped in arrays
18
+ */
19
+ items: DropdownItem[][]
20
+ /**
21
+ * Display name of the user
22
+ */
23
+ userName: string
24
+ /**
25
+ * Email address of the user
26
+ */
27
+ userEmail?: string
28
+ /**
29
+ * Navigation path for user profile
30
+ */
31
+ userTo?: string
32
+ }
33
+
34
+ const props = withDefaults(defineProps<Props>(), {
35
+ items: () => [],
36
+ userEmail: '',
37
+ userTo: '',
23
38
  })
24
39
 
40
+ defineSlots<{
41
+ /**
42
+ * Default slot content for dropdown trigger (REQUIRED)
43
+ */
44
+ default(): any
45
+ /**
46
+ * User information slot
47
+ */
48
+ user(props: { item: DropdownItem }): any
49
+ /**
50
+ * Additional dynamic slots
51
+ */
52
+ [name: string]: any
53
+ }>()
54
+
55
+ // Validate that the default slot is provided
56
+ const slots = useSlots()
57
+ if (!slots.default) {
58
+ console.warn('ADropdownAvatar: The default slot is required')
59
+ }
60
+
25
61
  const uiStyles = computed(() => {
26
- if (props.userTo) return {}
62
+ const ui = {
63
+ content: 'min-w-40',
64
+ }
65
+ if (props.userTo) return ui
27
66
  return {
67
+ ...ui,
28
68
  item: 'data-highlighted:first:before:bg-transparent data-[state=open]:first:before:bg-transparent',
29
69
  }
30
70
  })
31
71
 
32
- const itemsComputed = computed(() => [
33
- {
34
- userName: props.userName,
35
- userEmail: props.userEmail,
36
- slot: 'user',
37
- onSelect: () => {
38
- const path = props.userTo
39
- if (path) navigateTo(path)
72
+ const itemsComputed = computed((): DropdownItem[][] => [
73
+ [
74
+ {
75
+ userName: props.userName,
76
+ userEmail: props.userEmail,
77
+ slot: 'user',
78
+ onSelect: () => {
79
+ const path = props.userTo
80
+ if (path) navigateTo(path)
81
+ },
40
82
  },
41
- },
83
+ ],
42
84
  ...props.items,
43
85
  ])
44
86
  </script>
@@ -47,7 +89,7 @@ const itemsComputed = computed(() => [
47
89
  <UDropdownMenu :items="itemsComputed" :ui="uiStyles">
48
90
  <slot />
49
91
 
50
- <template #user="{ item }">
92
+ <template #user="{ item }: { item: any }">
51
93
  <div class="text-start">
52
94
  <span class="text-highlighted block text-lg font-bold">
53
95
  {{ item.userName }}
@@ -0,0 +1,91 @@
1
+ import type { Meta, StoryObj } from '@storybook/vue3'
2
+ import { fn } from '@storybook/test'
3
+ import APill, { type BadgeSize, type BadgeVariant } from './a-pill.vue'
4
+ import { semanticColors } from '@/utils/util-get-colors-from-css'
5
+
6
+ const meta = {
7
+ title: 'Atoms/Pill/APill',
8
+ component: APill,
9
+ parameters: {
10
+ docs: {
11
+ description: {
12
+ component:
13
+ "A small, rounded label used to display brief information. Typically used to show a user's name next to their avatar.",
14
+ },
15
+ },
16
+ },
17
+ argTypes: {
18
+ variant: {
19
+ control: 'select',
20
+ options: ['solid', 'outline', 'soft', 'subtle'] satisfies BadgeVariant[],
21
+ },
22
+ size: {
23
+ control: 'select',
24
+ options: ['xs', 'sm', 'md', 'lg', 'xl'] satisfies BadgeSize[],
25
+ },
26
+ color: {
27
+ control: 'select',
28
+ options: Object.keys(semanticColors),
29
+ },
30
+ 'onOn-close': {
31
+ table: { disable: true },
32
+ },
33
+ },
34
+ args: {
35
+ 'onOn-close': fn(),
36
+ },
37
+ } satisfies Meta<typeof APill>
38
+
39
+ export default meta
40
+
41
+ type Story = StoryObj<typeof meta>
42
+
43
+ export const Default: Story = {
44
+ args: {
45
+ label: 'Design System',
46
+ src: 'https://avatars.githubusercontent.com/u/739984?v=4',
47
+ color: 'neutral',
48
+ variant: 'outline',
49
+ size: 'sm',
50
+ hasAvatar: true,
51
+ isClosable: true,
52
+ },
53
+ }
54
+
55
+ export const SolidPrimary: Story = {
56
+ args: {
57
+ ...Default.args,
58
+ label: 'Primary Solid',
59
+ color: 'primary',
60
+ variant: 'solid',
61
+ size: 'md',
62
+ },
63
+ }
64
+
65
+ export const SoftSuccess: Story = {
66
+ args: {
67
+ ...Default.args,
68
+ label: 'Success Soft',
69
+ color: 'success',
70
+ variant: 'soft',
71
+ size: 'md',
72
+ },
73
+ }
74
+
75
+ export const NoAvatar: Story = {
76
+ args: {
77
+ ...Default.args,
78
+ label: 'Just Text',
79
+ hasAvatar: false,
80
+ color: 'neutral',
81
+ variant: 'subtle',
82
+ },
83
+ }
84
+
85
+ export const NotClosable: Story = {
86
+ args: {
87
+ ...Default.args,
88
+ label: 'Permanent',
89
+ isClosable: false,
90
+ },
91
+ }
@@ -1,45 +1,66 @@
1
- <script lang="jsx" setup>
2
- const props = defineProps({
3
- src: {
4
- type: String,
5
- default: '/media/img/user-unknown.png',
6
- required: false,
7
- },
8
- label: {
9
- type: String,
10
- default: '',
11
- required: false,
12
- },
13
- color: {
14
- type: String,
15
- default: 'neutral',
16
- required: false,
17
- },
18
- variant: {
19
- type: String,
20
- default: 'outline',
21
- required: false,
22
- validator: (value) => ['solid', 'outline', 'soft', 'subtle'].includes(value),
23
- },
24
- size: {
25
- type: String,
26
- default: 'sm',
27
- required: false,
28
- validator: (value) => ['xs', 'sm', 'md', 'lg', 'xl'].includes(value),
29
- },
30
- hasAvatar: {
31
- type: Boolean,
32
- default: true,
33
- required: false,
34
- },
35
- isClosable: {
36
- type: Boolean,
37
- default: true,
38
- required: false,
39
- },
1
+ <script lang="ts" setup>
2
+ import type { UBadge, UButton } from '#components'
3
+ import { computed } from 'vue'
4
+
5
+ // Derived types from Nuxt UI components
6
+ export type BadgeSize = InstanceType<typeof UBadge>['$props']['size']
7
+ export type BadgeColor = InstanceType<typeof UBadge>['$props']['color']
8
+ export type BadgeVariant = InstanceType<typeof UBadge>['$props']['variant']
9
+
10
+ interface Props {
11
+ /**
12
+ * Avatar image source URL
13
+ */
14
+ src?: string
15
+ /**
16
+ * Text label to display
17
+ */
18
+ label?: string
19
+ /**
20
+ * Color theme of the badge
21
+ */
22
+ color?: BadgeColor
23
+ /**
24
+ * Visual style variant
25
+ */
26
+ variant?: BadgeVariant
27
+ /**
28
+ * Size of the badge
29
+ */
30
+ size?: BadgeSize
31
+ /**
32
+ * Whether to show avatar
33
+ */
34
+ hasAvatar?: boolean
35
+ /**
36
+ * Whether to show close button
37
+ */
38
+ isClosable?: boolean
39
+ }
40
+
41
+ const props = withDefaults(defineProps<Props>(), {
42
+ src: '/media/img/user-unknown.png',
43
+ label: '',
44
+ color: 'neutral',
45
+ variant: 'outline',
46
+ size: 'sm',
47
+ hasAvatar: true,
48
+ isClosable: true,
40
49
  })
41
50
 
42
- const emit = defineEmits('on-close')
51
+ const emit = defineEmits<{
52
+ /**
53
+ * Emitted when the close button is clicked
54
+ */
55
+ (e: 'on-close'): void
56
+ }>()
57
+
58
+ defineSlots<{
59
+ /**
60
+ * The default slot in which all the content
61
+ */
62
+ default(): any
63
+ }>()
43
64
 
44
65
  const trailingIconSize = computed(() => {
45
66
  const sizeMap = {
@@ -72,7 +93,7 @@ const textSize = computed(() => {
72
93
  src,
73
94
  size,
74
95
  }
75
- : null
96
+ : undefined
76
97
  "
77
98
  :label="label"
78
99
  :trailing-icon="isClosable ? 'heroicons:x-mark' : ''"
@@ -82,6 +103,7 @@ const textSize = computed(() => {
82
103
  }"
83
104
  :color="color"
84
105
  :variant="variant"
106
+ :class="{ 'pl-2': !hasAvatar, 'pr-3': !isClosable }"
85
107
  class="rounded-full font-normal"
86
108
  >
87
109
  <template #trailing>
@@ -90,7 +112,6 @@ const textSize = computed(() => {
90
112
  :size="size"
91
113
  :ui="{
92
114
  leadingIcon: trailingIconSize,
93
- icon: trailingIconSize,
94
115
  }"
95
116
  :color="color"
96
117
  :variant="variant === 'solid' ? 'solid' : 'icon'"