This commit is contained in:
Zachary Baird
2023-05-02 12:49:00 -04:00
7 changed files with 182 additions and 107 deletions

View File

@@ -1,5 +1,6 @@
import { resolve } from 'path' import { resolve } from 'path'
import svgLoader from 'vite-svg-loader' import svgLoader from 'vite-svg-loader'
import eslintPlugin from 'vite-plugin-eslint'
export default { export default {
title: 'Omorphia', title: 'Omorphia',
@@ -66,6 +67,7 @@ export default {
], ],
}, },
}), }),
eslintPlugin(),
], ],
resolve: { resolve: {
alias: { alias: {

View File

@@ -7,18 +7,29 @@ const value = ref(null)
<DemoContainer> <DemoContainer>
<DropdownSelect <DropdownSelect
id="report-type"
v-model="value" v-model="value"
:options="['Daily', 'Weekly', 'Monthly']" :options="['Daily', 'Weekly', 'Monthly', 'Tomorrow', 'Yesterday', 'Today', 'Biweekly', 'Tuesday', 'January']"
placeholder="Choose Frequency" placeholder="Choose Frequency"
/> />
<DropdownSelect
v-model="value"
:options="['Daily', 'Weekly', 'Monthly', 'Tomorrow', 'Yesterday', 'Today', 'Biweekly', 'Tuesday', 'January']"
placeholder="Choose Frequency"
render-up
/>
<DropdownSelect
v-model="value"
:options="['Daily', 'Weekly', 'Monthly', 'Tomorrow', 'Yesterday', 'Today', 'Biweekly', 'Tuesday', 'January']"
placeholder="Choose Frequency"
disabled
/>
</DemoContainer> </DemoContainer>
```vue ```vue
<DropdownSelect <DropdownSelect
id="report-type" v-model="value"
v-model="reportType" :options="['Daily', 'Weekly', 'Monthly', 'Tomorrow', 'Yesterday', 'Today', 'Biweekly', 'Tuesday', 'January']"
:options="['Daily', 'Weekly', 'Monthly']"
placeholder="Choose Frequency" placeholder="Choose Frequency"
render-up
/> />
``` ```

View File

@@ -315,6 +315,11 @@ a,
margin: auto; margin: auto;
} }
} }
&.transparent {
background: none;
box-shadow: none;
}
} }
.btn-group { .btn-group {
@@ -469,17 +474,33 @@ a,
// TABLE // TABLE
.table { .table {
.table-container { display: grid;
display: grid; grid-template-rows: repeat(auto-fill, auto);
grid-template-rows: repeat(auto-fill, auto); width: 100%;
width: 100%; border-radius: var(--radius-md);
border-radius: var(--radius-md); overflow: hidden;
overflow: hidden;
}
.table-row { .table-row {
display: grid; display: grid;
grid-template-columns: 1fr 4fr 1.5fr;
transition: opacity 0.5s ease-in-out, filter 0.2s ease-in-out, scale 0.05s ease-in-out,
outline 0.2s ease-in-out;
&.selectable:focus-visible,
&.selectable:hover {
cursor: pointer;
filter: brightness(0.85);
}
&.selectable:active {
filter: brightness(0.8);
scale: 0.99;
}
}
.entire-row {
grid-template-columns: 1fr !important;
align-items: center;
} }
.table-head { .table-head {
@@ -491,42 +512,25 @@ a,
.table-cell { .table-cell {
padding: 1rem; padding: 1rem;
height: 100%; height: 100%;
align-items: center;
vertical-align: center; vertical-align: center;
display: flex; display: flex;
} }
.name-cell {
padding-left: 0;
}
.table-text { .table-text {
overflow: hidden; overflow: hidden;
white-space: nowrap; white-space: nowrap;
text-overflow: ellipsis; text-overflow: fade;
display: flex;
span {
display: inline-block;
text-overflow: ellipsis;
overflow: hidden;
}
} }
button { .table-row:nth-child(even) .table-cell {
width: 100%; background-color: var(--color-bg);
display: flex;
align-items: center;
justify-content: center;
span {
display: inherit;
align-items: center;
justify-content: center;
}
} }
} }
.table-row:nth-child(even) .table-cell {
background-color: var(--color-bg);
}
// CUSTOM COMPONENTS // CUSTOM COMPONENTS
// TODO: MOST OF THESE SHOULD BE MOVED TO AN OMORPHIA COMPONENT // TODO: MOST OF THESE SHOULD BE MOVED TO AN OMORPHIA COMPONENT
.textarea-wrapper { .textarea-wrapper {

View File

@@ -56,7 +56,6 @@ input[type='number'],
textarea { textarea {
border-radius: var(--radius-md); border-radius: var(--radius-md);
box-sizing: border-box; box-sizing: border-box;
border: 2px solid transparent;
// safari iOS rounds inputs by default // safari iOS rounds inputs by default
// set the appearance to none to prevent this // set the appearance to none to prevent this
appearance: none !important; appearance: none !important;
@@ -64,10 +63,9 @@ textarea {
color: var(--color-base); color: var(--color-base);
padding: 0.5rem 1rem; padding: 0.5rem 1rem;
font-weight: var(--font-weight-medium); font-weight: var(--font-weight-medium);
outline: 2px solid transparent;
box-shadow: var(--shadow-inset-sm), 0 0 0 0 transparent;
transition: box-shadow 0.1s ease-in-out; transition: box-shadow 0.1s ease-in-out;
min-height: 40px; min-height: 40px;
box-shadow: var(--shadow-inset-sm), 0 0 0 0 transparent;
&:focus, &:focus,
&:focus-visible { &:focus-visible {
@@ -87,7 +85,7 @@ textarea {
} }
&::placeholder { &::placeholder {
color: var(--color-contrast); color: var(--color-base);
opacity: 0.6; opacity: 0.6;
} }
} }
@@ -111,16 +109,19 @@ input[type='number'] {
} }
&:focus-within svg { &:focus-within svg {
color: var(--color-base);
opacity: 1; opacity: 1;
color: var(--color-contrast);
} }
:not(input) { svg {
position: absolute; position: absolute;
left: 0.75rem; left: 0.75rem;
height: 1.25rem; height: 1.25rem;
width: 1.25rem; width: 1.25rem;
z-index: 1; z-index: 1;
color: var(--color-base);
opacity: 0.6;
} }
} }

View File

@@ -21,6 +21,7 @@ html {
.light { .light {
--color-bg: #e5e7eb; --color-bg: #e5e7eb;
--color-raised-bg: #ffffff; --color-raised-bg: #ffffff;
--color-super-raised-bg: #e9e9e9;
--color-button-bg: hsl(220, 13%, 91%); --color-button-bg: hsl(220, 13%, 91%);
--color-base: hsl(221, 39%, 11%); --color-base: hsl(221, 39%, 11%);
@@ -59,6 +60,7 @@ html {
.dark { .dark {
--color-bg: #16181c; --color-bg: #16181c;
--color-raised-bg: #26292f; --color-raised-bg: #26292f;
--color-super-raised-bg: #40434a;
--color-button-bg: hsl(222, 13%, 30%); --color-button-bg: hsl(222, 13%, 30%);
--color-base: var(--dark-color-base); --color-base: var(--dark-color-base);

View File

@@ -12,35 +12,50 @@
@keydown.up.prevent="focusPreviousOption" @keydown.up.prevent="focusPreviousOption"
@keydown.down.prevent="focusNextOptionOrOpen" @keydown.down.prevent="focusNextOptionOrOpen"
> >
<div class="selected" :class="{ 'dropdown-open': dropdownVisible }" @click="toggleDropdown"> <div
class="selected"
:class="{
disabled: disabled,
'render-down': dropdownVisible && !renderUp && !disabled,
'render-up': dropdownVisible && renderUp && !disabled,
}"
@click="toggleDropdown"
>
<span>{{ selectedOption }}</span> <span>{{ selectedOption }}</span>
<i class="arrow" :class="{ rotate: dropdownVisible }"></i> <i class="arrow" :class="{ rotate: dropdownVisible }"></i>
</div> </div>
<transition name="slide-fade"> <div class="options-wrapper" :class="{ down: !renderUp, up: renderUp }">
<div v-show="dropdownVisible" class="options" role="listbox"> <transition name="options">
<div <div
v-for="(option, index) in options" v-show="dropdownVisible"
:key="index" class="options"
ref="optionElements" role="listbox"
tabindex="-1" :class="{ down: !renderUp, up: renderUp }"
role="option"
:class="{ 'selected-option': selectedValue === option }"
:aria-selected="selectedValue === option"
class="option"
@click="selectOption(option, index)"
@keydown.space.prevent="selectOption(option, index)"
> >
<input <div
:id="`${name}-${index}`" v-for="(option, index) in options"
v-model="radioValue" :key="index"
type="radio" ref="optionElements"
:value="option" tabindex="-1"
:name="name" role="option"
/> :class="{ 'selected-option': selectedValue === option }"
<label :for="`${name}-${index}`">{{ option }}</label> :aria-selected="selectedValue === option"
class="option"
@click="selectOption(option, index)"
@keydown.space.prevent="selectOption(option, index)"
>
<input
:id="`${name}-${index}`"
v-model="radioValue"
type="radio"
:value="option"
:name="name"
/>
<label :for="`${name}-${index}`">{{ option }}</label>
</div>
</div> </div>
</div> </transition>
</transition> </div>
</div> </div>
</template> </template>
@@ -67,6 +82,14 @@ export default {
type: String, type: String,
default: null, default: null,
}, },
renderUp: {
type: Boolean,
default: false,
},
disabled: {
type: Boolean,
default: false,
},
}, },
emits: ['input', 'change', 'update:modelValue'], emits: ['input', 'change', 'update:modelValue'],
data() { data() {
@@ -92,8 +115,10 @@ export default {
}, },
methods: { methods: {
toggleDropdown() { toggleDropdown() {
this.dropdownVisible = !this.dropdownVisible if (!this.disabled) {
this.$refs.dropdown.focus() this.dropdownVisible = !this.dropdownVisible
this.$refs.dropdown.focus()
}
}, },
selectOption(option, index) { selectOption(option, index) {
this.radioValue = option this.radioValue = option
@@ -101,8 +126,10 @@ export default {
this.dropdownVisible = false this.dropdownVisible = false
}, },
onFocus() { onFocus() {
this.focusedOptionIndex = this.options.findIndex((option) => option === this.selectedValue) if (!this.disabled) {
this.dropdownVisible = true this.focusedOptionIndex = this.options.findIndex((option) => option === this.selectedValue)
this.dropdownVisible = true
}
}, },
onBlur(event) { onBlur(event) {
if (!this.isChildOfDropdown(event.relatedTarget)) { if (!this.isChildOfDropdown(event.relatedTarget)) {
@@ -110,19 +137,23 @@ export default {
} }
}, },
focusPreviousOption() { focusPreviousOption() {
if (!this.dropdownVisible) { if (!this.disabled) {
this.toggleDropdown() if (!this.dropdownVisible) {
this.toggleDropdown()
}
this.focusedOptionIndex =
(this.focusedOptionIndex + this.options.length - 1) % this.options.length
this.$refs.optionElements[this.focusedOptionIndex].focus()
} }
this.focusedOptionIndex =
(this.focusedOptionIndex + this.options.length - 1) % this.options.length
this.$refs.optionElements[this.focusedOptionIndex].focus()
}, },
focusNextOptionOrOpen() { focusNextOptionOrOpen() {
if (!this.dropdownVisible) { if (!this.disabled) {
this.toggleDropdown() if (!this.dropdownVisible) {
this.toggleDropdown()
}
this.focusedOptionIndex = (this.focusedOptionIndex + 1) % this.options.length
this.$refs.optionElements[this.focusedOptionIndex].focus()
} }
this.focusedOptionIndex = (this.focusedOptionIndex + 1) % this.options.length
this.$refs.optionElements[this.focusedOptionIndex].focus()
}, },
isChildOfDropdown(element) { isChildOfDropdown(element) {
let currentNode = element let currentNode = element
@@ -157,13 +188,18 @@ export default {
cursor: pointer; cursor: pointer;
user-select: none; user-select: none;
border-radius: var(--radius-md); border-radius: var(--radius-md);
box-shadow: var(--shadow-inset-sm), 0 0 0 0 transparent;
&:hover { &.disabled {
filter: brightness(1.25); cursor: not-allowed;
transition: filter 0.3s ease-in-out; filter: brightness(0.75);
} }
&.dropdown-open { &.render-up {
border-radius: 0 0 var(--radius-md) var(--radius-md);
}
&.render-down {
border-radius: var(--radius-md) var(--radius-md) 0 0; border-radius: var(--radius-md) var(--radius-md) 0 0;
} }
@@ -181,7 +217,7 @@ export default {
border-left: 0.4rem solid transparent; border-left: 0.4rem solid transparent;
border-right: 0.4rem solid transparent; border-right: 0.4rem solid transparent;
border-top: 0.4rem solid var(--color-base); border-top: 0.4rem solid var(--color-base);
transition: transform 0.3s ease; transition: transform 0.2s ease;
&.rotate { &.rotate {
transform: rotate(180deg); transform: rotate(180deg);
} }
@@ -189,11 +225,10 @@ export default {
} }
.options { .options {
position: absolute;
width: 100%;
z-index: 10; z-index: 10;
border-radius: 0 0 var(--radius-lg) var(--radius-lg); max-height: min(12rem);
overflow: hidden; overflow-y: auto;
box-shadow: var(--shadow-inset-sm), 0 0 0 0 transparent;
.option { .option {
background-color: var(--color-button-bg); background-color: var(--color-button-bg);
@@ -204,19 +239,20 @@ export default {
user-select: none; user-select: none;
&:hover { &:hover {
filter: brightness(1.25); filter: brightness(0.85);
transition: filter 0.3s ease-in-out; transition: filter 0.2s ease-in-out;
} }
&:focus { &:focus {
outline: 0; outline: 0;
filter: brightness(1.25); filter: brightness(0.85);
transition: filter 0.3s ease-in-out; transition: filter 0.2s ease-in-out;
} }
&.selected-option { &.selected-option {
background-color: var(--color-brand); background-color: var(--color-brand);
color: var(--color-accent-contrast); color: var(--color-accent-contrast);
font-weight: bolder;
} }
input { input {
@@ -226,24 +262,43 @@ export default {
} }
} }
.slide-fade-enter { .options-enter-active,
opacity: 0; .options-leave-active {
transform: translateY(-20px); transition: transform 0.2s ease;
} }
.slide-fade-enter-to { .options-enter-from,
opacity: 1; .options-leave-to {
transform: translateY(0); &.up {
transform: translateY(100%);
}
&.down {
transform: translateY(-100%);
}
} }
.slide-fade-enter-active, .options-enter-to,
.slide-fade-leave-active { .options-leave-from {
transition: opacity 0.5s ease, transform 0.3s ease; &.up {
transform: translateY(0%);
}
} }
.slide-fade-leave, .options-wrapper {
.slide-fade-leave-to { position: absolute;
opacity: 0; width: 100%;
transform: translateY(-20px); overflow: auto;
z-index: 9;
&.up {
top: 0;
transform: translateY(-100%);
border-radius: var(--radius-md) var(--radius-md) 0 0;
}
&.down {
border-radius: 0 0 var(--radius-md) var(--radius-md);
}
} }
</style> </style>

View File

@@ -1,7 +1,7 @@
{ {
"name": "omorphia", "name": "omorphia",
"type": "module", "type": "module",
"version": "0.4.7", "version": "0.4.11",
"files": [ "files": [
"dist" "dist"
], ],
@@ -18,7 +18,7 @@
"build": "vite build", "build": "vite build",
"lint:js": "eslint --ext .js,.vue,.ts,.jsx,.tsx,.html,.vue .", "lint:js": "eslint --ext .js,.vue,.ts,.jsx,.tsx,.html,.vue .",
"lint": "npm run lint:js && prettier --check .", "lint": "npm run lint:js && prettier --check .",
"fix": "eslint --fix --ext .js,.vue,.ts,.jsx,.tsx,.html,.vue .", "fix": "eslint --fix --ext .js,.vue,.ts,.jsx,.tsx,.html,.vue . && prettier --write .",
"docs:dev": "vitepress dev docs", "docs:dev": "vitepress dev docs",
"docs:build": "vitepress build docs", "docs:build": "vitepress build docs",
"docs:preview": "vitepress preview docs" "docs:preview": "vitepress preview docs"