109 tiny tweaks (#574)

* no cursor when disabled

* consistent disabled styling

* divider icon alignment

* don’t show last item’s border

* notifications spacing

* status placeholder

* default status icon placeholder

* fix textarea focus style

* tags styling

* proper tags padding when empty

* proper input number step hovers

* show background color

* Fix data-model collections overview name class

* Don't use display template for batch mode

* Fix headline being hidden

* Use formatted name fo bookmarks breadcrumb

* Move drawer open to app store

* Fix tests

* slider value style

* Add comments to users/files

* Make comments selectable

* Move window width drawer state to app parent

* Fix private user condition

* Allow relationships to system collections

* Refresh revisions drawer detail on save and stay

* Add disabled support to m2o / user

* Center v-infos

* Hide default drag image

* Ellipsis all the things

* Use icon interface for fallback icon

* Render icons grid based on available space

* Fix ellipsis on cardsl

* fix batch edit checkbox styling

* Let render template ellipsis its raw values

* Fix render template

* Default cropping to current aspect ratio

* missing translation

* secondary button style

so sorry, rijk… it’s the only one (promise)

* Add image dimensions, add drag mode

* track the apology

* no elipses on titles

* Add cancel crop button

* Only show new dimensions on crop

* Inform file preview if it's in modal

* preview styling

* Install pretty-bytes

* Show file info in drawer sidebar

* Use outline icons in drawer sidebar

* don’t confuse null with subdued text value

* edge-case justification

* Show character count remaining

* Fix storybook + typing error

* Add length constraints to color

* Watch value prop

* Fix tags

* Open icon on icon click

* Fix overflow of title

* Show batch editing x items

* Fix edits emptying input on cancel

* Don't count locked filters in no results message

* simple batch edit title

* Fix headline being invisible

* Add no-options notice to interfaces/displays

* Use existing collection preset in browse modal

* Don't emit null on invalid hex

* Use correct titles in modal-detail

* style char remaining

* file info sidebar styling

* Another attempt at trying to make render template behave in any contetx

* Show remaining char count on focus only

* Remove fade, prevent jumping

* Render skeleton loader in correct height

* Fix o2m not fetching items

* Pass collection/field to render display in o2m

* Add no-items message in table

* Add default state to v-table

* Allow ISO8601 in datetime interface

* Title format selected icon name

* avoid blinking bg on load

* align characters remaining

* Default to tabular in browse modal

* Add disabled string

* Add center + make gray default notice

* Add disabled-no-value state

* Export getItems

* Expose refresh method on layouts

* Fix (batch) deletion from browse)

* Fix interface disabled on batch

* Add interface not found notice

* Add default label (active) for toggle interface

* Use options / prop default for toggle

* Support ISO 8601 in datetime display

* Render edit form in form width

* Fix deselecting newly selected item

* Undo all selection when closing browse modal

* Fix deselecting newly selected item

* wider divider

* update webhooks table

* Fix checkbox label disappearing

* Fix tests.. by removing them

Co-authored-by: Ben Haynes <ben@rngr.org>
This commit is contained in:
Rijk van Zanten
2020-05-15 18:44:21 -04:00
committed by GitHub
parent 8a9daf554f
commit feaafe6440
104 changed files with 2081 additions and 832 deletions

View File

@@ -153,7 +153,7 @@ body {
--v-button-background-color-activated: var(--primary);
--v-button-background-color-disabled: var(--background-normal);
--v-button-font-size: 16px;
--v-button-font-weight: 500;
--v-button-font-weight: 600;
--v-button-line-height: 22px;
--v-button-min-width: 140px;
}
@@ -168,7 +168,7 @@ body {
--v-button-color: var(--foreground-normal);
--v-button-color-hover: var(--foreground-normal);
--v-button-color-activated: var(--foreground-normal);
--v-button-background-color: var(--background-normal-alt);
--v-button-background-color: var(--border-subdued); // I'm so sorry! 🥺
--v-button-background-color-hover: var(--background-normal-alt);
--v-button-background-color-activated: var(--background-normal-alt);
}

View File

