mirror of
https://github.com/directus/directus.git
synced 2026-02-19 03:44:20 -05:00
Merge pull request #272 from directus/show-as-dot
Move color-dot option to labels
This commit is contained in:
31
app/package-lock.json
generated
31
app/package-lock.json
generated
@@ -3042,11 +3042,32 @@
|
||||
"@types/tern": "*"
|
||||
}
|
||||
},
|
||||
"@types/color": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/color/-/color-3.0.1.tgz",
|
||||
"integrity": "sha512-oeUWVaAwI+xINDUx+3F2vJkl/vVB03VChFF/Gl3iQCdbcakjuoJyMOba+3BXRtnBhxZ7uBYqQBi9EpLnvSoztA==",
|
||||
"requires": {
|
||||
"@types/color-convert": "*"
|
||||
}
|
||||
},
|
||||
"@types/color-convert": {
|
||||
"version": "1.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/color-convert/-/color-convert-1.9.0.tgz",
|
||||
"integrity": "sha512-OKGEfULrvSL2VRbkl/gnjjgbbF7ycIlpSsX7Nkab4MOWi5XxmgBYvuiQ7lcCFY5cPDz7MUNaKgxte2VRmtr4Fg==",
|
||||
"requires": {
|
||||
"@types/color-name": "*"
|
||||
}
|
||||
},
|
||||
"@types/color-name": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
|
||||
"integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ=="
|
||||
},
|
||||
"@types/color-string": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/color-string/-/color-string-1.5.0.tgz",
|
||||
"integrity": "sha512-17/8LWbkfvoKgfnnBvKbUnPTzPtzBlSELf5FPvVt9dRTQW88igOYZZ78us9dmbY1301VtTBEnuPTKspvmtGDxA=="
|
||||
},
|
||||
"@types/connect": {
|
||||
"version": "3.4.33",
|
||||
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.33.tgz",
|
||||
@@ -8533,7 +8554,6 @@
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/color/-/color-3.1.2.tgz",
|
||||
"integrity": "sha512-vXTJhHebByxZn3lDvDJYw4lR5+uB3vuoHsuYA5AKuxRVn5wzzIfQKGLBmgdVRHKTJYeK5rvJcHnrd0Li49CFpg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-convert": "^1.9.1",
|
||||
"color-string": "^1.5.2"
|
||||
@@ -8543,7 +8563,6 @@
|
||||
"version": "1.9.3",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
||||
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-name": "1.1.3"
|
||||
}
|
||||
@@ -8551,8 +8570,7 @@
|
||||
"color-name": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
|
||||
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
|
||||
"dev": true
|
||||
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -8573,7 +8591,6 @@
|
||||
"version": "1.5.3",
|
||||
"resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz",
|
||||
"integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-name": "^1.0.0",
|
||||
"simple-swizzle": "^0.2.2"
|
||||
@@ -23099,7 +23116,6 @@
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
|
||||
"integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-arrayish": "^0.3.1"
|
||||
},
|
||||
@@ -23107,8 +23123,7 @@
|
||||
"is-arrayish": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
|
||||
"integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==",
|
||||
"dev": true
|
||||
"integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -35,6 +35,8 @@
|
||||
"@sindresorhus/slugify": "^1.0.0",
|
||||
"@tinymce/tinymce-vue": "^3.2.2",
|
||||
"@types/codemirror": "^0.0.97",
|
||||
"@types/color": "^3.0.1",
|
||||
"@types/color-string": "^1.5.0",
|
||||
"@types/debug": "^4.1.5",
|
||||
"@types/htmlhint": "^0.9.1",
|
||||
"@types/js-yaml": "^3.12.5",
|
||||
@@ -45,6 +47,8 @@
|
||||
"base-64": "^0.1.0",
|
||||
"bytes": "^3.1.0",
|
||||
"codemirror": "^5.56.0",
|
||||
"color": "^3.1.2",
|
||||
"color-string": "^1.5.3",
|
||||
"cropperjs": "^1.5.7",
|
||||
"csslint": "^1.0.5",
|
||||
"date-fns": "^2.14.0",
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
<template>
|
||||
<div class="color-dot">
|
||||
<value-null v-if="value === null" />
|
||||
<div class="dot" :style="styles" v-tooltip="displayValue"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, computed, PropType } from '@vue/composition-api';
|
||||
import formatTitle from '@directus/format-title';
|
||||
import { isHex } from '@/utils/color';
|
||||
|
||||
type Choice = {
|
||||
value: string;
|
||||
text: string;
|
||||
color: string | null;
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
choices: {
|
||||
type: Array as PropType<Choice[]>,
|
||||
default: () => [],
|
||||
},
|
||||
defaultColor: {
|
||||
type: String,
|
||||
default: '#B0BEC5',
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const currentChoice = computed(() => {
|
||||
return props.choices.find((choice) => {
|
||||
return choice.value === props.value;
|
||||
});
|
||||
});
|
||||
|
||||
const displayValue = computed(() => {
|
||||
return formatTitle(props.value);
|
||||
});
|
||||
|
||||
const styles = computed(() => {
|
||||
if (isHex(props.value)) {
|
||||
return { backgroundColor: props.value || props.defaultColor };
|
||||
}
|
||||
|
||||
return {
|
||||
backgroundColor: currentChoice.value?.color || props.defaultColor,
|
||||
};
|
||||
});
|
||||
|
||||
return { displayValue, styles, isHex };
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.color-dot {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.dot {
|
||||
display: inline-block;
|
||||
flex-shrink: 0;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
margin: 0 4px;
|
||||
border-radius: 6px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,60 +0,0 @@
|
||||
import { defineDisplay } from '@/displays/define';
|
||||
import DisplayColorDot from './color-dot.vue';
|
||||
|
||||
export default defineDisplay(({ i18n }) => ({
|
||||
id: 'color-dot',
|
||||
name: i18n.t('displays.color-dot.color-dot'),
|
||||
description: i18n.t('displays.color-dot.description'),
|
||||
types: ['string'],
|
||||
icon: 'flag',
|
||||
handler: DisplayColorDot,
|
||||
options: [
|
||||
{
|
||||
field: 'defaultColor',
|
||||
name: i18n.t('displays.color-dot.default_color'),
|
||||
type: 'string',
|
||||
meta: {
|
||||
interface: 'color',
|
||||
width: 'half',
|
||||
},
|
||||
schema: {
|
||||
default_value: '#B0BEC5',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'choices',
|
||||
name: i18n.t('choices'),
|
||||
type: 'json',
|
||||
meta: {
|
||||
note: i18n.t('displays.color-dot.choices_note'),
|
||||
interface: 'repeater',
|
||||
options: {
|
||||
template: '{{text}}',
|
||||
fields: [
|
||||
{
|
||||
field: 'value',
|
||||
name: i18n.t('value'),
|
||||
type: 'string',
|
||||
meta: {
|
||||
interface: 'text-input',
|
||||
options: {
|
||||
font: 'monospace',
|
||||
},
|
||||
width: 'half',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'color',
|
||||
name: i18n.t('color'),
|
||||
type: 'string',
|
||||
meta: {
|
||||
interface: 'color',
|
||||
width: 'half',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
}));
|
||||
68
app/src/displays/color/color.vue
Normal file
68
app/src/displays/color/color.vue
Normal file
@@ -0,0 +1,68 @@
|
||||
<template>
|
||||
<div class="color-dot">
|
||||
<value-null v-if="value === null" />
|
||||
<div class="dot" :style="styles"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, computed, PropType } from '@vue/composition-api';
|
||||
import formatTitle from '@directus/format-title';
|
||||
import Color from 'color';
|
||||
import colorString from 'color-string';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
defaultColor: {
|
||||
type: String,
|
||||
default: '#B0BEC5',
|
||||
validator: (value: string) => colorString.get.rgb(value) !== null,
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const displayValue = computed(() => {
|
||||
return props.value;
|
||||
});
|
||||
|
||||
const styles = computed(() => {
|
||||
const style: Record<string, any> = { 'background-color': props.defaultColor };
|
||||
|
||||
if (Color(props.value) !== undefined) style['background-color'] = props.value;
|
||||
|
||||
const pageColorString = getComputedStyle(document.body).getPropertyValue('--background-page');
|
||||
|
||||
const pageColorRGB = colorString.get.rgb(pageColorString) || colorString.get.rgb('#FFF');
|
||||
const colorRGB = colorString.get.rgb(props.value) || colorString.get.rgb(props.defaultColor);
|
||||
|
||||
if (pageColorRGB == null || colorRGB == null) return {};
|
||||
|
||||
if (Color.rgb(...colorRGB.slice(0.3)).contrast(Color.rgb(...pageColorRGB.slice(0, 3))) < 3)
|
||||
style['border'] = '1px solid var(--border-normal-alt)';
|
||||
|
||||
return style;
|
||||
});
|
||||
|
||||
return { displayValue, styles };
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.color-dot {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.dot {
|
||||
display: inline-block;
|
||||
flex-shrink: 0;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
margin: 0 4px;
|
||||
border-radius: 6px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
25
app/src/displays/color/index.ts
Normal file
25
app/src/displays/color/index.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { defineDisplay } from '@/displays/define';
|
||||
import DisplayColor from './color.vue';
|
||||
|
||||
export default defineDisplay(({ i18n }) => ({
|
||||
id: 'color',
|
||||
name: i18n.t('displays.color.color'),
|
||||
description: i18n.t('displays.color.description'),
|
||||
types: ['string'],
|
||||
icon: 'flag',
|
||||
handler: DisplayColor,
|
||||
options: [
|
||||
{
|
||||
field: 'defaultColor',
|
||||
name: i18n.t('displays.color.default_color'),
|
||||
type: 'string',
|
||||
meta: {
|
||||
interface: 'color',
|
||||
width: 'half',
|
||||
},
|
||||
schema: {
|
||||
default_value: '#B0BEC5',
|
||||
},
|
||||
},
|
||||
],
|
||||
}));
|
||||
@@ -38,7 +38,7 @@ export default defineDisplay(({ i18n }) => ({
|
||||
name: i18n.t('format_text'),
|
||||
type: 'boolean',
|
||||
meta: {
|
||||
width: 'half-left',
|
||||
width: 'half',
|
||||
interface: 'toggle',
|
||||
options: {
|
||||
label: i18n.t('displays.labels.format_label'),
|
||||
@@ -48,6 +48,18 @@ export default defineDisplay(({ i18n }) => ({
|
||||
default_value: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'showAsDot',
|
||||
name: i18n.t('displays.labels.show_as_dot'),
|
||||
type: 'boolean',
|
||||
meta: {
|
||||
width: 'half',
|
||||
interface: 'toggle',
|
||||
},
|
||||
schema: {
|
||||
default_value: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'choices',
|
||||
name: i18n.t('choices'),
|
||||
|
||||
@@ -1,18 +1,29 @@
|
||||
<template>
|
||||
<div class="display-tags">
|
||||
<v-chip
|
||||
v-for="item in items"
|
||||
:key="item.value"
|
||||
:style="{
|
||||
'--v-chip-color': item.foreground,
|
||||
'--v-chip-background-color': item.background,
|
||||
}"
|
||||
small
|
||||
disabled
|
||||
label
|
||||
>
|
||||
{{ item.text }}
|
||||
</v-chip>
|
||||
<template v-if="!showAsDot">
|
||||
<v-chip
|
||||
v-for="item in items"
|
||||
:key="item.value"
|
||||
:style="{
|
||||
'--v-chip-color': item.foreground,
|
||||
'--v-chip-background-color': item.background,
|
||||
}"
|
||||
small
|
||||
disabled
|
||||
label
|
||||
>
|
||||
{{ item.text }}
|
||||
</v-chip>
|
||||
</template>
|
||||
<template v-else>
|
||||
<display-color
|
||||
v-for="item in items"
|
||||
:key="item.value"
|
||||
:value="item.background"
|
||||
:default-color="defaultBackground"
|
||||
v-tooltip="item.text"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -37,6 +48,10 @@ export default defineComponent({
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
showAsDot: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
choices: {
|
||||
type: Array as PropType<Choice[]>,
|
||||
default: () => [],
|
||||
@@ -51,7 +66,7 @@ export default defineComponent({
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
validator: (val: string) => ['json', 'string'].includes(val),
|
||||
validator: (val: string) => ['text', 'string'].includes(val),
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
@@ -90,7 +105,7 @@ export default defineComponent({
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.display-tags {
|
||||
display: inline-block;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.v-chip + .v-chip {
|
||||
|
||||
@@ -90,6 +90,10 @@
|
||||
"name": "Name",
|
||||
"both": "Both",
|
||||
"circle_label": "Show user in a circle"
|
||||
},
|
||||
"labels": {
|
||||
"labels": "Labels",
|
||||
"show_as_dot": "Show as dot"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
--background-highlight: #F9FAFB; // ADJUST
|
||||
--background-page: #263238;
|
||||
--background-page-rgb: 38, 50, 56;
|
||||
--background-inverted: #fff;
|
||||
--background-inverted: #FFF;
|
||||
|
||||
--overlay-color: rgba(19, 24, 26, 0.9);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user