diff --git a/apps/frontend/src/pages/[type]/[id]/settings/server.vue b/apps/frontend/src/pages/[type]/[id]/settings/server.vue index af88816222..cac014ed6f 100644 --- a/apps/frontend/src/pages/[type]/[id]/settings/server.vue +++ b/apps/frontend/src/pages/[type]/[id]/settings/server.vue @@ -26,15 +26,13 @@ >Languages (optional) - @@ -166,10 +164,10 @@ import { injectModrinthClient, injectNotificationManager, injectProjectPageContext, + MultiSelect, StyledInput, UnsavedChangesPopup, } from '@modrinth/ui' -import { Multiselect } from 'vue-multiselect' import CompatibilityCard from '~/components/ui/project-settings/CompatibilityCard.vue' diff --git a/packages/assets/styles/defaults.scss b/packages/assets/styles/defaults.scss index d77191e349..6b6a3d59ce 100644 --- a/packages/assets/styles/defaults.scss +++ b/packages/assets/styles/defaults.scss @@ -158,11 +158,11 @@ h3 { } ::-webkit-scrollbar-thumb { - background: var(--color-button-bg); + background: var(--color-scrollbar); } // Firefox scrollbar * { scrollbar-width: thin; - scrollbar-color: var(--color-button-bg) transparent; + scrollbar-color: var(--color-scrollbar) transparent; } diff --git a/packages/assets/styles/variables.scss b/packages/assets/styles/variables.scss index 76be6f6da8..7106722110 100644 --- a/packages/assets/styles/variables.scss +++ b/packages/assets/styles/variables.scss @@ -320,7 +320,7 @@ html { --color-button-bg: var(--surface-4); --color-button-border: rgba(193, 190, 209, 0.12); - --color-scrollbar: var(--color-button-bg); + --color-scrollbar: var(--surface-5); --color-divider: var(--color-button-bg); --color-divider-dark: #646c75; diff --git a/packages/ui/src/components/base/MultiSelect.vue b/packages/ui/src/components/base/MultiSelect.vue new file mode 100644 index 0000000000..f8b76e6ac9 --- /dev/null +++ b/packages/ui/src/components/base/MultiSelect.vue @@ -0,0 +1,711 @@ + + + + + diff --git a/packages/ui/src/components/base/index.ts b/packages/ui/src/components/base/index.ts index 48aeb49577..ec4805053d 100644 --- a/packages/ui/src/components/base/index.ts +++ b/packages/ui/src/components/base/index.ts @@ -39,6 +39,8 @@ export { default as JoinedButtons } from './JoinedButtons.vue' export { default as LoadingIndicator } from './LoadingIndicator.vue' export { default as ManySelect } from './ManySelect.vue' export { default as MarkdownEditor } from './MarkdownEditor.vue' +export type { MultiSelectOption } from './MultiSelect.vue' +export { default as MultiSelect } from './MultiSelect.vue' export type { MaybeCtxFn, StageButtonConfig, StageConfigInput } from './MultiStageModal.vue' export { default as MultiStageModal, resolveCtxFn } from './MultiStageModal.vue' export { default as OptionGroup } from './OptionGroup.vue' diff --git a/packages/ui/src/stories/base/MultiSelect.stories.ts b/packages/ui/src/stories/base/MultiSelect.stories.ts new file mode 100644 index 0000000000..6d7b59f4b0 --- /dev/null +++ b/packages/ui/src/stories/base/MultiSelect.stories.ts @@ -0,0 +1,95 @@ +import type { Meta, StoryObj } from '@storybook/vue3-vite' +import { ref } from 'vue' + +import MultiSelect from '../../components/base/MultiSelect.vue' + +const meta = { + title: 'Base/MultiSelect', + // @ts-ignore - error comes from generically typed component + component: MultiSelect, + render: (args) => ({ + components: { MultiSelect }, + setup() { + const selected = ref(args.modelValue) + return { args, selected } + }, + template: /*html*/ ` +
+ +
+ `, + }), +} satisfies Meta + +export default meta +type Story = StoryObj + +export const Default: Story = { + args: { + options: [ + { value: 'en', label: 'English' }, + { value: 'es', label: 'Spanish' }, + { value: 'fr', label: 'French' }, + { value: 'de', label: 'German' }, + { value: 'zh-CN', label: 'Chinese (Simplified)' }, + { value: 'ko', label: 'Korean' }, + { value: 'ja', label: 'Japanese' }, + { value: 'pt', label: 'Portuguese' }, + { value: 'ru', label: 'Russian' }, + { value: 'it', label: 'Italian' }, + { value: 'ar', label: 'Arabic' }, + ], + modelValue: ['en', 'es', 'fr', 'zh-CN'], + placeholder: 'Select languages', + }, +} + +export const WithSearch: Story = { + args: { + ...Default.args, + searchable: true, + searchPlaceholder: 'Search versions', + }, +} + +export const WithSelectAll: Story = { + args: { + ...Default.args, + searchable: true, + includeSelectAllOption: true, + searchPlaceholder: 'Search versions', + }, +} + +export const ManySelected: Story = { + args: { + ...Default.args, + modelValue: ['en', 'es', 'fr', 'zh-CN', 'ko', 'ja', 'pt', 'ru', 'it', 'ar', 'de'], + searchable: true, + includeSelectAllOption: true, + }, +} + +export const TwoTagRows: Story = { + args: { + ...ManySelected.args, + maxTagRows: 2, + }, +} + +export const NoOptions: Story = { + args: { + ...Default.args, + options: [], + modelValue: [], + searchable: true, + noOptionsMessage: 'No options available', + }, +} + +export const Empty: Story = { + args: { + ...Default.args, + modelValue: [], + }, +}