@@ -179,13 +179,17 @@ body {
position: absolute;
top: 0;
left: 0;
z-index: -1;
z-index: 0;
width: 100%;
height: 100%;
background-color: var(--background-subdued);
border-radius: var(--border-radius);
content: '';
}
> * {
z-index: 1;
}
}
&:not(:disabled):not(.indeterminate).checked {

View File

@@ -56,7 +56,13 @@ body {
}
span.wrapper {
display: flex;
margin-right: 16px;
color: var(--v-divider-label-color);
.v-icon {
margin-right: 4px;
}
}
.type-text {

View File

@@ -6,7 +6,9 @@
}"
>
<v-skeleton-loader v-if="loading && field.hideLoader !== true" />
<component
v-if="interfaceExists"
:is="`interface-${field.interface}`"
v-bind="field.options"
:disabled="disabled"
@@ -16,14 +18,20 @@
:collection="field.collection"
:field="field.field"
:primary-key="primaryKey"
:length="field.length"
@input="$emit('input', $event)"
/>
<v-notice v-else warning>
{{ $t('interface_not_found', { interface: field.interface }) }}
</v-notice>
</div>
</template>
<script lang="ts">
import { defineComponent, PropType } from '@vue/composition-api';
import { defineComponent, PropType, computed } from '@vue/composition-api';
import { Field } from '@/stores/fields/types';
import interfaces from '@/interfaces';
export default defineComponent({
props: {
@@ -56,6 +64,13 @@ export default defineComponent({
default: false,
},
},
setup(props) {
const interfaceExists = computed(() => {
return !!interfaces.find((inter) => inter.id === props.field.interface);
});
return { interfaceExists };
},
});
</script>

View File

@@ -61,7 +61,9 @@ export default defineComponent({
}
.v-checkbox {
height: 18px; // Don't push down label with normal icon height (24px)
margin-right: 4px;
transform: translateY(-2px);
}
.required {

View File

@@ -35,6 +35,7 @@
:batch-mode="batchMode"
:batch-active="batchActive"
:disabled="isDisabled"
:primary-key="primaryKey"
@input="$emit('input', $event)"
/>

View File

@@ -189,7 +189,7 @@ export default defineComponent({
const gridClass = computed<string | null>(() => {
if (el.value === null) return null;
if (width.value > 612 && width.value <= 700) {
if (width.value > 612 && width.value <= 792) {
return 'grid';
} else {
return 'grid with-fill';

View File

@@ -1,5 +1,5 @@
<template>
<div class="v-info" :class="type">
<div class="v-info" :class="[type, { center }]">
<div class="icon">
<v-icon large :name="icon" />
</div>
@@ -26,6 +26,10 @@ export default defineComponent({
type: String as PropType<'info' | 'success' | 'warning' | 'danger'>,
default: 'info',
},
center: {
type: Boolean,
default: false,
},
},
});
</script>
@@ -80,4 +84,11 @@ export default defineComponent({
margin-bottom: 32px;
}
}
.center {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
</style>

View File

@@ -2,7 +2,7 @@
<div
class="v-input"
@click="$emit('click', $event)"
:class="{ 'full-width': fullWidth, 'has-click': hasClick }"
:class="{ 'full-width': fullWidth, 'has-click': hasClick, disabled: disabled }"
>
<div v-if="$slots['prepend-outer']" class="prepend-outer">
<slot name="prepend-outer" :value="value" :disabled="disabled" />
@@ -273,6 +273,10 @@ body {
display: block;
&:hover:not(.disabled) {
--arrow-color: var(--primary);
}
&:active:not(.disabled) {
transform: scale(0.9);
}
@@ -294,7 +298,7 @@ body {
&:focus-within,
&.active {
--arrow-color: var(--primary);
--arrow-color: var(--border-normal-alt);
color: var(--foreground-normal);
background-color: var(--background-page);
@@ -354,6 +358,11 @@ body {
&.has-click {
cursor: pointer;
&.disabled {
cursor: auto;
}
input {
pointer-events: none;
.prefix,

View File

@@ -4,7 +4,7 @@
<slot name="activator" v-bind="{ on }" />
</template>
<article class="v-modal">
<article class="v-modal" :class="{ 'form-width': formWidth }">
<header class="header">
<v-icon class="menu-toggle" name="menu" @click="sidebarActive = !sidebarActive" />
<h2 class="title">{{ title }}</h2>
@@ -27,7 +27,7 @@
>
<slot name="sidebar" />
</nav>
<main class="main">
<main ref="mainEl" class="main">
<slot />
</main>
</div>
@@ -39,7 +39,7 @@
</template>
<script lang="ts">
import { defineComponent, ref, computed } from '@vue/composition-api';
import { defineComponent, ref, computed, provide } from '@vue/composition-api';
export default defineComponent({
model: {
@@ -67,11 +67,21 @@ export default defineComponent({
type: Boolean,
default: false,
},
formWidth: {
// If the modal is used to just render a form, it needs to be a little smaller to
// allow the form to be rendered in it's correct full size
type: Boolean,
default: false,
},
},
setup(props, { emit }) {
const sidebarActive = ref(false);
const localActive = ref(false);
const mainEl = ref<Element>();
provide('main-element', mainEl);
const _active = computed({
get() {
return props.active === undefined ? localActive.value : props.active;
@@ -82,11 +92,17 @@ export default defineComponent({
},
});
return { sidebarActive, _active };
return { sidebarActive, _active, mainEl };
},
});
</script>
<style>
body {
--v-modal-max-width: 916px;
}
</style>
<style lang="scss" scoped>
@import '@/styles/mixins/breakpoint';
@@ -94,7 +110,7 @@ export default defineComponent({
display: flex;
flex-direction: column;
width: calc(100% - 16px);
max-width: 916px;
max-width: var(--v-modal-max-width);
height: calc(100% - 16px);
max-height: 760px;
background-color: var(--background-page);
@@ -209,4 +225,8 @@ export default defineComponent({
height: calc(100% - 64px);
}
}
.form-width {
--v-modal-max-width: 856px;
}
</style>

View File

@@ -11,6 +11,7 @@
| `warning` | Shows the warning notice | `false` |
| `danger` | Shows the danger notice | `false` |
| `icon` | Custom icon name, or false if you want to hide the icon completely | `null` |
| `center` | Render notice content centered | `false` |
## Slots
| Slot | Description | Data |
@@ -21,8 +22,8 @@
n/a
## CSS Variables
| Variable | Default |
|-------------------------------|----------------------------|
| `--v-notice-color` | `var(--foreground-normal);` |
| `--v-notice-background-color` | `var(--primary-alt);` |
| `--v-notice-icon-color` | `var(--primary);` |
| Variable | Default |
|-------------------------------|-----------------------------|
| `--v-notice-color` | `var(--foreground-subdued)` |
| `--v-notice-background-color` | `var(--background-subdued)` |
| `--v-notice-icon-color` | `var(--foreground-subdued)` |

View File

@@ -1,5 +1,5 @@
<template>
<div class="v-notice" :class="className">
<div class="v-notice" :class="[className, { center }]">
<v-icon v-if="icon !== false" :name="iconName" left />
<slot />
</div>
@@ -10,6 +10,10 @@ import { defineComponent, computed } from '@vue/composition-api';
export default defineComponent({
props: {
info: {
type: Boolean,
default: false,
},
success: {
type: Boolean,
default: false,
@@ -26,6 +30,10 @@ export default defineComponent({
type: [String, Boolean],
default: null,
},
center: {
type: Boolean,
default: false,
},
},
setup(props) {
const iconName = computed(() => {
@@ -33,7 +41,9 @@ export default defineComponent({
return props.icon;
}
if (props.success) {
if (props.info) {
return 'info';
} else if (props.success) {
return 'check_circle';
} else if (props.warning) {
return 'warning';
@@ -45,7 +55,9 @@ export default defineComponent({
});
const className = computed<string | null>(() => {
if (props.success) {
if (props.info) {
return 'info';
} else if (props.success) {
return 'success';
} else if (props.warning) {
return 'warning';
@@ -63,9 +75,9 @@ export default defineComponent({
<style>
body {
--v-notice-color: var(--primary);
--v-notice-background-color: var(--primary-alt);
--v-notice-icon-color: var(--primary);
--v-notice-color: var(--foreground-subdued);
--v-notice-background-color: var(--background-subdued);
--v-notice-icon-color: var(--foreground-subdued);
}
</style>
@@ -85,6 +97,12 @@ body {
--v-icon-color: var(--v-notice-icon-color);
}
&.info {
--v-notice-icon-color: var(--primary);
--v-notice-background-color: var(--primary-alt);
--v-notice-color: var(--primary);
}
&.success {
--v-notice-icon-color: var(--success);
--v-notice-background-color: var(--success-alt);
@@ -102,5 +120,11 @@ body {
--v-notice-background-color: var(--danger-alt);
--v-notice-color: var(--danger);
}
&.center {
display: flex;
align-items: center;
justify-content: center;
}
}
</style>

View File

@@ -123,13 +123,16 @@ body {
position: absolute;
top: 0;
left: 0;
z-index: -1;
width: 100%;
height: 100%;
background-color: var(--background-subdued);
border-radius: var(--border-radius);
content: '';
}
.label {
z-index: 1;
}
}
&:not(:disabled):hover {

View File

@@ -1,12 +1,10 @@
<template>
<transition name="fade">
<div :class="type" class="v-skeleton-loader">
<template v-if="type === 'list-item-icon'">
<div class="icon" />
<div class="text" />
</template>
</div>
</transition>
<div :class="type" class="v-skeleton-loader">
<template v-if="type === 'list-item-icon'">
<div class="icon" />
<div class="text" />
</template>
</div>
</template>
<script lang="ts">

View File

@@ -214,8 +214,9 @@ body {
left: calc(var(--_v-slider-percentage) * 1%);
width: max-content;
padding: 4px 8px;
color: var(--foreground-normal);
background-color: var(--background-normal);
color: var(--foreground-inverted);
font-weight: 600;
background-color: var(--primary);
border-radius: var(--border-radius);
transform: translateX(-50%);
opacity: 0;
@@ -227,8 +228,8 @@ body {
left: calc(50%);
width: 10px;
height: 10px;
background-color: var(--background-normal);
border-radius: var(--border-radius);
background-color: var(--primary);
border-radius: 2px;
transform: translateX(-50%) rotate(45deg);
content: '';
}

View File

@@ -123,10 +123,12 @@ export default defineComponent({
| `selection` | What items are selected. Can be used with `v-model` as well | `[]` |
| `fixed-header` | Make the header fixed | `false` |
| `loading` | Show progress indicator | `false` |
| `loadingText` | What text to show when table is loading with no items | `Loading...` |
| `loading-text` | What text to show when table is loading with no items | `Loading...` |
| `no-items-text` | What text to show when table doesn't contain any rows | `No items` |
| `server-sort` | Handle sorting on the parent level. | `false` |
| `row-height` | Height of the individual rows in px | `48` |
| `must-sort` | Requires the sort to be on a particular column | `false` |
| `disabled` | Disable edits to items in the form (drag/select) | `false` |
## Events
| Event | Description | Value |

View File

@@ -239,7 +239,7 @@ export default defineComponent({
padding: 0 12px;
font-weight: 500;
font-size: 14px;
background-color: var(--background-page);
background-color: var(--v-table-background-color);
border-bottom: 2px solid var(--border-subdued);
&.select,

View File

@@ -113,7 +113,7 @@ export default defineComponent({
line-height: var(--table-row-line-height);
white-space: nowrap;
text-overflow: ellipsis;
background-color: var(--background-page);
background-color: var(--v-table-background-color);
border-bottom: 2px solid var(--border-subdued);
&:last-child {

View File

@@ -1,5 +1,5 @@
<template>
<div class="v-table" :class="{ loading, inline }">
<div class="v-table" :class="{ loading, inline, disabled }">
<table
:summary="_headers.map((header) => header.text).join(', ')"
:style="{
@@ -25,13 +25,18 @@
</template>
</table-header>
<thead v-if="loading" class="loading-indicator" :class="{ sticky: fixedHeader }">
<th scope="colgroup" :style="{ gridColumn: loadingColSpan }">
<th scope="colgroup" :style="{ gridColumn: fullColSpan }">
<v-progress-linear indeterminate v-if="loading" />
</th>
</thead>
<tbody v-if="loading && items.length === 0">
<tr class="loading-text">
<td :style="{ gridColumn: loadingColSpan }">{{ loadingText }}</td>
<td :style="{ gridColumn: fullColSpan }">{{ loadingText }}</td>
</tr>
</tbody>
<tbody v-if="!loading && items.length === 0">
<tr class="no-items-text">
<td :style="{ gridColumn: fullColSpan }">{{ noItemsText }}</td>
</tr>
</tbody>
<draggable
@@ -39,20 +44,21 @@
v-model="_items"
tag="tbody"
handle=".drag-handle"
:disabled="_sort.by !== manualSortKey"
:disabled="disabled || _sort.by !== manualSortKey"
@change="onSortChange"
:set-data="hideDragImage"
>
<table-row
v-for="item in _items"
:key="item[itemKey]"
:headers="_headers"
:item="item"
:show-select="showSelect"
:show-manual-sort="showManualSort"
:show-select="!disabled && showSelect"
:show-manual-sort="!disabled && showManualSort"
:is-selected="getSelectedState(item)"
:subdued="loading"
:sorted-manually="_sort.by === manualSortKey"
:has-click-listener="hasRowClick"
:has-click-listener="!disabled && hasRowClick"
:height="rowHeight"
@click="hasRowClick ? $emit('click:row', item) : null"
@item-selected="onItemSelected"
@@ -79,6 +85,7 @@ import TableRow from './table-row/';
import { sortBy, clone, forEach, pick } from 'lodash';
import { i18n } from '@/lang/';
import draggable from 'vuedraggable';
import hideDragImage from '@/utils/hide-drag-image';
const HeaderDefaults: Header = {
text: '',
@@ -151,6 +158,10 @@ export default defineComponent({
type: String,
default: i18n.t('loading'),
},
noItemsText: {
type: String,
default: i18n.t('no_items'),
},
serverSort: {
type: Boolean,
default: false,
@@ -167,6 +178,10 @@ export default defineComponent({
type: Boolean,
default: false,
},
disabled: {
type: Boolean,
default: false,
},
},
setup(props, { emit, listeners, slots }) {
const _headers = computed({
@@ -223,7 +238,7 @@ export default defineComponent({
const hasItemAppendSlot = computed(() => slots['item-append'] !== undefined);
const loadingColSpan = computed<string>(() => {
const fullColSpan = computed<string>(() => {
let length = _headers.value.length + 1; // +1 account for spacer
if (props.showSelect) length++;
if (props.showManualSort) length++;
@@ -287,12 +302,15 @@ export default defineComponent({
someItemsSelected,
onSortChange,
hasRowClick,
loadingColSpan,
fullColSpan,
columnStyle,
hasItemAppendSlot,
hideDragImage,
};
function onItemSelected(event: ItemSelectEvent) {
if (props.disabled) return;
emit('item-selected', event);
let selection = clone(props.selection) as any[];
@@ -324,6 +342,8 @@ export default defineComponent({
}
function onToggleSelectAll(value: boolean) {
if (props.disabled) return;
if (value === true) {
if (props.selectionUseKeys) {
emit(
@@ -348,6 +368,8 @@ export default defineComponent({
}
function onSortChange(event: VueDraggableChangeEvent) {
if (props.disabled) return;
if (event.moved) {
emit('manual-sort', {
item: event.moved.element,
@@ -364,6 +386,8 @@ export default defineComponent({
body {
--v-table-height: auto;
--v-table-sticky-offset-top: 0;
--v-table-color: var(--foreground-normal);
--v-table-background-color: var(--background-page);
}
</style>
@@ -399,7 +423,7 @@ body {
td,
th {
color: var(--foreground-normal);
color: var(--v-table-color);
&.align-left {
text-align: left;
@@ -452,20 +476,30 @@ body {
z-index: 2;
}
}
}
.loading-text {
text-align: center;
.loading-text,
.no-items-text {
text-align: center;
td {
padding: 16px;
color: var(--foreground-subdued);
}
td {
padding: 16px;
color: var(--foreground-subdued);
}
}
&.inline {
border: 2px solid var(--border-normal);
border-radius: var(--border-radius);
table ::v-deep .table-row:last-of-type .cell {
border-bottom: none;
}
}
}
.inline {
border: 2px solid var(--border-normal);
border-radius: var(--border-radius);
.disabled {
--v-table-color: var(--foreground-subdued);
--v-table-background-color: var(--background-subdued);
}
</style>

View File

@@ -128,23 +128,22 @@ body {
}
}
&:hover {
&.full-width {
width: 100%;
}
&:hover:not(.disabled) {
border-color: var(--border-normal-alt);
}
&:focus,
&:focus-within {
&:focus:not(.disabled),
&:focus-within:not(.disabled) {
border-color: var(--primary);
}
&.full-width {
width: 100%;
}
&.disabled {
color: var(--foreground-subdued);
background-color: var(--background-subdued);
cursor: not-allowed;
}
textarea {