mirror of
https://github.com/directus/directus.git
synced 2026-01-28 00:57:55 -05:00
Textarea updates (#387)
* textarea updates with placeholder, font, and trim * added wrapper div * test fix * font in css var * Rename readonly to disabled, remove unused div * Remove unused prop * Fix code smell Co-authored-by: rijkvanzanten <rijkvanzanten@me.com>
This commit is contained in:
@@ -11,35 +11,37 @@ The HTML `<textarea>` element supports a huge amount of attributes and events. I
|
||||
## Props
|
||||
|
||||
| Prop | Description | Default |
|
||||
|-------------------|--------------------------------------------------------------------------------|---------|
|
||||
| ----------------- | ------------------------------------------------------------------------------ | ------- |
|
||||
| `placeholder` | Text to show when no input is entered | `null` |
|
||||
| `autofocus` | Autofocusses the input on render | `false` |
|
||||
| `disabled` | Set the disabled state for the input | `false` |
|
||||
| `monospace` | Render the entered value in the monospace font | `false` |
|
||||
| `full-width` | Render the input with 100% width | `false` |
|
||||
| `value` | Current value. Syncs with `v-model` | |
|
||||
| `expand-on-focus` | Renders the textarea at regular input size, and expands to max-height on focus | `false` |
|
||||
| `trim` | Trim leading and trailing whitespace | `true` |
|
||||
|
||||
Note: all other attached attributes are bound to the input HTMLELement in the component. This allows you to attach any of the standard HTML attributes like `min`, `length`, or `pattern`.
|
||||
|
||||
## Slots
|
||||
|
||||
| Slot | Description | Data |
|
||||
|-----------|----------------------------------------------------------|------|
|
||||
| --------- | -------------------------------------------------------- | ---- |
|
||||
| `prepend` | Prepend elements before the text content in the textarea | |
|
||||
| `append` | Append elements after the text content | |
|
||||
|
||||
## Events
|
||||
|
||||
| Events | Description | Value |
|
||||
|---------|-------------------|-------|
|
||||
| ------- | ----------------- | ----- |
|
||||
| `input` | Updates `v-model` | `any` |
|
||||
|
||||
Note: all other listeners are bound to the input HTMLElement, allowing you to handle everything from `keydown` to `emptied`.
|
||||
|
||||
## CSS Variables
|
||||
|
||||
| Variable | Default |
|
||||
|---------------------------|----------------------------|
|
||||
| `--v-textarea-min-height` | `none` |
|
||||
| `--v-textarea-max-height` | `var(--input-height-tall)` |
|
||||
| `--v-textarea-height` | `var(--input-height-tall)` |
|
||||
| Variable | Default |
|
||||
| -------------------------- | -------------------------- |
|
||||
| `--v-textarea-min-height` | `none` |
|
||||
| `--v-textarea-max-height` | `var(--input-height-tall)` |
|
||||
| `--v-textarea-height` | `var(--input-height-tall)` |
|
||||
| `--v-textarea-font-family` | `var(--family-sans-serif)` |
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { withKnobs, boolean } from '@storybook/addon-knobs';
|
||||
import { withKnobs, boolean, text } from '@storybook/addon-knobs';
|
||||
import Vue from 'vue';
|
||||
import readme from './readme.md';
|
||||
import withPadding from '../../../.storybook/decorators/with-padding';
|
||||
@@ -22,8 +22,11 @@ export const basic = () =>
|
||||
disabled: {
|
||||
default: boolean('Disabled', false),
|
||||
},
|
||||
monospace: {
|
||||
default: boolean('Monospace', false),
|
||||
trim: {
|
||||
default: boolean('Trim', false),
|
||||
},
|
||||
placeholder: {
|
||||
default: text('Placeholder', 'Enter a longer value.....'),
|
||||
},
|
||||
fullWidth: {
|
||||
default: boolean('Full Width', false),
|
||||
@@ -44,9 +47,8 @@ export const basic = () =>
|
||||
v-model="value"
|
||||
:disabled="disabled"
|
||||
:full-width="fullWidth"
|
||||
:monospace="monospace"
|
||||
v-bind="{placeholder, trim}"
|
||||
:expand-on-focus="expandOnFocus"
|
||||
placeholder="Enter a value..."
|
||||
/>
|
||||
<raw-value>{{ value }}</raw-value>
|
||||
</div>
|
||||
|
||||
@@ -17,7 +17,6 @@ describe('Textarea', () => {
|
||||
it('Sets the correct classes based on props', async () => {
|
||||
component.setProps({
|
||||
disabled: true,
|
||||
monospace: true,
|
||||
});
|
||||
|
||||
await component.vm.$nextTick();
|
||||
|
||||
@@ -8,15 +8,15 @@
|
||||
'has-content': hasContent,
|
||||
}"
|
||||
>
|
||||
<div class="prepend" v-if="$scopedSlots.prepend"><slot name="prepend" /></div>
|
||||
<textarea
|
||||
v-bind="$attrs"
|
||||
v-focus="autofocus"
|
||||
v-on="_listeners"
|
||||
:class="{ monospace }"
|
||||
:placeholder="placeholder"
|
||||
:disabled="disabled"
|
||||
:value="value"
|
||||
/>
|
||||
<div class="prepend" v-if="$scopedSlots.prepend"><slot name="prepend" /></div>
|
||||
<div class="append" v-if="$scopedSlots.append"><slot name="append" /></div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -34,10 +34,6 @@ export default defineComponent({
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
monospace: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
fullWidth: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
@@ -50,6 +46,14 @@ export default defineComponent({
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
trim: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
},
|
||||
setup(props, { emit, listeners }) {
|
||||
const _listeners = computed(() => ({
|
||||
@@ -62,7 +66,11 @@ export default defineComponent({
|
||||
return { _listeners, hasContent };
|
||||
|
||||
function emitValue(event: InputEvent) {
|
||||
emit('input', (event.target as HTMLInputElement).value);
|
||||
let value = (event.target as HTMLInputElement).value;
|
||||
if (props.trim === true) {
|
||||
value = value.trim();
|
||||
}
|
||||
emit('input', value);
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -73,6 +81,7 @@ export default defineComponent({
|
||||
--v-textarea-min-height: none;
|
||||
--v-textarea-max-height: var(--input-height-tall);
|
||||
--v-textarea-height: var(--input-height-tall);
|
||||
--v-textarea-font-family: var(--family-sans-serif);
|
||||
|
||||
position: relative;
|
||||
display: flex;
|
||||
@@ -141,6 +150,7 @@ export default defineComponent({
|
||||
height: var(--input-height);
|
||||
padding: var(--input-padding);
|
||||
color: var(--foreground-normal);
|
||||
font-family: var(--v-textarea-font-family);
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
resize: none;
|
||||
@@ -148,10 +158,6 @@ export default defineComponent({
|
||||
&::placeholder {
|
||||
color: var(--foreground-subdued);
|
||||
}
|
||||
|
||||
&.monospace {
|
||||
font-family: var(--family-monospace);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -57,15 +57,15 @@ export default defineComponent({
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.v-input {
|
||||
.monospace {
|
||||
&.monospace {
|
||||
--v-input-font-family: var(--family-monospace);
|
||||
}
|
||||
|
||||
.serif {
|
||||
&.serif {
|
||||
--v-input-font-family: var(--family-serif);
|
||||
}
|
||||
|
||||
.sans-serif {
|
||||
&.sans-serif {
|
||||
--v-input-font-family: var(--family-sans-serif);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,12 +7,6 @@ export default defineInterface(({ i18n }) => ({
|
||||
icon: 'box',
|
||||
component: InterfaceTextarea,
|
||||
options: [
|
||||
{
|
||||
field: 'monospace',
|
||||
name: 'Monospace',
|
||||
width: 'half',
|
||||
interface: 'switch',
|
||||
},
|
||||
{
|
||||
field: 'placeholder',
|
||||
name: 'Placeholder',
|
||||
@@ -20,15 +14,23 @@ export default defineInterface(({ i18n }) => ({
|
||||
interface: 'text-input',
|
||||
},
|
||||
{
|
||||
field: 'rows',
|
||||
name: 'Rows',
|
||||
field: 'trim',
|
||||
name: 'Trim',
|
||||
width: 'half',
|
||||
interface: 'numeric',
|
||||
interface: 'switch',
|
||||
},
|
||||
{
|
||||
field: 'font',
|
||||
name: 'Font',
|
||||
width: 'half',
|
||||
interface: 'dropdown',
|
||||
options: {
|
||||
min: 5,
|
||||
max: 100,
|
||||
items: [
|
||||
{ itemText: 'Sans', itemValue: 'sans-serif' },
|
||||
{ itemText: 'Mono', itemValue: 'monospace' },
|
||||
{ itemText: 'Serif', itemValue: 'serif' },
|
||||
],
|
||||
},
|
||||
default: 8,
|
||||
},
|
||||
],
|
||||
}));
|
||||
|
||||
@@ -1 +1,10 @@
|
||||
# Textarea
|
||||
|
||||
## Options
|
||||
|
||||
| Option | Description | Default |
|
||||
| ------------- | ------------------------------------------------------------------- | ------------ |
|
||||
| `placeholder` | Text to show when no input is entered | `null` |
|
||||
| `trim` | Trim leading and trailing whitespace | `true` |
|
||||
| `font` | Font to render the value in (`sans-serif`, `serif`, or `monospace`) | `sans-serif` |
|
||||
| `readonly` | Readonly | `false` |
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { withKnobs, boolean, text } from '@storybook/addon-knobs';
|
||||
import { withKnobs, boolean, text, optionsKnob } from '@storybook/addon-knobs';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import Vue from 'vue';
|
||||
import InterfaceTextarea from './textarea.vue';
|
||||
import markdown from './readme.md';
|
||||
import withPadding from '../../../.storybook/decorators/with-padding';
|
||||
import { defineComponent } from '@vue/composition-api';
|
||||
import { defineComponent, ref } from '@vue/composition-api';
|
||||
import RawValue from '../../../.storybook/raw-value.vue';
|
||||
|
||||
Vue.component('interface-textarea', InterfaceTextarea);
|
||||
|
||||
@@ -18,22 +19,40 @@ export default {
|
||||
|
||||
export const basic = () =>
|
||||
defineComponent({
|
||||
components: { RawValue },
|
||||
props: {
|
||||
monospace: {
|
||||
default: boolean('Monospace', false, 'Options'),
|
||||
disabled: {
|
||||
default: boolean('Disabled', false, 'Options'),
|
||||
},
|
||||
placeholder: {
|
||||
default: text('Placeholder', 'Enter a value...', 'Options'),
|
||||
},
|
||||
trim: {
|
||||
default: boolean('Trim', false, 'Options'),
|
||||
},
|
||||
font: {
|
||||
default: optionsKnob(
|
||||
'Font',
|
||||
{ Sans: 'sans-serif', Serif: 'serif', Mono: 'monospace' },
|
||||
'sans',
|
||||
{ display: 'select' },
|
||||
'Options'
|
||||
),
|
||||
},
|
||||
},
|
||||
setup() {
|
||||
const value = ref('');
|
||||
const onInput = action('input');
|
||||
return { onInput };
|
||||
return { onInput, value };
|
||||
},
|
||||
template: `
|
||||
<div>
|
||||
<interface-textarea
|
||||
v-bind="{ monospace, placeholder, rows }"
|
||||
v-model="value"
|
||||
v-bind="{ placeholder, trim, font, disabled }"
|
||||
@input="onInput"
|
||||
/>
|
||||
<raw-value>{{ value }}</raw-value>
|
||||
</div>
|
||||
`,
|
||||
});
|
||||
|
||||
@@ -13,10 +13,7 @@ describe('Interfaces / Text Input', () => {
|
||||
const component = shallowMount(InterfaceTextarea, {
|
||||
localVue,
|
||||
propsData: {
|
||||
options: {
|
||||
monospace: false,
|
||||
placeholder: 'Enter value...',
|
||||
},
|
||||
placeholder: 'Enter value...',
|
||||
},
|
||||
listeners: {
|
||||
input: () => {},
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
<template>
|
||||
<v-textarea
|
||||
v-model="value"
|
||||
:placeholder="placeholder"
|
||||
:monospace="monospace"
|
||||
:rows="rows"
|
||||
v-bind="{ placeholder, trim }"
|
||||
:value="value"
|
||||
:disabled="disabled"
|
||||
:class="font"
|
||||
@input="$listeners.input"
|
||||
full-width
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from '@vue/composition-api';
|
||||
import { defineComponent, PropType } from '@vue/composition-api';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
@@ -25,10 +26,30 @@ export default defineComponent({
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
monospace: {
|
||||
trim: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
default: true,
|
||||
},
|
||||
font: {
|
||||
type: String as PropType<'sans-serif' | 'serif' | 'monospace'>,
|
||||
default: 'sans-serif',
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.v-textarea {
|
||||
&.monospace {
|
||||
--v-input-font-family: var(--family-monospace);
|
||||
}
|
||||
|
||||
&.serif {
|
||||
--v-input-font-family: var(--family-serif);
|
||||
}
|
||||
|
||||
&.sans-serif {
|
||||
--v-input-font-family: var(--family-sans-serif);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user