Avatar, Badge, Checkbox, Chips, Pagination components (#10)

This commit is contained in:
Geometrically 2023-03-01 17:31:48 -07:00 committed by GitHub
parent 76c0432f96
commit d5785e87e8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 1112 additions and 53 deletions

88
.eslintignore Normal file
View File

@ -0,0 +1,88 @@
node_modules
*.log*
.nuxt
.nitro
.cache
.output
.env
dist
*.md
generated/
!.gitkeep
# Created by .ignore support plugin (hsz.mobi)
### Node template
# Logs
/logs
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
jspm_packages/
# TypeScript v1 declaration files
typings/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# Serverless directories
.serverless
# IDE / Editor
.idea
# Service worker
sw.*
# macOS
.DS_Store
# Vim swap files
*.swp
# pnpm files
pnpm-lock.yaml
/.npmrc

View File

@ -4,11 +4,7 @@
"es2021": true,
"node": true
},
"extends": [
"plugin:vue/vue3-recommended",
"eslint:recommended",
"prettier"
],
"extends": ["plugin:vue/vue3-recommended", "eslint:recommended", "prettier"],
"parserOptions": {
"ecmaVersion": "latest",
"sourceType": "module"

88
.prettierignore Normal file
View File

@ -0,0 +1,88 @@
node_modules
*.log*
.nuxt
.nitro
.cache
.output
.env
dist
*.md
generated/
!.gitkeep
# Created by .ignore support plugin (hsz.mobi)
### Node template
# Logs
/logs
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
jspm_packages/
# TypeScript v1 declaration files
typings/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# Serverless directories
.serverless
# IDE / Editor
.idea
# Service worker
sw.*
# macOS
.DS_Store
# Vim swap files
*.swp
# pnpm files
pnpm-lock.yaml
/.npmrc

14
COPYING.md Normal file
View File

@ -0,0 +1,14 @@
# Copying
The source code of the omorphia repository is licensed under the GNU General Public License, Version 3 only, which is provided in the file [LICENSE](./LICENSE). However, some files listed below are licensed under a different license.
## Modrinth logo
Any files depicting the Modrinth branding, including the wrench-in-labyrinth logo, the landing image, and variations thereof, are licensed as follows:
> All rights reserved. © 2020-2022 Rinth, Inc.
This includes, but may not be limited to, the following files:
- assets/branding/*

View File

@ -10,20 +10,26 @@ export default {
items: [
{ text: 'Introduction', link: '/' },
{ text: 'Setup', link: '/setup' },
]
],
},
{
text: 'Components',
items: [
{ text: 'Icons', link: '/components/icons' },
{ text: 'Avatar', link: '/components/avatar' },
{ text: 'Badge', link: '/components/badge' },
{ text: 'Button', link: '/components/button' },
{ text: 'Card', link: '/components/card' },
]
}
{ text: 'Checkbox', link: '/components/checkbox' },
{ text: 'Chips', link: '/components/chips' },
{ text: 'Icons', link: '/components/icons' },
{ text: 'Pagination', link: '/components/pagination' },
],
},
],
footer: {
message: 'Released under the <a href="https://github.com/modrinth/omoprhia/blob/main/LICENSE">GPLv3 License</a>.',
copyright: 'Copyright © 2023-present <a href="https://modrinth.com">Rinth, Inc.</a>'
message:
'Released under the <a href="https://github.com/modrinth/omoprhia/blob/main/LICENSE">GPLv3 License</a>.',
copyright: 'Copyright © 2023-present <a href="https://modrinth.com">Rinth, Inc.</a>',
},
},
vite: {
@ -40,15 +46,15 @@ export default {
},
},
],
}, }
),
},
}),
],
resolve: {
alias: {
'@': resolve(__dirname, '../../lib'),
'omorphia': resolve(__dirname, '../../lib'),
omorphia: resolve(__dirname, '../../lib'),
},
dedupe: ['vue'],
}
}
},
},
}

View File

@ -1,5 +1,5 @@
<template>
<div class="demo bg-raised radius-lg padding-md"><slot /></div>
<div class="demo bg-raised radius-lg padding-md"><slot /></div>
</template>
<script>
@ -9,5 +9,9 @@ export default {}
<style lang="scss" scoped>
.demo {
border: 1px solid var(--color-button-bg);
display: flex;
gap: var(--gap-md);
align-items: center;
flex-wrap: wrap;
}
</style>

View File

@ -3,12 +3,16 @@ body {
background-color: var(--vp-c-bg);
}
.VPLink, .title, .pager-link, .link, .header-anchor {
.VPLink,
.title,
.pager-link,
.link,
.header-anchor {
color: inherit;
transition: none;
&:hover {
text-decoration: none;
text-decoration: none;
}
&:active:not(&:disabled) {

View File

@ -2,7 +2,6 @@ import DefaultTheme from 'vitepress/theme'
import Omorphia from 'omorphia'
import DemoContainer from './DemoContainer.vue'
import './compat.scss'
export default {
@ -10,5 +9,5 @@ export default {
enhanceApp(ctx) {
ctx.app.use(Omorphia)
ctx.app.component('DemoContainer', DemoContainer)
}
},
}

16
docs/components/avatar.md Normal file
View File

@ -0,0 +1,16 @@
# Icons
<DemoContainer class="columns">
<Avatar size="lg" circle src="https://cdn.modrinth.com/user/MpxzqsyW/eb0038489a55e7e7a188a5b50462f0b10dfc1613.jpeg" />
<Avatar size="md" src="https://cdn.modrinth.com/data/AANobbMI/icon.png" />
<Avatar size="md" src="https://cdn.modrinth.com/data/ssUbhMkL/icon.png" />
<Avatar size="sm" />
<Avatar size="xs" circle src="https://avatars1.githubusercontent.com/u/6166773?v=4" />
</DemoContainer>
```vue
<Avatar size="lg" circle src="https://cdn.modrinth.com/user/MpxzqsyW/eb0038489a55e7e7a188a5b50462f0b10dfc1613.jpeg" />
<Avatar size="md" src="https://cdn.modrinth.com/data/AANobbMI/icon.png" />
<Avatar size="sm" />
<Avatar size="xs" circle src="https://avatars1.githubusercontent.com/u/6166773?v=4" />
```

15
docs/components/badge.md Normal file
View File

@ -0,0 +1,15 @@
# Badge
<DemoContainer>
<Badge color="red" type="Tomato" />
<Badge color="orange" type="Squash" />
<Badge color="green" type="Lettuce" />
<Badge type="Onion" />
</DemoContainer>
```vue
<Badge color="red" type="Tomato" />
<Badge color="orange" type="Squash" />
<Badge color="green" type="Lettuce" />
<Badge type="Onion" />
```

View File

@ -1,13 +1,9 @@
# Button
<DemoContainer>
<Button :action="() => {}" >
Test Button
</Button>
<Button>Test Button</Button>
</DemoContainer>
```vue
<Button :action="() => {}" >
Test Button
</Button>
<Button>Test Button</Button>
```

View File

@ -1,9 +1,9 @@
# Card
<DemoContainer style="background-color: var(--color-bg)">
<Card>
This is a card!
</Card>
<DemoContainer class="standard-body" style="background-color: var(--color-bg)">
<Card>
This is a card!
</Card>
</DemoContainer>
```vue

View File

@ -0,0 +1,21 @@
# Checkbox
<script setup>
import { ref } from "vue";
const value = ref(false)
</script>
<DemoContainer>
<Checkbox v-model="value">Test</Checkbox>
</DemoContainer>
```vue
<script setup>
import { ref } from "vue";
const value = ref(false)
</script>
<Checkbox v-model="value">Test</Checkbox>
```

20
docs/components/chips.md Normal file
View File

@ -0,0 +1,20 @@
# Chips
<script setup>
import { ref } from "vue";
const value = ref('option 1')
</script>
<DemoContainer>
<Chips v-model="value" :items="['option 1', 'option 2', 'option 3', 'option 4']" />
</DemoContainer>
```vue
<script setup>
import { ref } from "vue";
const value = ref('option 1')
</script>
<Chips v-model="value" :items="['option 1', 'option 2', 'option 3', 'option 4']" />
```

View File

@ -1,9 +1,27 @@
# Icons
Omorphia includes a set of icons. You can view the available icons in the `~/assets/icons/*` folder of this repository.
<DemoContainer>
<CheckIcon />
<CheckIcon />
<UnknownIcon />
<SettingsIcon />
<TagIcon />
<SendIcon />
<LogOutIcon />
<HeartHandshakeIcon />
<EyeOffIcon />
<ClipboardCopyIcon />
</DemoContainer>
```vue
<CheckIcon />
<UnknownIcon />
<SettingsIcon />
<TagIcon />
<SendIcon />
<LogOutIcon />
<HeartHandshakeIcon />
<EyeOffIcon />
<ClipboardCopyIcon />
```

View File

@ -0,0 +1,28 @@
# Pagination
<script setup>
import { ref } from "vue";
const currentPage = ref(1)
function switchPage(page) {
currentPage.value = page
}
</script>
<DemoContainer style="background-color: var(--color-bg)">
<Pagination :page="currentPage" :count="100" @switch-page="switchPage" />
</DemoContainer>
```vue
<script setup>
import { ref } from "vue";
const currentPage = ref(1)
function switchPage(page) {
currentPage.value = page
}
</script>
<Pagination :page="currentPage" :count="100" @switch-page="switchPage" />
```

View File

@ -0,0 +1,4 @@
<svg width="512" height="514" viewBox="0 0 512 514" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M503.16 323.56C514.55 281.47 515.32 235.91 503.2 190.76C466.57 54.2299 326.04 -26.8001 189.33 9.77991C83.8101 38.0199 11.3899 128.07 0.689941 230.47H43.99C54.29 147.33 113.74 74.7298 199.75 51.7098C306.05 23.2598 415.13 80.6699 453.17 181.38L411.03 192.65C391.64 145.8 352.57 111.45 306.3 96.8198L298.56 140.66C335.09 154.13 364.72 184.5 375.56 224.91C391.36 283.8 361.94 344.14 308.56 369.17L320.09 412.16C390.25 383.21 432.4 310.3 422.43 235.14L464.41 223.91C468.91 252.62 467.35 281.16 460.55 308.07L503.16 323.56Z" fill="var(--color-brand)"/>
<path d="M321.99 504.22C185.27 540.8 44.7501 459.77 8.11011 323.24C3.84011 307.31 1.17 291.33 0 275.46H43.27C44.36 287.37 46.4699 299.35 49.6799 311.29C53.0399 323.8 57.45 335.75 62.79 347.07L101.38 323.92C98.1299 316.42 95.39 308.6 93.21 300.47C69.17 210.87 122.41 118.77 212.13 94.7601C229.13 90.2101 246.23 88.4401 262.93 89.1501L255.19 133C244.73 133.05 234.11 134.42 223.53 137.25C157.31 154.98 118.01 222.95 135.75 289.09C136.85 293.16 138.13 297.13 139.59 300.99L188.94 271.38L174.07 231.95L220.67 184.08L279.57 171.39L296.62 192.38L269.47 219.88L245.79 227.33L228.87 244.72L237.16 267.79C237.16 267.79 253.95 285.63 253.98 285.64L277.7 279.33L294.58 260.79L331.44 249.12L342.42 273.82L304.39 320.45L240.66 340.63L212.08 308.81L162.26 338.7C187.8 367.78 226.2 383.93 266.01 380.56L277.54 423.55C218.13 431.41 160.1 406.82 124.05 361.64L85.6399 384.68C136.25 451.17 223.84 484.11 309.61 461.16C371.35 444.64 419.4 402.56 445.42 349.38L488.06 364.88C457.17 431.16 398.22 483.82 321.99 504.22Z" fill="var(--color-brand)"/>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -1 +1 @@
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="4" y1="9" x2="20" y2="9"></line><line x1="4" y1="15" x2="20" y2="15"></line><line x1="10" y1="3" x2="8" y2="21"></line><line x1="16" y1="3" x2="14" y2="21"></line></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="4" y1="9" x2="20" y2="9"></line><line x1="4" y1="15" x2="20" y2="15"></line><line x1="10" y1="3" x2="8" y2="21"></line><line x1="16" y1="3" x2="14" y2="21"></line></svg>

Before

Width:  |  Height:  |  Size: 303 B

After

Width:  |  Height:  |  Size: 338 B

View File

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M3 6l3 1m0 0l-3 9a5.002 5.002 0 006.001 0M6 7l3 9M6 7l6-2m6 2l3-1m-3 1l-3 9a5.002 5.002 0 006.001 0M18 7l3 9m-3-9l-6-2m0-2v2m0 16V5m0 16H9m3 0h3" />
</svg>

After

Width:  |  Height:  |  Size: 342 B

View File

@ -43,7 +43,7 @@ a,
color: var(--color-contrast);
background-color: var(--color-button-bg);
box-shadow: var(--shadow-inset-sm), 0 0 0 0 transparent;
border-radius: var(--radius-sm);
border-radius: var(--radius-md);
}
.standard-body {
@ -51,3 +51,35 @@ a,
margin-bottom: 0;
}
}
.button-within {
transition: opacity 0.5s ease-in-out, filter 0.2s ease-in-out, transform 0.05s ease-in-out,
outline 0.2s ease-in-out;
&:focus-visible:not(&.disabled),
&:hover:not(&.disabled) {
filter: brightness(0.85);
}
&:active:not(&.disabled) {
filter: brightness(0.8);
}
// For some reason this within the above block makes it universal and not only applied to children. SCSS bug maybe?
&:active:not(&.disabled) button:not(&:disabled) {
transform: scale(0.95);
}
&.disabled {
cursor: not-allowed;
filter: grayscale(50%);
opacity: 0.5;
box-shadow: none;
&disabled,
&[disabled] {
cursor: not-allowed;
box-shadow: none;
}
}
}

View File

@ -29,3 +29,8 @@ a {
a.uncolored {
color: inherit;
}
svg {
height: 1em;
width: 1em;
}

View File

@ -77,6 +77,7 @@ $colors: (
'contrast': var(--color-contrast),
'accent-contrast': var(--color-accent-contrast),
'brand': var(--color-brand),
'brand-highlight': var(--color-brand-highlight),
'red': var(--color-red),
'orange': var(--color-orange),
'green': var(--color-green),

View File

@ -17,7 +17,8 @@ html {
--radius-max: 999999999px;
}
.light-mode, .light {
.light-mode,
.light {
--color-bg: #e5e7eb;
--color-raised-bg: #ffffff;
--color-button-bg: hsl(220, 13%, 91%);
@ -34,9 +35,24 @@ html {
--color-gray: #595b61;
--color-brand: var(--color-green);
--color-brand-highlight: rgba(0, 175, 92, 0.25);
--shadow-inset-lg: inset 0px -2px 2px hsla(221, 39%, 11%, 0.1);
--shadow-inset: inset 0px -2px 2px hsla(221, 39%, 11%, 0.05);
--shadow-inset-sm: inset 0px -1px 2px hsla(221, 39%, 11%, 0.15);
--shadow-raised-lg: 0px 2px 4px hsla(221, 39%, 11%, 0.2);
--shadow-raised: 0.3px 0.5px 0.6px hsl(var(--shadow-color) / 0.15),
1px 2px 2.2px -1.7px hsl(var(--shadow-color) / 0.12),
4.4px 8.8px 9.7px -3.4px hsl(var(--shadow-color) / 0.09);
--shadow-floating: hsla(0, 0%, 0%, 0) 0px 0px 0px 0px, hsla(0, 0%, 0%, 0) 0px 0px 0px 0px,
hsla(0, 0%, 0%, 0.1) 0px 4px 6px -1px, hsla(0, 0%, 0%, 0.1) 0px 2px 4px -1px;
--shadow-card: rgba(50, 50, 100, 0.1) 0px 2px 4px 0px;
}
.dark-mode, .dark {
.dark-mode,
.dark {
--color-bg: #16181c;
--color-raised-bg: #26292f;
--color-button-bg: hsl(222, 13%, 30%);
@ -53,6 +69,18 @@ html {
--color-gray: #9fa4b3;
--color-brand: var(--color-green);
--color-brand-highlight: rgba(27, 217, 106, 0.25);
--shadow-inset-lg: inset 0px -2px 2px hsla(221, 39%, 11%, 0.1);
--shadow-inset: inset 0px -2px 2px hsla(221, 39%, 11%, 0.05);
--shadow-inset-sm: inset 0px -1px 1px hsla(221, 39%, 11%, 0.25);
--shadow-raised-lg: 0px 2px 4px hsla(221, 39%, 11%, 0.2);
--shadow-raised: 0px -2px 4px hsla(221, 39%, 11%, 0.1);
--shadow-floating: hsla(0, 0%, 0%, 0) 0px 0px 0px 0px, hsla(0, 0%, 0%, 0) 0px 0px 0px 0px,
hsla(0, 0%, 0%, 0.1) 0px 4px 6px -1px, rgba(0, 0, 0, 0.06) 0px 2px 4px -1px;
--shadow-card: rgba(0, 0, 0, 0.25) 0px 2px 4px 0px;
}
.oled-mode {

View File

@ -0,0 +1,120 @@
<template>
<img
v-if="src"
ref="img"
:class="`avatar size-${size} ${circle ? 'circle' : ''} ${noShadow ? 'no-shadow' : ''}`"
:src="src"
:alt="alt"
:loading="loading"
/>
<svg
v-else
:class="`avatar size-${size} ${circle ? 'circle' : ''} ${noShadow ? 'no-shadow' : ''}`"
xml:space="preserve"
fill-rule="evenodd"
stroke-linecap="round"
stroke-linejoin="round"
stroke-miterlimit="1.5"
clip-rule="evenodd"
viewBox="0 0 104 104"
aria-hidden="true"
>
<path fill="none" d="M0 0h103.4v103.4H0z" />
<path
fill="none"
stroke="#9a9a9a"
stroke-width="5"
d="M51.7 92.5V51.7L16.4 31.3l35.3 20.4L87 31.3 51.7 11 16.4 31.3v40.8l35.3 20.4L87 72V31.3L51.7 11"
/>
</svg>
</template>
<script>
export default {
props: {
src: {
type: String,
default: null,
},
alt: {
type: String,
default: '',
},
size: {
type: String,
default: 'sm',
validator(value) {
return ['xs', 'sm', 'md', 'lg'].includes(value)
},
},
circle: {
type: Boolean,
default: false,
},
noShadow: {
type: Boolean,
default: false,
},
loading: {
type: String,
default: 'eager',
},
},
mounted() {
if (this.$refs.img && this.$refs.img.naturalWidth) {
const isPixelated = () => {
if (this.$refs.img.naturalWidth < 96 && this.$refs.img.naturalWidth > 0) {
this.$refs.img.style.imageRendering = 'pixelated'
}
}
if (this.$refs.img.naturalWidth) {
isPixelated()
} else {
this.$refs.img.onload = isPixelated
}
}
},
}
</script>
<style lang="scss" scoped>
.avatar {
border-radius: var(--radius-md);
box-shadow: var(--shadow-inset-lg), var(--shadow-card);
height: var(--size);
width: var(--size);
background-color: var(--color-button-bg);
object-fit: contain;
&.size-xs {
--size: 2.5rem;
box-shadow: var(--shadow-inset), var(--shadow-card);
border-radius: var(--radius-sm);
}
&.size-sm {
--size: 3rem;
box-shadow: var(--shadow-inset), var(--shadow-card);
border-radius: var(--radius-sm);
}
&.size-md {
--size: 6rem;
border-radius: var(--radius-lg);
}
&.size-lg {
--size: 9rem;
border-radius: var(--radius-lg);
}
&.circle {
border-radius: 50%;
}
&.no-shadow {
box-shadow: none;
}
}
</style>

View File

@ -0,0 +1,118 @@
<template>
<span :class="'version-badge ' + color + ' type--' + type">
<template v-if="color"> <span class="circle" /> {{ type }} </template>
<!-- User roles -->
<template v-else-if="type === 'admin'"> <ModrinthIcon /> Modrinth Team </template>
<template v-else-if="type === 'moderator'"> <ScaleIcon /> Moderator </template>
<template v-else-if="type === 'creator'"><BoxIcon /> Creator</template>
<!-- Project statuses -->
<template v-else-if="type === 'approved'"><ListIcon /> Listed</template>
<template v-else-if="type === 'unlisted'"><EyeOffIcon /> Unlisted</template>
<template v-else-if="type === 'withheld'"><EyeOffIcon /> Withheld</template>
<template v-else-if="type === 'private'"><LockIcon /> Private</template>
<template v-else-if="type === 'scheduled'"> <CalendarIcon /> Scheduled </template>
<template v-else-if="type === 'draft'"><FileTextIcon /> Draft</template>
<template v-else-if="type === 'archived'"> <ArchiveIcon /> Archived </template>
<template v-else-if="type === 'rejected'"><XIcon /> Rejected</template>
<template v-else-if="type === 'processing'"> <UpdatedIcon /> Under review </template>
<!-- Team members -->
<template v-else-if="type === 'accepted'"><CheckIcon /> Accepted</template>
<template v-else-if="type === 'pending'"> <UpdatedIcon /> Pending </template>
<template v-else> <span class="circle" /> {{ type }} </template>
</span>
</template>
<script setup>
import {
ModrinthIcon,
ScaleIcon,
BoxIcon,
ListIcon,
EyeOffIcon,
FileTextIcon,
XIcon,
ArchiveIcon,
UpdatedIcon,
CheckIcon,
LockIcon,
CalendarIcon,
} from '@/components'
defineProps({
type: {
type: String,
required: true,
},
color: {
type: String,
default: '',
},
})
</script>
<style lang="scss" scoped>
.version-badge {
display: flex;
align-items: center;
font-weight: bold;
width: fit-content;
--badge-color: var(--color-gray);
color: var(--badge-color);
.circle {
width: 0.5rem;
height: 0.5rem;
border-radius: 50%;
display: inline-block;
margin-right: 0.25rem;
background-color: var(--badge-color);
}
svg {
margin-right: 0.25rem;
}
&.type--withheld,
&.type--rejected,
&.red {
--badge-color: var(--color-red);
}
&.type--pending,
&.type--moderator,
&.type--processing,
&.type--scheduled,
&.orange {
--badge-color: var(--color-orange);
}
&.type--accepted,
&.type--admin,
&.green {
--badge-color: var(--color-green);
}
&.type--creator,
&.type--approved,
&.blue {
color: var(--color-blue);
}
&.type--unlisted,
&.purple {
color: var(--color-purple);
}
&.type--private,
&.gray {
--badge-color: var(--color-gray);
}
&::first-letter {
text-transform: capitalize;
}
}
</style>

View File

@ -1,5 +1,5 @@
<script setup>
import { IssuesIcon, ExternalIcon, UnknownIcon} from '@/components'
import { ExternalIcon, UnknownIcon } from '@/components'
import { computed } from 'vue'
@ -27,7 +27,7 @@ const props = defineProps({
iconOnly: {
type: Boolean,
default: false,
}
},
})
const defaultDesign = computed(() => props.design === 'default')
@ -56,7 +56,7 @@ const accentedButton = computed(
<!-- </nuxt-link>-->
<a
v-if="link"
class="omorphia__button button-base padding-block-sm padding-inline-lg radius-sm"
class="omorphia__button button-base padding-block-sm padding-inline-lg radius-md"
:class="{
'standard-button': defaultDesign,
'icon-only': props.iconOnly,
@ -73,8 +73,8 @@ const accentedButton = computed(
<UnknownIcon v-if="!$slots.default" />
</a>
<button
v-else-if="action"
class="omorphia__button button-base padding-block-sm padding-inline-lg radius-sm"
v-else
class="omorphia__button button-base padding-block-sm padding-inline-lg radius-md"
:class="{
'standard-button': defaultDesign,
'icon-only': props.iconOnly,
@ -88,13 +88,6 @@ const accentedButton = computed(
<slot />
<UnknownIcon v-if="!$slots.default" />
</button>
<div
v-else
class="omorphia__button button-base button-color-base padding-block-sm padding-inline-lg radius-sm bg-red color-accent-contrast"
>
<IssuesIcon />
Missing link or action!
</div>
</template>
<style lang="scss" scoped>

View File

@ -0,0 +1,136 @@
<template>
<div
class="checkbox-outer button-within"
:class="{ disabled }"
role="presentation"
@click="toggle"
>
<button
class="checkbox"
role="checkbox"
:disabled="disabled"
:class="{ checked: modelValue, collapsing: collapsingToggleStyle }"
:aria-label="description"
:aria-checked="modelValue"
>
<CheckIcon v-if="modelValue && !collapsingToggleStyle" aria-hidden="true" />
<DropdownIcon v-else-if="collapsingToggleStyle" aria-hidden="true" />
</button>
<!-- aria-hidden is set so screenreaders only use the <button>'s aria-label -->
<p v-if="label" aria-hidden="true">
{{ label }}
</p>
<slot v-else />
</div>
</template>
<script setup>
import { CheckIcon, DropdownIcon } from '@/components'
</script>
<script>
import { defineComponent } from 'vue'
export default defineComponent({
props: {
label: {
type: String,
default: '',
},
disabled: {
type: Boolean,
default: false,
},
description: {
type: String,
default: '',
},
modelValue: Boolean,
clickEvent: {
type: Function,
default: () => {},
},
collapsingToggleStyle: {
type: Boolean,
default: false,
},
},
emits: ['update:modelValue'],
methods: {
toggle() {
if (!this.disabled) {
this.$emit('update:modelValue', !this.modelValue)
}
},
},
})
</script>
<style lang="scss" scoped>
.checkbox-outer {
display: flex;
align-items: center;
cursor: pointer;
p {
user-select: none;
padding: 0.2rem 0;
margin: 0;
}
&.disabled {
cursor: not-allowed;
}
}
.checkbox {
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
min-width: 1rem;
min-height: 1rem;
padding: 0;
margin: 0 0.5rem 0 0;
color: var(--color-contrast);
background-color: var(--color-button-bg);
border-radius: var(--radius-xs);
box-shadow: var(--shadow-inset-sm), 0 0 0 0 transparent;
&.checked {
background-color: var(--color-brand);
}
svg {
color: var(--color-accent-contrast);
stroke-width: 0.2rem;
height: 0.8rem;
width: 0.8rem;
flex-shrink: 0;
}
&.collapsing {
background-color: transparent !important;
box-shadow: none;
svg {
color: inherit;
height: 1rem;
width: 1rem;
transition: transform 0.25s ease-in-out;
}
&.checked {
svg {
transform: rotate(180deg);
}
}
}
&:disabled {
box-shadow: none;
cursor: not-allowed;
}
}
</style>

View File

@ -0,0 +1,94 @@
<template>
<div class="chips">
<Button
v-for="item in items"
:key="item"
class="iconified-button"
:class="{ selected: selected === item }"
@click="toggleItem(item)"
>
<CheckIcon v-if="selected === item" />
<span>{{ formatLabel(item) }}</span>
</Button>
</div>
</template>
<script setup>
import { CheckIcon, Button } from '@/components'
</script>
<script>
import { defineComponent } from 'vue'
export default defineComponent({
props: {
modelValue: {
required: true,
type: String,
},
items: {
required: true,
type: Array,
},
neverEmpty: {
default: true,
type: Boolean,
},
formatLabel: {
default: (x) => x,
type: Function,
},
},
emits: ['update:modelValue'],
computed: {
selected: {
get() {
return this.modelValue
},
set(value) {
this.$emit('update:modelValue', value)
},
},
},
created() {
if (this.items.length > 0 && this.neverEmpty) {
this.selected = this.items[0]
}
},
methods: {
toggleItem(item) {
if (this.selected === item && !this.neverEmpty) {
this.selected = null
} else {
this.selected = item
}
},
},
})
</script>
<style lang="scss" scoped>
.chips {
display: flex;
grid-gap: 0.5rem;
flex-wrap: wrap;
.iconified-button {
text-transform: capitalize;
svg {
width: 1em;
height: 1em;
}
&:focus-visible {
outline: 0.25rem solid #ea80ff;
border-radius: 0.25rem;
}
}
.selected {
color: var(--color-contrast);
background-color: var(--color-brand-highlight);
box-shadow: inset 0 0 0 transparent, 0 0 0 2px var(--color-brand);
}
}
</style>

View File

@ -0,0 +1,202 @@
<template>
<div v-if="count > 1" class="paginates">
<a
:class="{ disabled: page === 1 }"
:tabindex="page === 1 ? -1 : 0"
class="left-arrow paginate has-icon"
aria-label="Previous Page"
:href="linkFunction(page - 1)"
@click.prevent="page !== 1 ? switchPage(page - 1) : null"
>
<LeftArrowIcon />
</a>
<div
v-for="(item, index) in pages"
:key="'page-' + item + '-' + index"
:class="{
'page-number': page !== item,
shrink: item > 99,
}"
class="page-number-container"
>
<div v-if="item === '-'" class="has-icon">
<GapIcon />
</div>
<a
v-else
:class="{
'page-number current': page === item,
shrink: item > 99,
}"
:href="linkFunction(item)"
@click.prevent="page !== item ? switchPage(item) : null"
>
{{ item }}
</a>
</div>
<a
:class="{
disabled: page === pages[pages.length - 1],
}"
:tabindex="page === pages[pages.length - 1] ? -1 : 0"
class="right-arrow paginate has-icon"
aria-label="Next Page"
:href="linkFunction(page + 1)"
@click.prevent="page !== pages[pages.length - 1] ? switchPage(page + 1) : null"
>
<RightArrowIcon />
</a>
</div>
</template>
<script setup>
import { GapIcon, LeftArrowIcon, RightArrowIcon } from '@/components'
</script>
<script>
import { defineComponent } from 'vue'
export default defineComponent({
props: {
page: {
type: Number,
default: 1,
},
count: {
type: Number,
default: 1,
},
linkFunction: {
type: Function,
default() {
return null
},
},
},
emits: ['switch-page'],
computed: {
pages() {
let pages = []
if (this.count > 4) {
if (this.page + 3 >= this.count) {
pages = [
1,
'-',
this.count - 4,
this.count - 3,
this.count - 2,
this.count - 1,
this.count,
]
} else if (this.page > 4) {
pages = [1, '-', this.page - 1, this.page, this.page + 1, '-', this.count]
} else {
pages = [1, 2, 3, 4, 5, '-', this.count]
}
} else {
pages = Array.from({ length: this.count }, (_, i) => i + 1)
}
return pages
},
},
methods: {
switchPage(newPage) {
this.$emit('switch-page', newPage)
},
},
})
</script>
<style scoped lang="scss">
.paginates {
display: flex;
}
a {
color: var(--color-contrast);
box-shadow: var(--shadow-raised), var(--shadow-inset);
padding: 0.5rem 1rem;
margin: 0;
border-radius: 2rem;
background: var(--color-raised-bg);
cursor: pointer;
transition: opacity 0.5s ease-in-out, filter 0.2s ease-in-out, transform 0.05s ease-in-out,
outline 0.2s ease-in-out;
&:hover {
color: inherit;
text-decoration: none;
}
&.page-number.current {
background: var(--color-brand);
color: var(--color-accent-contrast);
cursor: default;
}
&.paginate.disabled {
background-color: transparent;
cursor: not-allowed;
filter: grayscale(50%);
opacity: 0.5;
}
&:hover:not(&:disabled) {
filter: brightness(0.85);
}
&:active:not(&:disabled) {
transform: scale(0.95);
filter: brightness(0.8);
}
}
.has-icon {
display: flex;
align-items: center;
svg {
width: 1em;
}
}
.page-number-container,
a,
.has-icon {
display: flex;
justify-content: center;
align-items: center;
}
.paginates {
height: 2em;
margin: 0.5rem 0;
> div,
.has-icon {
margin: 0 0.3em;
}
}
.left-arrow {
margin-left: auto !important;
}
.right-arrow {
margin-right: auto !important;
}
@media screen and (max-width: 400px) {
.paginates {
font-size: 80%;
}
}
@media screen and (max-width: 530px) {
a {
width: 2.5rem;
padding: 0.5rem 0;
}
}
</style>

View File

@ -1,6 +1,11 @@
export { default as Card } from './base/Card.vue'
export { default as Page } from './base/Page.vue'
export { default as Avatar } from './base/Avatar.vue'
export { default as Badge } from './base/Badge.vue'
export { default as Button } from './base/Button.vue'
export { default as Card } from './base/Card.vue'
export { default as Checkbox } from './base/Checkbox.vue'
export { default as Chips } from './base/Chips.vue'
export { default as Page } from './base/Page.vue'
export { default as Pagination } from './base/Pagination.vue'
export { default as NavItem } from './nav/NavItem.vue'
export { default as NavRow } from './nav/NavRow.vue'
@ -24,7 +29,7 @@ export { default as ClientIcon } from '@/assets/icons/client.svg'
export { default as ClipboardCopyIcon } from '@/assets/icons/clipboard-copy.svg'
export { default as CoinsIcon } from '@/assets/icons/coins.svg'
export { default as ContractIcon } from '@/assets/icons/contract.svg'
export { default as copyrightIcon } from '@/assets/icons/copyright.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'
export { default as DownloadIcon } from '@/assets/icons/download.svg'
@ -61,6 +66,7 @@ export { default as PlusIcon } from '@/assets/icons/plus.svg'
export { default as ReportIcon } from '@/assets/icons/report.svg'
export { default as RightArrowIcon } from '@/assets/icons/right-arrow.svg'
export { default as SaveIcon } from '@/assets/icons/save.svg'
export { default as ScaleIcon } from '@/assets/icons/scale.svg'
export { default as SearchIcon } from '@/assets/icons/search.svg'
export { default as SendIcon } from '@/assets/icons/send.svg'
export { default as ServerIcon } from '@/assets/icons/server.svg'
@ -86,3 +92,5 @@ export { default as UsersIcon } from '@/assets/icons/users.svg'
export { default as VersionIcon } from '@/assets/icons/version.svg'
export { default as WikiIcon } from '@/assets/icons/wiki.svg'
export { default as XIcon } from '@/assets/icons/x.svg'
export { default as ModrinthIcon } from '@/assets/branding/logo.svg'

View File

@ -1,6 +1,6 @@
import * as components from './components'
function install (app) {
function install(app) {
for (const key in components) {
app.component(key, components[key])
}

View File

@ -2,7 +2,9 @@
"name": "omorphia",
"type": "module",
"version": "0.0.0",
"files": ["dist"],
"files": [
"dist"
],
"main": "./dist/omorphia.umd.cjs",
"module": "./dist/omorphia.js",
"exports": {