Ранее мы создавали этот компонент во Vue здесь; Теперь мы рассмотрим его создание в Svelte - красивом и крошечном фреймворке, который компилирует компоненты и не требует времени выполнения.
Исправляя некоторые ошибки и внося некоторые обновления в свою адресную книгу, я решил изменить поля ввода на встроенный редактируемый стиль, который есть в Trello (см. Ниже), Google Таблицах и т. Д.

Я хотел бы использовать этот компонент в других приложениях, поэтому в этом руководстве мы сделаем его отдельным компонентом.
Поскольку это единый компонент, проще тестировать в онлайн-песочнице, такой как эта на Sandbox.io, вместо того, чтобы создавать полноценное приложение.
Svelte предоставляет базу для создания общих компонентов, и мы будем ее использовать.
Для начала бегу:
$ npx degit sveltejs/component-template svelte-inline-input
$ cd svelte-inline-input
$ npm install
В package.json мы меняем имя на svelte-inline-input и переименовываем src/Component.svelte в src/InlineInput.svelte. Мы также обновляем ссылку в src/index.js.
Инициализируйте Git и создайте коммит.
Состояние встроенного редактируемого компонента по умолчанию - отображение статического элемента. При щелчке по компоненту он трансформируется в поле ввода, которое принимает вводимые пользователем данные. Когда фокус уходит с элемента, он переключается обратно на статический элемент.
Наш компонент будет обрабатывать входные данные text, number, select и textarea.
Мы оставим стили на усмотрение пользователей. По крайней мере, вы должны показывать курсор-указатель при наведении указателя мыши на статический элемент.
Число и текст
<script> import { tick } from 'svelte';// Props export let value = ''; export let type = 'text'; export let placeholder = ''; export let labelClasses = ''; export let inputClasses = '';let editing = false; let inputEl; let label;// Computed $: isText = type === 'text'; $: isNumber = type === 'number'; $: if (isNumber) { label = value === '' ? placeholder : value; } else if (isText) { label = value ? value : placeholder; }const toggle = async (_) => { editing = !editing;if (editing) { await tick(); inputEl.focus(); } };const handleInput = (e) => { value = isNumber ? +e.target.value : e.target.value; };const handleEnter = (e) => { if (e.keyCode === 13) inputEl.blur(); };const handleBlur = (_) => { toggle(); }; </script>{#if editing && (isText || isNumber)} <input class={inputClasses} bind:this={inputEl} {type} {value} {placeholder} on:input={handleInput} on:keyup={handleEnter} on:blur={handleBlur}> {:else} <span class={labelClasses} on:click={toggle}> {label} </span> {/if}
Наше состояние по умолчанию - статический элемент. Мы будем использовать диапазон как наш статический элемент. При щелчке по диапазону мы скрываем его и показываем наш элемент ввода.
В строках 5–9 мы объявляем наши реквизиты (т.е. переменные, которые мы ожидаем от родительских компонентов) и устанавливаем их значения по умолчанию. В строках 11–13 объявляются наши внутренние переменные. Строки 16–22 содержат наши реактивные объявления (т. Е. Эквивалент вычисляемых свойств Vue в Svelte).
При щелчке по диапазону вызывается toggle(), который выполняет две функции. Он скрывает диапазон, устанавливая для editing значение true, и помещает фокус на наш ввод, чтобы он мог принимать вводимые пользователем данные без того, чтобы пользователю приходилось нажимать на ввод еще раз. Это одна из причин, по которой мы удерживаем ссылку на вход через inputEl; другой - убрать фокус с ввода.
Когда фокус покидает вход, мы обрабатываем событие размытия, запущенное через handleBlur(). Эта функция вызывает toggle(), чтобы скрыть ввод и показать диапазон.
Мы также хотим переключать компонент, когда пользователь нажимает клавишу Enter. Мы обрабатываем это через handleEnter(), который просто забирает фокус с элемента ввода, что вызывает вызов handleBlur().
Когда пользователь изменяет входное значение, мы вызываем handleInput(). Он проверяет, является ли свойство типа числом, и если это так, преобразует значение события в число и обновляет value.
Если вы видели версию Vue, возможно, вам интересно, где находится emitValue(). Поскольку Svelte поддерживает двустороннюю привязку данных с помощью директивы bind, emitValue() не требуется. Нам просто нужно выполнить привязку к value в наших родительских компонентах следующим образом:
<InlineInput bind:value={yourVariable} />
Если ваша переменная называется value, вы можете сделать:
<InlineInput bind:value />
Свойства inputClasses и labelClasses предназначены для того, чтобы дать пользователю полный контроль над стилем, поскольку мы не собираемся делать какие-либо стили.
Наша label вычисляемая опора управляет тем, что отображается, когда отображается статический элемент. Для числового ввода мы отображаем placeholder только тогда, когда ничего не было введено; в противном случае мы отображаем ввод пользователя (это гарантирует, что отображается 0). Для ввода текста мы отображаем placeholder, если value пусто.
В строке 50 обратите внимание на привязку {type}. Это сокращение от type={type}, что эквивалентно :type="type" в Vue.
Это все, что нужно для ввода текста и чисел. Пойдем дальше и создадим коммит здесь.
Textarea
<script> // ...// Props // ... export let rows = 2; export let cols = 20;// ...// Computed // ... $: isTextArea = type === 'textarea'; $: if (isNumber) { label = value === '' ? placeholder : value; } else if (isText || isTextArea) { label = value ? value : placeholder; }// ... </script>{#if editing && (isText || isNumber)} <!-- --> {:else if editing && isTextArea} <textarea class={inputClasses} bind:this={inputEl} {placeholder} {value} {rows} {cols} on:input={handleInput} on:blur={handleBlur} /> {:else} <!-- --> {/if}
В текстовых полях Enter - это нормальный ввод, и было бы плохим UX переключать компонент при нажатии Enter. По этой причине мы обрабатываем Enter отдельно от других входных данных. Таким образом, нам нужно только не добавлять обработчик в текстовое поле, а не изменять handleInput(), чтобы отличать Enter от других полей ввода и текстовых полей для числа и текста. Это сделало бы нашу функцию более сложной и более подверженной ошибкам.
Единственное добавление - это свойства rows и cols, которые позволяют родительскому компоненту контролировать размер. Они инициализируются значениями по умолчанию HTML, если родительский компонент не предоставляет значения.
Это все, что нужно для поддержки текстовых полей. Сделаем коммит здесь.
Выбирать
<script> // ...// Props // ... export let options = [];// ... let selectedIndex = options.findIndex(o => o.value === value);// Computed // ... $: isSelect = type === 'select'; $: if (isNumber) { label = value === '' ? placeholder : value; } else if (isText || isTextArea) { label = value ? value : placeholder; } else { // Select label = selectedIndex === -1 ? placeholder : options[selectedIndex].label; }// ...const handleChange = (e) => { selectedIndex = placeholder ? e.target.selectedIndex - 1 : e.target.selectedIndex; value = options[selectedIndex].value; }; </script>{#if editing && (isText || isNumber)} <!-- --> {:else if editing && isTextArea} <!-- --> {:else if editing && isSelect} <select class={inputClasses} bind:this={inputEl} {value} on:blur={handleBlur}> {#if placeholder} <option selected value disabled>{placeholder}</option> {/if} {#each options as { label, value }, i} <option key={i} {value}> {label} </option> {/each} </select> {:else} <span class={labelClasses} on:click={toggle}> {label} <slot name="selectCaret"> {#if isSelect} <span>▼</span> {/if} </slot> </span> {/if}
Для выбора нам нужен массив параметров, который предоставляется через свойство options, которое представляет собой массив объектов с ключами label и value.
В элементе span мы показываем курсор, чтобы указать, что это выбранный элемент. Мы добавили курсор в именованный слот, чтобы пользователь мог изменить значение по умолчанию.
В нашей вычисляемой опоре label для выборок мы отображаем метку текущего выбранного значения, если таковое имеется, или предоставленный заполнитель.
Когда выбор сделан, мы получаем индекс выбранной опции (который на единицу меньше, если был указан заполнитель). Затем мы обновляем value.
При этом мы добавили поддержку select входов. Как и раньше, делаем коммит здесь.
Публикация в NPM
Перед публикацией нам нужно создать README с полезным текстом и собрать компонент.
Чтобы построить компонент, запустите:
$ npm run build
В этом случае мы производим сборку только при публикации компонента с использованием ловушки prepublishOnly в нашем объявлении package.json scripts.
Наш контент README:
# svelte-inline-input> An inline editable input component for Svelte.Displays as text and becomes editable by clicking or tapping.## Installation```js npm install svelte-inline-input ```### Browser```html <script type="text/javascript" src="https://unpkg.com/svelte-inline-input/dist/index.mjs"></script> ```### Module```js import InlineInput from 'svelte-inline-input'; ```## UsageOnce installed, it can be used in a template as:```html <InlineInput bind:value /> ```See the props table below for the available options.### Props| Property | Type | Description | Default | |:--|:--|:--|:--| | type | string | The input type. Could be text, number, textarea or select | text | | placeholder | string | Text to be shown as a placeholder while there is no input | empty string | | labelClasses | string | CSS classes for the label element | empty string | | inputClasses | string | CSS classes for the input element | empty string | | rows | integer | Textarea rows | 2 | | cols | integer | Textarea columns | 20 | | options | array | Provides the options for selects. Each object should have the format `{label: x, value: x}` | [] |## License[MIT](http://opensource.org/licenses/MIT)
Затем мы добавляем в наш package.json дополнительную информацию, необходимую для публикации компонента. После этого фиксируем изменения.
Чтобы опубликовать, я сначала вхожу в свою учетную запись NPM, используя:
$ npm login
Затем из корневого каталога я запускаю:
$ npm publish
После успешной публикации мы можем использовать наш компонент в проектах Node.js, установив его с помощью:
$ npm install svelte-inline-input
или в браузере с помощью https://unpkg.com/svelte-inline-input.
Репозиторий GitHub можно найти здесь.