Merge pull request #98 from modrinth/feature/popout-menus
Small button refactor, overflow and popout menus
This commit is contained in:
commit
ffb491a61f
@ -30,6 +30,8 @@ export default {
|
||||
{ text: 'Modal', link: '/components/modal' },
|
||||
{ text: 'Dropdown Select', link: '/components/dropdown-select' },
|
||||
{ text: 'Dropdown Button', link: '/components/dropdown-button' },
|
||||
{ text: 'Popout Menu', link: '/components/popout-menu' },
|
||||
{ text: 'Overflow Menu', link: '/components/overflow-menu' },
|
||||
{ text: 'Project Card', link: '/components/project-card' },
|
||||
{ text: 'Environment Indicator', link: '/components/environment-indicator' },
|
||||
{ text: 'Categories', link: '/components/categories' },
|
||||
|
||||
@ -1,35 +1,109 @@
|
||||
# Button
|
||||
# Buttons
|
||||
|
||||
## Standard
|
||||
|
||||
<DemoContainer>
|
||||
<Button>Standard</Button>
|
||||
<Button color="primary">Primary</Button>
|
||||
<Button color="secondary">Secondary</Button>
|
||||
<Button color="highlight">Highlight</Button>
|
||||
<Button color="raised">Raised</Button>
|
||||
<Button color="danger">Danger</Button>
|
||||
<Button><CheckIcon /> With Icon</Button>
|
||||
<Button iconOnly><XIcon /></Button>
|
||||
<Button large>Large button</Button>
|
||||
<Button color="primary" large>Discover mods</Button>
|
||||
<Button color="danger" large outline>Be warned</Button>
|
||||
<Button large outline>Sign up</Button>
|
||||
<Button><HeartIcon /> Follow project</Button>
|
||||
<Button outline color="danger"><TrashIcon /> Delete project</Button>
|
||||
<Button><BookmarkIcon /> Save</Button>
|
||||
<Button color="primary"><UploadIcon /> Upload</Button>
|
||||
<Button color="secondary"><PlusIcon /> Create new instance</Button>
|
||||
<Button color="highlight"><ScaleIcon /> Submit for review</Button>
|
||||
<Button color="danger"><TrashIcon /> Delete</Button>
|
||||
</DemoContainer>
|
||||
|
||||
```vue
|
||||
<Button>Standard</Button>
|
||||
<Button color="primary">Primary</Button>
|
||||
<Button color="secondary">Secondary</Button>
|
||||
<Button color="highlight">Highlight</Button>
|
||||
<Button color="raised">Raised</Button>
|
||||
<Button color="danger">Danger</Button>
|
||||
<Button><CheckIcon /> With Icon</Button>
|
||||
<Button iconOnly><XIcon /></Button>
|
||||
<Button large>Large button</Button>
|
||||
<Button color="primary" large>Discover mods</Button>
|
||||
<Button color="danger" large outline>Be warned</Button>
|
||||
<Button large outline>Sign up</Button>
|
||||
<Button><HeartIcon /> Follow project</Button>
|
||||
<Button outline color="danger"><TrashIcon /> Delete project</Button>
|
||||
<Button><BookmarkIcon /> Save</Button>
|
||||
<Button color="primary"><UploadIcon /> Upload</Button>
|
||||
<Button color="secondary"><PlusIcon /> Create new instance</Button>
|
||||
<Button color="highlight"><ScaleIcon /> Submit for review</Button>
|
||||
<Button color="danger"><TrashIcon /> Delete</Button>
|
||||
```
|
||||
|
||||
## Large
|
||||
|
||||
<DemoContainer>
|
||||
<Button color="primary" large><DownloadIcon /> Download</Button>
|
||||
<Button color="blue" large><ServerIcon /> Host a Server</Button>
|
||||
<Button color="purple" large><HeartIcon /> Donate</Button>
|
||||
</DemoContainer>
|
||||
|
||||
```vue
|
||||
<Button color="primary" large><DownloadIcon /> Download</Button>
|
||||
<Button color="blue" large><ServerIcon /> Host a Server</Button>
|
||||
<Button color="purple" large><HeartIcon /> Donate</Button>
|
||||
```
|
||||
|
||||
## Outline
|
||||
|
||||
<DemoContainer>
|
||||
<Button color="primary" outline><DownloadIcon/> Get Modrinth App</Button>
|
||||
<Button color="red" outline><ReportIcon /> Report project</Button>
|
||||
</DemoContainer>
|
||||
|
||||
```vue
|
||||
<Button color="primary" outline><DownloadIcon/> Get Modrinth App</Button>
|
||||
<Button color="red" outline><ReportIcon /> Report project</Button>
|
||||
```
|
||||
|
||||
## Transparent
|
||||
|
||||
<DemoContainer>
|
||||
<Button transparent><IssuesIcon /> Report issues</Button>
|
||||
<Button transparent><CodeIcon /> View sources</Button>
|
||||
<Button color="blue" transparent><GlobeIcon/> Visit website</Button>
|
||||
</DemoContainer>
|
||||
|
||||
```vue
|
||||
<Button transparent><IssuesIcon /> Report issues</Button>
|
||||
<Button transparent><CodeIcon /> View sources</Button>
|
||||
<Button color="blue" transparent><GlobeIcon/> Visit website</Button>
|
||||
```
|
||||
|
||||
## Hover-filled
|
||||
|
||||
<DemoContainer>
|
||||
<Button color="green" transparent hoverFilled><PlayIcon /> Play</Button>
|
||||
<Button color="red" transparent hoverFilled><TrashIcon /> Delete</Button>
|
||||
<Button color="green" outline hoverFilled><PlayIcon /> Play</Button>
|
||||
<Button color="red" outline hoverFilled><TrashIcon /> Delete</Button>
|
||||
</DemoContainer>
|
||||
|
||||
```vue
|
||||
<Button color="green" transparent hoverFilled><PlayIcon /> Play</Button>
|
||||
<Button color="red" transparent hoverFilled><TrashIcon /> Delete</Button>
|
||||
<Button color="green" outline hoverFilled><PlayIcon /> Play</Button>
|
||||
<Button color="red" outline hoverFilled><TrashIcon /> Delete</Button>
|
||||
```
|
||||
|
||||
## Hover-filled-only
|
||||
|
||||
<DemoContainer>
|
||||
<Button color="green" transparent hoverFilledOnly><PlayIcon /> Play</Button>
|
||||
<Button color="red" transparent hoverFilledOnly><TrashIcon /> Delete</Button>
|
||||
<Button color="green" outline hoverFilledOnly><PlayIcon /> Play</Button>
|
||||
<Button color="red" outline hoverFilledOnly><TrashIcon /> Delete</Button>
|
||||
</DemoContainer>
|
||||
|
||||
```vue
|
||||
<Button color="green" transparent hoverFilledOnly><PlayIcon /> Play</Button>
|
||||
<Button color="red" transparent hoverFilledOnly><TrashIcon /> Delete</Button>
|
||||
<Button color="green" outline hoverFilledOnly><PlayIcon /> Play</Button>
|
||||
<Button color="red" outline hoverFilledOnly><TrashIcon /> Delete</Button>
|
||||
```
|
||||
|
||||
## Icon-only
|
||||
|
||||
<DemoContainer>
|
||||
<Button icon-only><HeartIcon /></Button>
|
||||
<Button icon-only><XIcon /></Button>
|
||||
<Button icon-only><MoreHorizontalIcon /></Button>
|
||||
<Button icon-only transparent><SunIcon /></Button>
|
||||
<Button icon-only transparent><DropdownIcon /></Button>
|
||||
</DemoContainer>
|
||||
|
||||
```vue
|
||||
<Button icon-only><HeartIcon /></Button>
|
||||
<Button icon-only><XIcon /></Button>
|
||||
<Button icon-only><MoreHorizontalIcon /></Button>
|
||||
<Button icon-only transparent><SunIcon /></Button>
|
||||
<Button icon-only transparent><DropdownIcon /></Button>
|
||||
```
|
||||
|
||||
94
docs/components/overflow-menu.md
Normal file
94
docs/components/overflow-menu.md
Normal file
@ -0,0 +1,94 @@
|
||||
# Overflow Menu
|
||||
<DemoContainer>
|
||||
<OverflowMenu :options="[
|
||||
{
|
||||
'id': 'play',
|
||||
'color': 'primary',
|
||||
'action': () => {},
|
||||
'hoverFilledOnly': true
|
||||
},
|
||||
{ divider: true },
|
||||
{
|
||||
'id': 'duplicate',
|
||||
'action': () => {}
|
||||
},
|
||||
{
|
||||
'id': 'report',
|
||||
'action': () => {}
|
||||
},
|
||||
{
|
||||
'id': 'remain',
|
||||
'action': () => {},
|
||||
'remainOnClick': true,
|
||||
},
|
||||
{ divider: true },
|
||||
{
|
||||
'id': 'delete',
|
||||
'color': 'danger',
|
||||
'action': () => {},
|
||||
'hoverFilled': true,
|
||||
}
|
||||
]" class="btn">
|
||||
More options...
|
||||
<template #play>
|
||||
<PlayIcon /> Play
|
||||
</template>
|
||||
<template #duplicate>
|
||||
<CopyIcon /> Duplicate
|
||||
</template>
|
||||
<template #report>
|
||||
<ReportIcon /> Report
|
||||
</template>
|
||||
<template #remain>
|
||||
<ClearIcon /> I shall remain
|
||||
</template>
|
||||
<template #delete>
|
||||
<TrashIcon /> Delete
|
||||
</template>
|
||||
</OverflowMenu>
|
||||
</DemoContainer>
|
||||
|
||||
```vue
|
||||
<OverflowMenu
|
||||
class="btn"
|
||||
:options="[
|
||||
{
|
||||
'id': 'play',
|
||||
'color': 'primary',
|
||||
'action': () => {},
|
||||
'hoverFilledOnly': true
|
||||
},
|
||||
{ divider: true },
|
||||
{
|
||||
'id': 'duplicate',
|
||||
'action': () => {}
|
||||
},
|
||||
{
|
||||
'id': 'report',
|
||||
'action': () => {}
|
||||
},
|
||||
{
|
||||
'id': 'remain',
|
||||
'action': () => {},
|
||||
'remainOnClick': true,
|
||||
},
|
||||
{ divider: true },
|
||||
{
|
||||
'id': 'delete',
|
||||
'color': 'danger',
|
||||
'action': () => {},
|
||||
'hoverFilled': true,
|
||||
}
|
||||
]">
|
||||
More options...
|
||||
<template #like>
|
||||
<HeartIcon /> Like
|
||||
</template>
|
||||
<template #report>
|
||||
<ReportIcon /> Report
|
||||
</template>
|
||||
<template #delete>
|
||||
<TrashIcon /> Delete
|
||||
</template>
|
||||
</OverflowMenu>
|
||||
```
|
||||
76
docs/components/popout-menu.md
Normal file
76
docs/components/popout-menu.md
Normal file
@ -0,0 +1,76 @@
|
||||
# Popout Menu
|
||||
<DemoContainer>
|
||||
<PopoutMenu class="btn" position="bottom" direction="left">
|
||||
Bottom going left
|
||||
<template #menu>
|
||||
Menu contents!
|
||||
Menu contents!
|
||||
Menu contents!
|
||||
</template>
|
||||
</PopoutMenu>
|
||||
<PopoutMenu class="btn" position="bottom" direction="right">
|
||||
Bottom going right
|
||||
<template #menu>
|
||||
Menu contents!
|
||||
Menu contents!
|
||||
Menu contents!
|
||||
</template>
|
||||
</PopoutMenu>
|
||||
<PopoutMenu class="btn" position="top" direction="left">
|
||||
Top going left
|
||||
<template #menu>
|
||||
Menu contents!
|
||||
Menu contents!
|
||||
Menu contents!
|
||||
</template>
|
||||
</PopoutMenu>
|
||||
<PopoutMenu class="btn" position="top" direction="right">
|
||||
Top going right
|
||||
<template #menu>
|
||||
Menu contents!
|
||||
Menu contents!
|
||||
Menu contents!
|
||||
</template>
|
||||
</PopoutMenu>
|
||||
<PopoutMenu class="btn" position="left" direction="up">
|
||||
Left going up
|
||||
<template #menu>
|
||||
Menu contents!
|
||||
Menu contents!
|
||||
Menu contents!
|
||||
</template>
|
||||
</PopoutMenu>
|
||||
<PopoutMenu class="btn" position="left" direction="down">
|
||||
Left going down
|
||||
<template #menu>
|
||||
Menu contents!
|
||||
Menu contents!
|
||||
Menu contents!
|
||||
</template>
|
||||
</PopoutMenu>
|
||||
<PopoutMenu class="btn" position="right" direction="up">
|
||||
Right going up
|
||||
<template #menu>
|
||||
Menu contents!
|
||||
Menu contents!
|
||||
Menu contents!
|
||||
</template>
|
||||
</PopoutMenu>
|
||||
<PopoutMenu class="btn" position="right" direction="down">
|
||||
Right going down
|
||||
<template #menu>
|
||||
Menu contents!
|
||||
Menu contents!
|
||||
Menu contents!
|
||||
</template>
|
||||
</PopoutMenu>
|
||||
</DemoContainer>
|
||||
|
||||
```vue
|
||||
<PopoutMenu class="btn" position="bottom" direction="right">
|
||||
Bottom going right
|
||||
<template #menu>
|
||||
Menu contents!
|
||||
</template>
|
||||
</PopoutMenu>
|
||||
```
|
||||
1
lib/assets/icons/bookmark.svg
Normal file
1
lib/assets/icons/bookmark.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-bookmark"><path d="m19 21-7-4-7 4V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2v16z"/></svg>
|
||||
|
After Width: | Height: | Size: 281 B |
1
lib/assets/icons/copy.svg
Normal file
1
lib/assets/icons/copy.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-copy"><rect width="14" height="14" x="8" y="8" rx="2" ry="2"/><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/></svg>
|
||||
|
After Width: | Height: | Size: 337 B |
1
lib/assets/icons/more-horizontal.svg
Normal file
1
lib/assets/icons/more-horizontal.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-more-horizontal"><circle cx="12" cy="12" r="1"/><circle cx="19" cy="12" r="1"/><circle cx="5" cy="12" r="1"/></svg>
|
||||
|
After Width: | Height: | Size: 317 B |
1
lib/assets/icons/more-vertical.svg
Normal file
1
lib/assets/icons/more-vertical.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-more-vertical"><circle cx="12" cy="12" r="1"/><circle cx="12" cy="5" r="1"/><circle cx="12" cy="19" r="1"/></svg>
|
||||
|
After Width: | Height: | Size: 315 B |
@ -266,10 +266,13 @@ a,
|
||||
&:focus-visible:not(&:disabled),
|
||||
&:hover:not(&:disabled) {
|
||||
cursor: pointer;
|
||||
filter: brightness(0.85);
|
||||
|
||||
&:not(.btn-outline.btn-hover-filled, .btn-transparent) {
|
||||
filter: brightness(0.85);
|
||||
}
|
||||
}
|
||||
|
||||
&:active:not(&:disabled) {
|
||||
&:active:not(&:disabled, .btn-outline.btn-hover-filled, .btn-transparent) {
|
||||
filter: brightness(0.8);
|
||||
}
|
||||
|
||||
@ -321,44 +324,121 @@ a,
|
||||
.btn {
|
||||
@extend .button-base;
|
||||
|
||||
--text-color: var(--color-contrast);
|
||||
--background-color: var(--color-button-bg);
|
||||
--shadow: var(--shadow-inset-sm), 0 0 0 0 transparent;
|
||||
--_text-color: var(--color-base);
|
||||
--_background-color: var(--color-button-bg);
|
||||
--_accent-color: var(--color-base);
|
||||
--_shadow: var(--shadow-inset-sm), 0 0 0 0 transparent;
|
||||
|
||||
&.btn-outline {
|
||||
--background-color: var(--color-contrast);
|
||||
--text-color: var(--color-raised-bg);
|
||||
&.btn-outline,
|
||||
&.btn-transparent {
|
||||
box-sizing: border-box;
|
||||
|
||||
background-color: transparent;
|
||||
border: 2px solid var(--background-color);
|
||||
color: var(--background-color);
|
||||
|
||||
padding-block: calc(var(--gap-sm) - 2px);
|
||||
transition: background-color 0.2s ease-in-out, border-color 0.2s ease-in-out,
|
||||
color 0.2s ease-in-out;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
&.btn-raised {
|
||||
--shadow: var(--shadow-inset-sm), var(--shadow-raised);
|
||||
--background-color: var(--color-raised-bg);
|
||||
&.btn-transparent {
|
||||
--_accent-color: var(--color-base);
|
||||
color: var(--_accent-color);
|
||||
|
||||
&.btn-hover-filled-only {
|
||||
color: var(--color-base);
|
||||
}
|
||||
|
||||
&:focus-visible:not(&:disabled),
|
||||
&:hover:not(&:disabled) {
|
||||
background-color: var(--color-button-bg);
|
||||
|
||||
&.btn-hover-filled,
|
||||
&.btn-hover-filled-only {
|
||||
color: var(--_text-color);
|
||||
background-color: var(--_background-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.btn-outline {
|
||||
--_accent-color: var(--color-contrast);
|
||||
border: 2px solid var(--_accent-color);
|
||||
padding-block: calc(var(--gap-sm) - 2px);
|
||||
color: var(--_background-color);
|
||||
|
||||
&.btn-hover-filled-only {
|
||||
color: var(--color-contrast);
|
||||
border-color: var(--color-contrast);
|
||||
}
|
||||
|
||||
&:focus-visible:not(&:disabled),
|
||||
&:hover:not(&:disabled) {
|
||||
&.btn-hover-filled,
|
||||
&.btn-hover-filled-only {
|
||||
border-color: var(--_accent-color);
|
||||
color: var(--_text-color);
|
||||
background-color: var(--_background-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.btn-danger {
|
||||
--text-color: var(--color-accent-contrast);
|
||||
--background-color: var(--color-red);
|
||||
--_text-color: var(--color-accent-contrast);
|
||||
--_background-color: var(--color-red);
|
||||
--_accent-color: var(--color-red);
|
||||
}
|
||||
|
||||
&.btn-primary {
|
||||
--text-color: var(--color-accent-contrast);
|
||||
--background-color: var(--color-brand);
|
||||
--_text-color: var(--color-accent-contrast);
|
||||
--_background-color: var(--color-brand);
|
||||
--_accent-color: var(--color-brand);
|
||||
}
|
||||
|
||||
&.btn-secondary {
|
||||
--background-color: var(--color-brand-highlight);
|
||||
--_text-color: var(--color-contrast);
|
||||
--_background-color: var(--color-brand-highlight);
|
||||
--_accent-color: var(--color-brand-highlight);
|
||||
}
|
||||
|
||||
&.btn-highlight {
|
||||
--text-color: var(--color-accent-contrast);
|
||||
--background-color: var(--color-orange);
|
||||
--_text-color: var(--color-accent-contrast);
|
||||
--_background-color: var(--color-orange);
|
||||
--_accent-color: var(--color-orange);
|
||||
}
|
||||
|
||||
&.btn-red {
|
||||
--_text-color: var(--color-accent-contrast);
|
||||
--_background-color: var(--color-red);
|
||||
--_accent-color: var(--color-red);
|
||||
}
|
||||
|
||||
&.btn-orange {
|
||||
--_text-color: var(--color-accent-contrast);
|
||||
--_background-color: var(--color-orange);
|
||||
--_accent-color: var(--color-orange);
|
||||
}
|
||||
|
||||
&.btn-green {
|
||||
--_text-color: var(--color-accent-contrast);
|
||||
--_background-color: var(--color-green);
|
||||
--_accent-color: var(--color-green);
|
||||
}
|
||||
|
||||
&.btn-blue {
|
||||
--_text-color: var(--color-accent-contrast);
|
||||
--_background-color: var(--color-blue);
|
||||
--_accent-color: var(--color-blue);
|
||||
}
|
||||
|
||||
&.btn-purple {
|
||||
--_text-color: var(--color-accent-contrast);
|
||||
--_background-color: var(--color-purple);
|
||||
--_accent-color: var(--color-purple);
|
||||
}
|
||||
|
||||
&.btn-gray {
|
||||
--_text-color: var(--color-accent-contrast);
|
||||
--_background-color: var(--color-gray);
|
||||
--_accent-color: var(--color-gray);
|
||||
}
|
||||
|
||||
&.btn-large {
|
||||
@ -368,25 +448,26 @@ a,
|
||||
|
||||
box-sizing: border-box;
|
||||
|
||||
color: var(--text-color);
|
||||
background-color: var(--background-color);
|
||||
box-shadow: var(--shadow);
|
||||
color: var(--_text-color);
|
||||
background-color: var(--_background-color);
|
||||
box-shadow: var(--_shadow);
|
||||
border-radius: var(--radius-md);
|
||||
|
||||
padding-inline: var(--gap-lg);
|
||||
padding-block: var(--gap-sm);
|
||||
padding: var(--gap-sm) var(--gap-lg);
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
width: fit-content;
|
||||
height: fit-content;
|
||||
text-decoration: none;
|
||||
gap: 0.5rem;
|
||||
line-height: 1.25rem;
|
||||
|
||||
svg {
|
||||
width: 1.1rem;
|
||||
height: 1.1rem;
|
||||
margin-right: 0.5rem;
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
}
|
||||
|
||||
:deep(.external-icon) {
|
||||
@ -398,17 +479,7 @@ a,
|
||||
}
|
||||
|
||||
&.icon-only {
|
||||
padding: 0;
|
||||
height: 2.25rem;
|
||||
width: 2.25rem;
|
||||
|
||||
svg {
|
||||
min-width: 1.25rem;
|
||||
max-width: 1.25rem;
|
||||
min-height: 1.25rem;
|
||||
max-height: 1.25rem;
|
||||
margin: auto;
|
||||
}
|
||||
padding: var(--gap-sm);
|
||||
}
|
||||
|
||||
&.transparent {
|
||||
|
||||
@ -25,6 +25,7 @@ html {
|
||||
--color-button-bg: hsl(220, 13%, 91%);
|
||||
|
||||
--color-base: hsl(221, 39%, 11%);
|
||||
--color-secondary: #6b7280;
|
||||
--color-contrast: #1a202c;
|
||||
--color-accent-contrast: #ffffff;
|
||||
|
||||
@ -67,6 +68,7 @@ html {
|
||||
--color-button-bg: hsl(222, 13%, 30%);
|
||||
|
||||
--color-base: var(--dark-color-base);
|
||||
--color-secondary: #96a2b0;
|
||||
--color-contrast: var(--dark-color-contrast);
|
||||
--color-accent-contrast: #000000;
|
||||
|
||||
|
||||
@ -32,9 +32,23 @@ const props = defineProps({
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
transparent: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
hoverFilled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
hoverFilledOnly: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
})
|
||||
|
||||
const accentedButton = computed(() => ['danger', 'primary'].includes(props.color))
|
||||
const accentedButton = computed(() =>
|
||||
['danger', 'primary', 'red', 'orange', 'green', 'blue', 'purple', 'gray'].includes(props.color)
|
||||
)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -42,14 +56,22 @@ const accentedButton = computed(() => ['danger', 'primary'].includes(props.color
|
||||
v-if="link"
|
||||
class="btn"
|
||||
:class="{
|
||||
'icon-only': props.iconOnly,
|
||||
'btn-large': props.large,
|
||||
'btn-raised': color === 'raised',
|
||||
'icon-only': iconOnly,
|
||||
'btn-large': large,
|
||||
'btn-danger': color === 'danger',
|
||||
'btn-primary': color === 'primary',
|
||||
'btn-secondary': color === 'secondary',
|
||||
'btn-highlight': color === 'highlight',
|
||||
'btn-outline': props.outline,
|
||||
'btn-red': color === 'red',
|
||||
'btn-orange': color === 'orange',
|
||||
'btn-green': color === 'green',
|
||||
'btn-blue': color === 'blue',
|
||||
'btn-purple': color === 'purple',
|
||||
'btn-gray': color === 'gray',
|
||||
'btn-transparent': transparent,
|
||||
'btn-hover-filled': hoverFilled,
|
||||
'btn-hover-filled-only': hoverFilledOnly,
|
||||
'btn-outline': outline,
|
||||
'color-accent-contrast': accentedButton,
|
||||
}"
|
||||
:to="link"
|
||||
@ -63,14 +85,22 @@ const accentedButton = computed(() => ['danger', 'primary'].includes(props.color
|
||||
v-else
|
||||
class="btn"
|
||||
:class="{
|
||||
'icon-only': props.iconOnly,
|
||||
'btn-large': props.large,
|
||||
'btn-raised': color === 'raised',
|
||||
'icon-only': iconOnly,
|
||||
'btn-large': large,
|
||||
'btn-danger': color === 'danger',
|
||||
'btn-primary': color === 'primary',
|
||||
'btn-secondary': color === 'secondary',
|
||||
'btn-highlight': color === 'highlight',
|
||||
'btn-outline': props.outline,
|
||||
'btn-red': color === 'red',
|
||||
'btn-orange': color === 'orange',
|
||||
'btn-green': color === 'green',
|
||||
'btn-blue': color === 'blue',
|
||||
'btn-purple': color === 'purple',
|
||||
'btn-gray': color === 'gray',
|
||||
'btn-transparent': transparent,
|
||||
'btn-hover-filled': hoverFilled,
|
||||
'btn-hover-filled-only': hoverFilledOnly,
|
||||
'btn-outline': outline,
|
||||
'color-accent-contrast': accentedButton,
|
||||
}"
|
||||
@click="action"
|
||||
|
||||
85
lib/components/base/OverflowMenu.vue
Normal file
85
lib/components/base/OverflowMenu.vue
Normal file
@ -0,0 +1,85 @@
|
||||
<template>
|
||||
<PopoutMenu
|
||||
ref="dropdown"
|
||||
v-bind="$attrs"
|
||||
:disabled="disabled"
|
||||
:position="position"
|
||||
:direction="direction"
|
||||
>
|
||||
<slot></slot>
|
||||
<template #menu>
|
||||
<template v-for="(option, index) in options">
|
||||
<div v-if="option.divider" :key="`divider-${index}`" class="card-divider"></div>
|
||||
<Button
|
||||
v-else
|
||||
:key="`option-${option.id}`"
|
||||
:color="option.color ? option.color : 'default'"
|
||||
:hover-filled="option.hoverFilled"
|
||||
:hover-filled-only="option.hoverFilledOnly"
|
||||
transparent
|
||||
:action="
|
||||
() => {
|
||||
option.action()
|
||||
if (!option.remainOnClick) {
|
||||
close()
|
||||
}
|
||||
}
|
||||
"
|
||||
>
|
||||
<template v-if="!$slots[option.id]">{{ option.id }}</template>
|
||||
<slot :name="option.id"></slot>
|
||||
</Button>
|
||||
</template>
|
||||
</template>
|
||||
</PopoutMenu>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import PopoutMenu from '@/components/base/PopoutMenu.vue'
|
||||
import Button from '@/components/base/Button.vue'
|
||||
|
||||
defineProps({
|
||||
options: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
position: {
|
||||
type: String,
|
||||
default: 'bottom',
|
||||
},
|
||||
direction: {
|
||||
type: String,
|
||||
default: 'left',
|
||||
},
|
||||
})
|
||||
defineOptions({
|
||||
inheritAttrs: false,
|
||||
})
|
||||
|
||||
const dropdown = ref(null)
|
||||
|
||||
const close = () => {
|
||||
console.log('closing!')
|
||||
dropdown.value.hide()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.btn {
|
||||
white-space: nowrap;
|
||||
width: 100%;
|
||||
box-shadow: none;
|
||||
--text-color: var(--color-base);
|
||||
--background-color: transparent;
|
||||
justify-content: flex-start;
|
||||
|
||||
&:not(:last-child) {
|
||||
margin-bottom: var(--gap-xs);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
195
lib/components/base/PopoutMenu.vue
Normal file
195
lib/components/base/PopoutMenu.vue
Normal file
@ -0,0 +1,195 @@
|
||||
<template>
|
||||
<div ref="dropdown" class="popup-container" tabindex="0" :aria-expanded="dropdownVisible">
|
||||
<button v-bind="$attrs" ref="dropdownButton" @click="toggleDropdown">
|
||||
<slot></slot>
|
||||
</button>
|
||||
<div
|
||||
class="popup-menu"
|
||||
:class="`position-${position}-${direction} ${dropdownVisible ? 'visible' : ''}`"
|
||||
>
|
||||
<slot name="menu"> </slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onBeforeUnmount, onMounted, ref } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
position: {
|
||||
type: String,
|
||||
default: 'bottom',
|
||||
},
|
||||
direction: {
|
||||
type: String,
|
||||
default: 'left',
|
||||
},
|
||||
})
|
||||
defineOptions({
|
||||
inheritAttrs: false,
|
||||
})
|
||||
|
||||
const dropdownVisible = ref(false)
|
||||
const dropdown = ref(null)
|
||||
const dropdownButton = ref(null)
|
||||
|
||||
const toggleDropdown = () => {
|
||||
if (!props.disabled) {
|
||||
dropdownVisible.value = !dropdownVisible.value
|
||||
if (!dropdownVisible.value) {
|
||||
dropdownButton.value.focus()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const hide = () => {
|
||||
dropdownVisible.value = false
|
||||
dropdownButton.value.focus()
|
||||
}
|
||||
|
||||
const show = () => {
|
||||
dropdownVisible.value = true
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
show,
|
||||
hide,
|
||||
})
|
||||
|
||||
const handleClickOutside = (event) => {
|
||||
const elements = document.elementsFromPoint(event.clientX, event.clientY)
|
||||
if (
|
||||
dropdown.value.$el !== event.target &&
|
||||
!elements.includes(dropdown.value.$el) &&
|
||||
!dropdown.value.contains(event.target)
|
||||
) {
|
||||
dropdownVisible.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
window.addEventListener('click', handleClickOutside)
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('click', handleClickOutside)
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.popup-container {
|
||||
position: relative;
|
||||
|
||||
.popup-menu {
|
||||
--_animation-offset: -1rem;
|
||||
position: absolute;
|
||||
scale: 0.75;
|
||||
border: 1px solid var(--color-button-bg);
|
||||
padding: var(--gap-sm);
|
||||
width: fit-content;
|
||||
border-radius: var(--radius-md);
|
||||
background-color: var(--color-raised-bg);
|
||||
box-shadow: var(--shadow-floating);
|
||||
z-index: 10;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transition: bottom 0.125s ease-in-out, top 0.125s ease-in-out, left 0.125s ease-in-out,
|
||||
right 0.125s ease-in-out, opacity 0.125s ease-in-out, scale 0.125s ease-in-out;
|
||||
|
||||
&.position-bottom-left {
|
||||
top: calc(100% + var(--gap-sm) - 1rem);
|
||||
right: -1rem;
|
||||
}
|
||||
|
||||
&.position-bottom-right {
|
||||
top: calc(100% + var(--gap-sm) - 1rem);
|
||||
left: -1rem;
|
||||
}
|
||||
|
||||
&.position-top-left {
|
||||
bottom: calc(100% + var(--gap-sm) - 1rem);
|
||||
right: -1rem;
|
||||
}
|
||||
|
||||
&.position-top-right {
|
||||
bottom: calc(100% + var(--gap-sm) - 1rem);
|
||||
left: -1rem;
|
||||
}
|
||||
|
||||
&.position-left-up {
|
||||
bottom: -1rem;
|
||||
right: calc(100% + var(--gap-sm) - 1rem);
|
||||
}
|
||||
|
||||
&.position-left-down {
|
||||
top: -1rem;
|
||||
right: calc(100% + var(--gap-sm) - 1rem);
|
||||
}
|
||||
|
||||
&.position-right-up {
|
||||
bottom: -1rem;
|
||||
left: calc(100% + var(--gap-sm) - 1rem);
|
||||
}
|
||||
|
||||
&.position-right-down {
|
||||
top: -1rem;
|
||||
left: calc(100% + var(--gap-sm) - 1rem);
|
||||
}
|
||||
|
||||
&.visible,
|
||||
&:focus-within {
|
||||
opacity: 1;
|
||||
pointer-events: unset;
|
||||
scale: 1;
|
||||
|
||||
&.position-bottom-left {
|
||||
top: calc(100% + var(--gap-sm));
|
||||
right: 0;
|
||||
}
|
||||
|
||||
&.position-bottom-right {
|
||||
top: calc(100% + var(--gap-sm));
|
||||
left: 0;
|
||||
}
|
||||
|
||||
&.position-top-left {
|
||||
bottom: calc(100% + var(--gap-sm));
|
||||
right: 0;
|
||||
}
|
||||
|
||||
&.position-top-right {
|
||||
bottom: calc(100% + var(--gap-sm));
|
||||
left: 0;
|
||||
}
|
||||
|
||||
&.position-left-up {
|
||||
bottom: 0rem;
|
||||
right: calc(100% + var(--gap-sm));
|
||||
}
|
||||
|
||||
&.position-left-down {
|
||||
top: 0rem;
|
||||
right: calc(100% + var(--gap-sm));
|
||||
}
|
||||
|
||||
&.position-right-up {
|
||||
bottom: 0rem;
|
||||
left: calc(100% + var(--gap-sm));
|
||||
}
|
||||
|
||||
&.position-right-down {
|
||||
top: 0rem;
|
||||
left: calc(100% + var(--gap-sm));
|
||||
}
|
||||
}
|
||||
|
||||
.btn {
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -4,10 +4,10 @@
|
||||
<div class="slide-container">
|
||||
<div class="snap-points">
|
||||
<div
|
||||
v-for="snapPoint in props.snapPoints"
|
||||
:key="snapPoint"
|
||||
class="snap-point"
|
||||
:class="{ green: snapPoint <= currentValue }"
|
||||
v-for="snapPoint in props.snapPoints"
|
||||
v-bind:key="snapPoint"
|
||||
:style="{ left: ((snapPoint - props.min) / (props.max - props.min)) * 100 + '%' }"
|
||||
></div>
|
||||
</div>
|
||||
|
||||
@ -36,12 +36,16 @@ export { default as NavItem } from './nav/NavItem.vue'
|
||||
export { default as NavRow } from './nav/NavRow.vue'
|
||||
export { default as NavStack } from './nav/NavStack.vue'
|
||||
|
||||
export { default as PopoutMenu } from './base/PopoutMenu.vue'
|
||||
export { default as OverflowMenu } from './base/OverflowMenu.vue'
|
||||
|
||||
export { default as AlignLeftIcon } from '@/assets/icons/align-left.svg'
|
||||
export { default as ArchiveIcon } from '@/assets/icons/archive.svg'
|
||||
export { default as AsteriskIcon } from '@/assets/icons/asterisk.svg'
|
||||
export { default as BellIcon } from '@/assets/icons/bell.svg'
|
||||
export { default as BellRingIcon } from '@/assets/icons/bell-ring.svg'
|
||||
export { default as BookIcon } from '@/assets/icons/book.svg'
|
||||
export { default as BookmarkIcon } from '@/assets/icons/bookmark.svg'
|
||||
export { default as BoxIcon } from '@/assets/icons/box.svg'
|
||||
export { default as CalendarIcon } from '@/assets/icons/calendar.svg'
|
||||
export { default as ChartIcon } from '@/assets/icons/chart.svg'
|
||||
@ -55,6 +59,7 @@ export { default as ClipboardCopyIcon } from '@/assets/icons/clipboard-copy.svg'
|
||||
export { default as CodeIcon } from '@/assets/icons/code.svg'
|
||||
export { default as CoinsIcon } from '@/assets/icons/coins.svg'
|
||||
export { default as ContractIcon } from '@/assets/icons/contract.svg'
|
||||
export { default as CopyIcon } from '@/assets/icons/copy.svg'
|
||||
export { default as CopyrightIcon } from '@/assets/icons/copyright.svg'
|
||||
export { default as CurrencyIcon } from '@/assets/icons/currency.svg'
|
||||
export { default as DashboardIcon } from '@/assets/icons/dashboard.svg'
|
||||
@ -94,6 +99,8 @@ export { default as LockIcon } from '@/assets/icons/lock.svg'
|
||||
export { default as LogInIcon } from '@/assets/icons/log-in.svg'
|
||||
export { default as LogOutIcon } from '@/assets/icons/log-out.svg'
|
||||
export { default as MoonIcon } from '@/assets/icons/moon.svg'
|
||||
export { default as MoreHorizontalIcon } from '@/assets/icons/more-horizontal.svg'
|
||||
export { default as MoreVerticalIcon } from '@/assets/icons/more-vertical.svg'
|
||||
export { default as OmorphiaIcon } from '@/assets/icons/omorphia.svg'
|
||||
export { default as PaintBrushIcon } from '@/assets/icons/paintbrush.svg'
|
||||
export { default as PlayIcon } from '@/assets/icons/play.svg'
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "omorphia",
|
||||
"type": "module",
|
||||
"version": "0.4.41",
|
||||
"version": "0.5.0",
|
||||
"files": [
|
||||
"dist",
|
||||
"lib"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user