Files
directus/app/src/components/v-checkbox.vue
Rijk van Zanten 2983e61870 The Great TypeScript Modernization Program Season 3 Episode 6: The Big One (#18014)
* Step 1

* Step 2

* False sense of confidence

* Couple more before dinner

* Update schema package

* Update format-title

* Upgrade specs file

* Close

* Replace ts-node-dev with tsx, and various others

* Replace lodash with lodash-es

* Add lodash-es types

* Update knex import

* More fun is had

* FSE

* Consolidate repos

* Various tweaks and fixes

* Fix specs

* Remove dependency on knex-schema-inspector

* Fix wrong imports of inspector

* Move shared exceptions to new package

* Move constants to separate module

* Move types to new types package

* Use directus/types

* I believe this is no longer needed

* [WIP] Start moving utils to esm

* ESMify Shared

* Move shared utils to  @directus/utils

* Use @directus/utils instead of @directus/shared/utils

* It runs!

* Use correct schemaoverview type

* Fix imports

* Fix the thing

* Start on new update-checker lib

* Use new update-check package

* Swap out directus/shared in app

* Pushing through the last bits now

* Dangerously make extensions SDK ESM

* Use @directus/types in tests

* Copy util function to test

* Fix linter config

* Add missing import

* Hot takes

* Fix build

* Curse these default exports

* No tests in constants

* Add tests

* Remove tests from types

* Add tests for exceptions

* Fix test

* Fix app tests

* Fix import in test

* Fix various tests

* Fix specs export

* Some more tests

* Remove broken integration tests

These were broken beyond repair.. They were also written before we really knew what we we're doing with tests, so I think it's better to say goodbye and start over with these

* Regenerate lockfile

* Fix imports from merge

* I create my own problems

* Make sharp play nice

* Add vitest config

* Install missing blackbox dep

* Consts shouldn't be in types

tsk tsk tsk tsk

* Fix type/const usage in extensions-sdk

* cursed.default

* Reduce circular deps

* Fix circular dep in items service

* vvv

* Trigger testing for all vendors

* Add workaround for rollup

* Prepend the file protocol for the ESM loader to be compatible with Windows
"WARN: Only URLs with a scheme in: file and data are supported by the default ESM loader. On Windows, absolute paths must be valid file:// URLs. Received protocol 'c:'"

* Fix postgres

* Schema package updates

Co-authored-by: Azri Kahar <42867097+azrikahar@users.noreply.github.com>

* Resolve cjs/mjs extensions

* Clean-up eslint config

* fixed extension concatination

* using string interpolation for consistency

* Revert MySQL optimisation

* Revert testing for all vendors

* Replace tsx with esbuild-kit/esm-loader

Is a bit faster and we can rely on the built-in `watch` and `inspect`
functionalities of Node.js

Note: The possibility to watch other files (.env in our case) might be
added in the future, see https://github.com/nodejs/node/issues/45467

* Use exact version for esbuild-kit/esm-loader

* Fix import

---------

Co-authored-by: ian <licitdev@gmail.com>
Co-authored-by: Brainslug <tim@brainslug.nl>
Co-authored-by: Azri Kahar <42867097+azrikahar@users.noreply.github.com>
Co-authored-by: Pascal Jufer <pascal-jufer@bluewin.ch>
2023-04-04 17:41:56 -04:00

241 lines
5.2 KiB
Vue

<template>
<component
:is="customValue ? 'div' : 'button'"
class="v-checkbox"
type="button"
role="checkbox"
:aria-pressed="isChecked ? 'true' : 'false'"
:disabled="disabled"
:class="{ checked: isChecked, indeterminate, block }"
@click.stop="toggleInput"
>
<div v-if="$slots.prepend" class="prepend"><slot name="prepend" /></div>
<v-icon class="checkbox" :name="icon" :disabled="disabled" />
<span class="label type-text">
<slot v-if="customValue === false">{{ label }}</slot>
<input v-else v-model="internalValue" class="custom-input" />
</span>
<div v-if="$slots.append" class="append"><slot name="append" /></div>
</component>
</template>
<script setup lang="ts">
import { computed } from 'vue';
import { useSync } from '@directus/composables';
interface Props {
/** If the `modelValue` is an array of strings, activates the checkbox if the value is inside it */
value?: string | null;
/** Used to model the active state */
modelValue?: boolean | string[] | null;
/** Label for the checkbox */
label?: string | null;
/** Disable the checkbox */
disabled?: boolean;
/** Renders the checkbox neither selected nor unselected */
indeterminate?: boolean;
/** What icon to use for the on state */
iconOn?: string;
/** What icon to use for the off state */
iconOff?: string;
/** What icon to use for the indeterminate state */
iconIndeterminate?: string;
/** Show as styled block. Matches input size */
block?: boolean;
/** If a custom value can be entered next to it */
customValue?: boolean;
/** TODO: What the? */
checked?: boolean | null;
}
const props = withDefaults(defineProps<Props>(), {
value: null,
modelValue: null,
label: null,
disabled: false,
indeterminate: false,
iconOn: 'check_box',
iconOff: 'check_box_outline_blank',
iconIndeterminate: 'indeterminate_check_box',
block: false,
customValue: false,
checked: null,
});
const emit = defineEmits(['update:indeterminate', 'update:modelValue', 'update:value']);
const internalValue = useSync(props, 'value', emit);
const isChecked = computed<boolean>(() => {
if (props.checked !== null) return props.checked;
if (props.modelValue instanceof Array) {
if (!props.value) return false;
return props.modelValue.includes(props.value);
}
return props.modelValue === true;
});
const icon = computed<string>(() => {
if (props.indeterminate === true) return props.iconIndeterminate;
if (props.checked === null && props.modelValue === null) return props.iconIndeterminate;
return isChecked.value ? props.iconOn : props.iconOff;
});
function toggleInput(): void {
if (props.disabled) return;
if (props.indeterminate === true) {
emit('update:indeterminate', false);
}
if (props.modelValue instanceof Array && props.value) {
const newValue = [...props.modelValue];
if (props.modelValue.includes(props.value) === false) {
newValue.push(props.value);
} else {
newValue.splice(newValue.indexOf(props.value), 1);
}
emit('update:modelValue', newValue);
} else {
emit('update:modelValue', !props.modelValue);
}
}
</script>
<style>
body {
--v-checkbox-color: var(--primary);
--v-checkbox-unchecked-color: var(--foreground-subdued);
}
</style>
<style lang="scss" scoped>
@import '@/styles/mixins/no-wrap';
.v-checkbox {
--v-icon-color: var(--v-checkbox-unchecked-color);
--v-icon-color-hover: var(--primary);
position: relative;
display: flex;
align-items: center;
font-size: 0;
text-align: left;
background-color: transparent;
border: none;
border-radius: 0;
appearance: none;
.label:not(:empty) {
flex-grow: 1;
margin-left: 8px;
transition: color var(--fast) var(--transition);
input {
width: 100%;
background-color: transparent;
border: none;
border-bottom: 2px solid var(--border-normal);
border-radius: 0;
}
@include no-wrap;
}
& .checkbox {
--v-icon-color: var(--v-checkbox-unchecked-color);
transition: color var(--fast) var(--transition);
}
&:disabled {
cursor: not-allowed;
.label {
color: var(--foreground-subdued);
}
.checkbox {
--v-icon-color: var(--foreground-subdued);
}
}
&.block {
position: relative;
width: 100%;
height: var(--input-height);
padding: 10px; // 14 - 4 (border)
background-color: var(--background-page);
border: var(--border-width) solid var(--border-normal);
border-radius: var(--border-radius);
transition: all var(--fast) var(--transition);
&:disabled {
background-color: var(--background-subdued);
}
&::before {
position: absolute;
top: 0;
left: 0;
z-index: 0;
width: 100%;
height: 100%;
border-radius: var(--border-radius);
content: '';
}
> * {
z-index: 1;
}
}
&:not(:disabled):hover {
.checkbox {
--v-icon-color: var(--primary);
}
&.block {
background-color: var(--background-subdued);
border-color: var(--border-normal-alt);
}
}
&:not(:disabled):not(.indeterminate) {
.label {
color: var(--foreground-normal);
}
&.block {
&::before {
opacity: 0.1;
}
}
}
&:not(:disabled):not(.indeterminate).checked {
.checkbox {
--v-icon-color: var(--v-checkbox-color);
}
&.block {
.label {
color: var(--v-checkbox-color);
}
}
}
.prepend,
.append {
display: contents;
font-size: 1rem;
}
}
</style>