({
- components: { LayoutTabs },
- setup() {
- return { args }
- },
- template: `
-
-
- Title: {{ activeItem.title }}
- ID: {{ activeItem.id }}
-
-
`
- }),
- args: {
- items: defaultItems
- }
-}
diff --git a/packages/ui-components/src/components/layout/Tabs.vue b/packages/ui-components/src/components/layout/Tabs.vue
deleted file mode 100644
index eb33975a3..000000000
--- a/packages/ui-components/src/components/layout/Tabs.vue
+++ /dev/null
@@ -1,37 +0,0 @@
-
-
-
-
- {{ item.title }}
-
-
-
-
-
-
diff --git a/packages/ui-components/src/components/layout/tabs/Horizontal.stories.ts b/packages/ui-components/src/components/layout/tabs/Horizontal.stories.ts
new file mode 100644
index 000000000..eb7c03521
--- /dev/null
+++ b/packages/ui-components/src/components/layout/tabs/Horizontal.stories.ts
@@ -0,0 +1,67 @@
+import type { Meta, StoryObj } from '@storybook/vue3'
+import LayoutTabsHorizontal from '~~/src/components/layout/tabs/Horizontal.vue'
+import type { LayoutPageTabItem } from '~~/src/helpers/layout/components'
+import { useStorybookVmodel } from '~~/src/composables/testing'
+
+const items: LayoutPageTabItem[] = [
+ { title: 'Models', id: 'models', count: 300 },
+ { title: 'Discussions', id: 'discussions' },
+ { title: 'Automations', id: 'automations', tag: 'New' },
+ { title: 'Settings', id: 'settings' },
+ {
+ title: 'Disabled Item',
+ id: 'disabled',
+ disabled: true,
+ disabledMessage: 'Example disabled message'
+ }
+]
+
+export default {
+ component: LayoutTabsHorizontal,
+ parameters: {
+ docs: {
+ description: {
+ component:
+ 'This component displays a set of horizontal tabs, allowing user interaction and selection.'
+ }
+ }
+ },
+ argTypes: {
+ items: {
+ description: 'Array of items to display in the tabs'
+ },
+ title: {
+ description: 'Title of the tabs, displayed above the tabs if provided'
+ },
+ activeItem: {
+ description: 'The active item model. Not required.'
+ },
+ 'update:activeItem': {
+ description: 'Event emitted when the active item changes',
+ type: 'function',
+ action: 'v-model:activeItem'
+ }
+ }
+} as Meta
+
+export const Default: StoryObj = {
+ render: (args, ctx) => ({
+ components: { LayoutTabsHorizontal },
+ setup() {
+ const { model } = useStorybookVmodel({ args, prop: 'activeItem', ctx })
+ return { args, model }
+ },
+ template: `
+
+
+ Title: {{ activeItem?.title }}
+ ID: {{ activeItem?.id }}
+
+
`
+ }),
+ args: {
+ items,
+ title: 'Tab Example',
+ activeItem: items[2]
+ }
+}
diff --git a/packages/ui-components/src/components/layout/tabs/Horizontal.vue b/packages/ui-components/src/components/layout/tabs/Horizontal.vue
new file mode 100644
index 000000000..1fc12fc53
--- /dev/null
+++ b/packages/ui-components/src/components/layout/tabs/Horizontal.vue
@@ -0,0 +1,241 @@
+
+
+
+
+
+
diff --git a/packages/ui-components/src/components/layout/tabs/Vertical.stories.ts b/packages/ui-components/src/components/layout/tabs/Vertical.stories.ts
new file mode 100644
index 000000000..d7099ff20
--- /dev/null
+++ b/packages/ui-components/src/components/layout/tabs/Vertical.stories.ts
@@ -0,0 +1,67 @@
+import type { Meta, StoryObj } from '@storybook/vue3'
+import LayoutTabsVertical from '~~/src/components/layout/tabs/Vertical.vue'
+import type { LayoutPageTabItem } from '~~/src/helpers/layout/components'
+import { useStorybookVmodel } from '~~/src/composables/testing'
+
+const items: LayoutPageTabItem[] = [
+ { title: 'Models', id: 'models', count: 300 },
+ { title: 'Discussions', id: 'discussions' },
+ { title: 'Automations', id: 'automations', tag: 'New' },
+ { title: 'Settings', id: 'settings' },
+ {
+ title: 'Disabled Item',
+ id: 'disabled',
+ disabled: true,
+ disabledMessage: 'Example disabled message'
+ }
+]
+
+export default {
+ component: LayoutTabsVertical,
+ parameters: {
+ docs: {
+ description: {
+ component:
+ 'This component displays a set of vertical tabs, allowing user interaction and selection.'
+ }
+ }
+ },
+ argTypes: {
+ items: {
+ description: 'Array of items to display in the tabs'
+ },
+ title: {
+ description: 'Title of the tabs, displayed above the tabs if provided'
+ },
+ activeItem: {
+ description: 'The active item model. Not required.'
+ },
+ 'update:activeItem': {
+ description: 'Event emitted when the active item changes',
+ type: 'function',
+ action: 'v-model:activeItem'
+ }
+ }
+} as Meta
+
+export const Default: StoryObj = {
+ render: (args, ctx) => ({
+ components: { LayoutTabsVertical },
+ setup() {
+ const { model } = useStorybookVmodel({ args, prop: 'activeItem', ctx })
+ return { args, model }
+ },
+ template: `
+
+
+ Title: {{ activeItem?.title }}
+ ID: {{ activeItem?.id }}
+
+
`
+ }),
+ args: {
+ items,
+ title: 'Tab Example',
+ activeItem: items[2]
+ }
+}
diff --git a/packages/ui-components/src/components/layout/tabs/Vertical.vue b/packages/ui-components/src/components/layout/tabs/Vertical.vue
new file mode 100644
index 000000000..46e1f130a
--- /dev/null
+++ b/packages/ui-components/src/components/layout/tabs/Vertical.vue
@@ -0,0 +1,107 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/ui-components/src/composables/form/textInput.ts b/packages/ui-components/src/composables/form/textInput.ts
index 011ed9671..2f129aacb 100644
--- a/packages/ui-components/src/composables/form/textInput.ts
+++ b/packages/ui-components/src/composables/form/textInput.ts
@@ -46,7 +46,10 @@ export function useTextInputCore
(params: {
})
const labelClasses = computed(() => {
- const classParts = ['block label text-foreground-2 mb-2']
+ const classParts = [
+ 'flex label mb-1.5',
+ unref(props.color) === 'foundation' ? 'text-foreground' : 'text-foreground-2'
+ ]
if (!unref(props.showLabel)) {
classParts.push('sr-only')
}
@@ -58,7 +61,7 @@ export function useTextInputCore(params: {
const classParts: string[] = [
'focus:outline-none disabled:cursor-not-allowed disabled:bg-foundation-disabled',
'disabled:text-disabled-muted placeholder:text-foreground-2',
- 'rounded'
+ 'rounded-md'
]
return classParts.join(' ')
@@ -80,7 +83,9 @@ export function useTextInputCore(params: {
const color = unref(props.color)
if (color === 'foundation') {
- classParts.push('bg-foundation shadow-sm hover:shadow')
+ classParts.push(
+ 'bg-foundation !border border-outline-3 focus:border-outline-1 focus:!outline-0 focus:!ring-0'
+ )
} else if (color === 'transparent') {
classParts.push('bg-transparent')
} else {
@@ -108,7 +113,7 @@ export function useTextInputCore(params: {
hasHelpTip.value ? `${unref(props.name)}-${internalHelpTipId.value}` : undefined
)
const helpTipClasses = computed((): string => {
- const classParts = ['mt-2 text-xs sm:text-sm']
+ const classParts = ['mt-2 text-xs']
classParts.push(error.value ? 'text-danger' : 'text-foreground-2')
return classParts.join(' ')
})
diff --git a/packages/ui-components/src/helpers/layout/components.ts b/packages/ui-components/src/helpers/layout/components.ts
index f02f3ae26..3fc4b4f28 100644
--- a/packages/ui-components/src/helpers/layout/components.ts
+++ b/packages/ui-components/src/helpers/layout/components.ts
@@ -1,4 +1,5 @@
import type { ConcreteComponent } from 'vue'
+import type { PropAnyComponent } from '~~/src/helpers/common/components'
export enum GridListToggleValue {
Grid = 'grid',
@@ -13,9 +14,11 @@ export type LayoutTabItem = {
export type LayoutPageTabItem = {
title: string
id: I
- icon?: ConcreteComponent
count?: number
tag?: string
+ icon?: PropAnyComponent
+ disabled?: boolean
+ disabledMessage?: string
}
export type LayoutMenuItem = {
diff --git a/packages/ui-components/src/lib.ts b/packages/ui-components/src/lib.ts
index 13d95defa..5d092cc68 100644
--- a/packages/ui-components/src/lib.ts
+++ b/packages/ui-components/src/lib.ts
@@ -20,6 +20,7 @@ import CommonVimeoEmbed from '~~/src/components/common/VimeoEmbed.vue'
import FormCardButton from '~~/src/components/form/CardButton.vue'
import FormCheckbox from '~~/src/components/form/Checkbox.vue'
import FormRadio from '~~/src/components/form/Radio.vue'
+import FormRadioGroup from '~~/src/components/form/RadioGroup.vue'
import FormTextArea from '~~/src/components/form/TextArea.vue'
import FormTextInput from '~~/src/components/form/TextInput.vue'
import * as ValidationHelpers from '~~/src/helpers/common/validation'
@@ -53,8 +54,8 @@ import {
} from '~~/src/composables/common/window'
import LayoutMenu from '~~/src/components/layout/Menu.vue'
import type { LayoutMenuItem, LayoutTabItem } from '~~/src/helpers/layout/components'
-import LayoutTabs from '~~/src/components/layout/Tabs.vue'
-import LayoutPageTabs from '~~/src/components/layout/PageTabs.vue'
+import LayoutTabsHoriztonal from '~~/src/components/layout/tabs/Horizontal.vue'
+import LayoutTabsVertical from '~~/src/components/layout/tabs/Vertical.vue'
import LayoutTable from '~~/src/components/layout/Table.vue'
import InfiniteLoading from '~~/src/components/InfiniteLoading.vue'
import type { InfiniteLoaderState } from '~~/src/helpers/global/components'
@@ -81,6 +82,7 @@ import type {
} from '~~/src/composables/form/fileUpload'
import { UniqueFileTypeSpecifier, prettyFileSize } from '~~/src/helpers/form/file'
import type { FileTypeSpecifier } from '~~/src/helpers/form/file'
+import type { PropAnyComponent } from '~~/src/helpers/common/components'
export * from '~~/src/helpers/common/error'
import CommonLoadingIcon from '~~/src/components/common/loading/Icon.vue'
import type { AvatarUser, AvatarUserWithId } from '~~/src/composables/user/avatar'
@@ -107,6 +109,7 @@ export {
FormCardButton,
FormCheckbox,
FormRadio,
+ FormRadioGroup,
FormTextArea,
FormTextInput,
FormSwitch,
@@ -134,8 +137,8 @@ export {
useOnBeforeWindowUnload,
useResponsiveHorizontalDirectionCalculation,
LayoutMenu,
- LayoutTabs,
- LayoutPageTabs,
+ LayoutTabsHoriztonal,
+ LayoutTabsVertical,
LayoutTable,
InfiniteLoading,
LayoutPanel,
@@ -162,5 +165,6 @@ export type {
FileTypeSpecifier,
AvatarUser,
AvatarUserWithId,
- LayoutPageTabItem
+ LayoutPageTabItem,
+ PropAnyComponent
}
diff --git a/workspace.code-workspace b/workspace.code-workspace
index 945bf53be..70a46b020 100644
--- a/workspace.code-workspace
+++ b/workspace.code-workspace
@@ -126,7 +126,9 @@
"Vue.volar",
"bradlc.vscode-tailwindcss",
"stylelint.vscode-stylelint",
- "cpylua.language-postcss"
+ "cpylua.language-postcss",
+ "graphql.vscode-graphql",
+ "graphql.vscode-graphql-syntax"
],
// List of extensions recommended by VS Code that should not be recommended for users of this workspace.
"unwantedRecommendations": ["octref.vetur", "vscode.typescript-language-features"]