@operato/input 10.10.0 → 10.10.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"ox-input-search.js","sourceRoot":"","sources":["../../src/ox-input-search.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @license Copyright © HatioLab Inc. All rights reserved.\n */\n\nimport '@material/web/icon/icon.js'\n\n// import debounce from 'lodash-es/debounce.js'\nimport { css, html } from 'lit'\nimport { ifDefined } from 'lit/directives/if-defined.js'\nimport { customElement, property, query } from 'lit/decorators.js'\n\nimport { i18next } from '@operato/i18n'\n\nimport { OxFormField } from './ox-form-field.js'\nimport { InputStyles } from './input-styles.js'\n\n@customElement('ox-input-search')\nexport class OxInputSearch extends OxFormField {\n static styles = [\n InputStyles,\n css`\n :host {\n display: flex;\n position: relative;\n align-items: center;\n\n color: var(--ox-input-search-color, var(--md-sys-color-on-surface-variant));\n background-color: var(--ox-input-search-background-color, var(--md-sys-color-surface-variant));\n\n --md-icon-size: 20px;\n }\n\n [type='text'] {\n flex: 1;\n color: inherit;\n background-color: transparent;\n border: 0;\n padding: var(--input-search-padding, calc(var(--ox-input-gap) / 2));\n padding-left: 25px;\n overflow: hidden;\n }\n\n [type='text']:focus {\n outline: none;\n }\n\n /*\n * 아이콘 색을 글자와 따로 받는다.\n *\n * 돋보기는 **장치**이고 입력한 글자는 **값**이다. 둘이 같은 색이면 장치가 값만큼 눈에 걸린다.\n * 기본은 물려받는 색이라 지금 쓰는 곳의 거동이 바뀌지 않고, 담는 화면이 필요할 때만 내린다.\n */\n md-icon {\n position: absolute;\n color: var(--ox-input-search-icon-color, inherit);\n }\n\n input {\n background-color: var(--input-field-background, var(--md-sys-color-surface-container-highest));\n color: var(--input-field-color, var(--md-sys-color-on-surface));\n width: 100%;\n }\n `\n ]\n\n @property({ type: String }) placeholder?: string\n @property({ attribute: 'select-after-change', type: Boolean }) selectAfterChange?: boolean\n @property({ type: Number, attribute: 'max-length' }) maxLength?: number\n\n @query('input') input!: HTMLInputElement\n\n // private dispatchChangeEventDebouncer = debounce((value: string) => this.dispatchChangeEvent(value), 100)\n\n render() {\n return html`\n <!--\n 돋보기 — 누르면 검색을 건다. 그런데 md-icon 은 포커스를 받지 않아서 키보드로는 닿지 않는다.\n 같은 일을 입력칸에서 Enter 로 할 수 있으므로 기능이 막힌 것은 아니다. 이름 없는 클릭 대상이\n 하나 더 읽히지 않도록 보조기술에서는 지운다 — 감춰도 잃는 동작이 없다.\n -->\n <md-icon aria-hidden=\"true\" @click=${(e: Event) => !this.disabled && this._onClickSearch(e)}>search</md-icon>\n <!--\n 입력칸에 이름을 붙인다. 전에는 placeholder 만 있었다 — placeholder 는 이름이 아니다.\n 글자를 치는 순간 사라지므로 「내가 지금 무엇을 적고 있었나」를 되물을 수 없고, 부르는 쪽이\n placeholder 를 안 넘기면 아무것도 남지 않는다(대부분의 화면이 안 넘긴다).\n\n 부르는 쪽이 정한 문구가 있으면 그것을 쓰고, 없으면 「검색어」로 물러난다.\n -->\n <input\n type=\"text\"\n aria-label=${this.placeholder || i18next.t('label.search', { defaultValue: 'search' })}\n .value=${this.value || ''}\n .placeholder=${this.placeholder || ''}\n maxlength=${ifDefined(this.maxLength)}\n @change=${(e: Event) => {\n e.stopPropagation()\n this.dispatchChangeEvent((e.target as HTMLInputElement).value)\n // this.dispatchChangeEventDebouncer((e.target as HTMLInputElement).value)\n }}\n @keydown=${(e: KeyboardEvent) => {\n if (e.key === 'Enter') {\n if ((e.target as HTMLInputElement).value == this.value) {\n /* Even if the value has not changed, the enter key triggers a change event. */\n e.preventDefault() /* Prevent change event from occurring twice. */\n\n this.input.dispatchEvent(\n new CustomEvent('change', {\n detail: (e.target as HTMLInputElement).value\n })\n )\n }\n }\n }}\n ?disabled=${this.disabled}\n />\n `\n }\n\n firstUpdated() {\n if (this.autofocus) {\n this.input.focus()\n }\n }\n\n dispatchChangeEvent(value: string) {\n this.value = value\n\n this.dispatchEvent(\n new CustomEvent('change', {\n bubbles: true,\n composed: true,\n detail: value\n })\n )\n\n if (this.selectAfterChange) {\n requestAnimationFrame(() => {\n this.input.select()\n })\n }\n }\n\n _onClickSearch(e: Event) {\n if (this._form) {\n e.stopPropagation()\n this._form.requestSubmit()\n } else {\n this.input.dispatchEvent(\n new CustomEvent('change', {\n detail: this.input.value\n })\n )\n }\n }\n}\n"]}
1
+ {"version":3,"file":"ox-input-search.js","sourceRoot":"","sources":["../../src/ox-input-search.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @license Copyright © HatioLab Inc. All rights reserved.\n */\n\nimport '@material/web/icon/icon.js'\n\n// import debounce from 'lodash-es/debounce.js'\nimport { css, html } from 'lit'\nimport { ifDefined } from 'lit/directives/if-defined.js'\nimport { customElement, property, query } from 'lit/decorators.js'\n\nimport { i18next } from '@operato/i18n'\n\nimport { OxFormField } from './ox-form-field.js'\nimport { InputStyles } from './input-styles.js'\n\n@customElement('ox-input-search')\nexport class OxInputSearch extends OxFormField {\n static styles = [\n InputStyles,\n css`\n :host {\n display: flex;\n position: relative;\n align-items: center;\n\n color: var(--ox-input-search-color, var(--md-sys-color-on-surface-variant));\n background-color: var(--ox-input-search-background-color, var(--md-sys-color-surface-variant));\n\n --md-icon-size: 20px;\n }\n\n [type='text'] {\n flex: 1;\n color: inherit;\n background-color: transparent;\n border: 0;\n padding: var(--input-search-padding, calc(var(--ox-input-gap) / 2));\n padding-left: 25px;\n overflow: hidden;\n }\n\n [type='text']:focus {\n outline: none;\n }\n\n /*\n * 아이콘 색을 글자와 따로 받는다.\n *\n * 돋보기는 **장치**이고 입력한 글자는 **값**이다. 둘이 같은 색이면 장치가 값만큼 눈에 걸린다.\n * 기본은 물려받는 색이라 지금 쓰는 곳의 거동이 바뀌지 않고, 담는 화면이 필요할 때만 내린다.\n */\n md-icon {\n position: absolute;\n color: var(--ox-input-search-icon-color, inherit);\n }\n\n input {\n background-color: var(--input-field-background, var(--md-sys-color-surface-container-highest));\n color: var(--input-field-color, var(--md-sys-color-on-surface));\n width: 100%;\n }\n `\n ]\n\n @property({ type: String }) placeholder?: string\n @property({ attribute: 'select-after-change', type: Boolean }) selectAfterChange?: boolean\n @property({ type: Number, attribute: 'max-length' }) maxLength?: number\n\n @query('input') input!: HTMLInputElement\n\n // private dispatchChangeEventDebouncer = debounce((value: string) => this.dispatchChangeEvent(value), 100)\n\n render() {\n return html`\n <!--\n The magnifier runs the search on click, but md-icon takes no focus, so the keyboard never\n reaches it. Enter in the input does the same thing, so no function is blocked. Hiding it\n from assistive tech removes one more unnamed click target and costs nothing.\n -->\n <md-icon aria-hidden=\"true\" @click=${(e: Event) => !this.disabled && this._onClickSearch(e)}>search</md-icon>\n <!--\n Name the input. It had only a placeholder, and a placeholder is not a name: it disappears\n the moment you type, so there is no way to ask again what you were entering, and when the\n caller passes none — which most screens do — nothing is left.\n\n Use whatever the caller set, and fall back to \"search\".\n -->\n <input\n type=\"text\"\n aria-label=${this.placeholder || i18next.t('label.search', { defaultValue: 'search' })}\n .value=${this.value || ''}\n .placeholder=${this.placeholder || ''}\n maxlength=${ifDefined(this.maxLength)}\n @change=${(e: Event) => {\n e.stopPropagation()\n this.dispatchChangeEvent((e.target as HTMLInputElement).value)\n // this.dispatchChangeEventDebouncer((e.target as HTMLInputElement).value)\n }}\n @keydown=${(e: KeyboardEvent) => {\n if (e.key === 'Enter') {\n if ((e.target as HTMLInputElement).value == this.value) {\n /* Even if the value has not changed, the enter key triggers a change event. */\n e.preventDefault() /* Prevent change event from occurring twice. */\n\n this.input.dispatchEvent(\n new CustomEvent('change', {\n detail: (e.target as HTMLInputElement).value\n })\n )\n }\n }\n }}\n ?disabled=${this.disabled}\n />\n `\n }\n\n firstUpdated() {\n if (this.autofocus) {\n this.input.focus()\n }\n }\n\n dispatchChangeEvent(value: string) {\n this.value = value\n\n this.dispatchEvent(\n new CustomEvent('change', {\n bubbles: true,\n composed: true,\n detail: value\n })\n )\n\n if (this.selectAfterChange) {\n requestAnimationFrame(() => {\n this.input.select()\n })\n }\n }\n\n _onClickSearch(e: Event) {\n if (this._form) {\n e.stopPropagation()\n this._form.requestSubmit()\n } else {\n this.input.dispatchEvent(\n new CustomEvent('change', {\n detail: this.input.value\n })\n )\n }\n }\n}\n"]}
@@ -3,47 +3,48 @@ import { html } from 'lit';
3
3
  import '../src/ox-input-background-pattern.js';
4
4
  import { registerImageSelector } from '../src/image-selector.js';
5
5
  /**
6
- * 그림 고르는 칸의 자리.
6
+ * The image-picking slot.
7
7
  *
8
- * ## 왜 시험이 필요한가
8
+ * ## Why this is tested
9
9
  *
10
- * `@operato/input` 이 `@operato/attachment` 를 직접 부르고 있었다. 가장 아래의 부품이 위의 기능을
11
- * 아는 모양이라 고리가 생겼고(input ▶ attachment ▶ data-grist ▶ input), 그 고리 때문에 이 package
12
- * 는 **빌드되지 않았다**(TS5055 일곱 건, noEmitOnError).
10
+ * `@operato/input` used to import `@operato/attachment` directly. The bottom layer reached for a
11
+ * feature above it, closing a cycle (input ▶ attachment ▶ data-grist ▶ input) that stopped this
12
+ * package from building — TS5055 on seven files, with `noEmitOnError`.
13
13
  *
14
- * 화살표를 뒤집으면서 두 가지가 지켜져야 한다.
14
+ * Reversing that arrow has to hold two things:
15
15
  *
16
16
  * ```
17
- * 비지 않는다 등록이 없어도 칸이 있어야 한다. 등록 안 된 custom element 는 소리 없이 빈 상자다
18
- * 값이 담긴다 어느 갈래가 그려지든 value-key 와 change 로 값이 같은 길을 지나야 한다
17
+ * never empty with nothing registered there must still be a field; an unregistered
18
+ * custom element renders as a silent empty box
19
+ * value lands either branch carries value-key and raises change, so the value takes
20
+ * the same path out
19
21
  * ```
20
22
  */
21
- describe('그림 고르는 칸은 위 layer 가 채운다', () => {
23
+ describe('image-picking field', () => {
22
24
  async function pattern(value = {}) {
23
25
  const el = await fixture(html `<ox-input-background-pattern .value=${value}></ox-input-background-pattern>`);
24
26
  await el.updateComplete;
25
27
  return el;
26
28
  }
27
- it('등록이 없으면 글자 칸이다 — 빈 자리로 두지 않는다', async () => {
29
+ it('falls back to a text box when nothing is registered', async () => {
28
30
  const el = await pattern({ image: 'https://example.com/a.png' });
29
31
  const field = el.renderRoot.querySelector('[value-key="image"]');
30
- expect(field, '그림 칸이 아예 없다').to.exist;
32
+ expect(field, 'no image field at all').to.exist;
31
33
  expect(field.tagName).to.equal('INPUT');
32
- expect(field.value, '적혀 있던 주소가 칸에 실리지 않았다').to.equal('https://example.com/a.png');
34
+ expect(field.value, 'existing address did not reach the field').to.equal('https://example.com/a.png');
33
35
  });
34
- it('글자 칸에 적은 주소가 값으로 담긴다', async () => {
36
+ it('takes an address typed into the fallback text box', async () => {
35
37
  const el = await pattern({});
36
38
  const field = el.renderRoot.querySelector('[value-key="image"]');
37
39
  field.value = 'https://example.com/b.png';
38
40
  field.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
39
41
  expect(el.value.image).to.equal('https://example.com/b.png');
40
42
  });
41
- it('그림을 가진 쪽이 자기 칸을 등록하면 그것으로 바뀐다', async () => {
43
+ it('swaps in the registered field, even when registration lands after render', async () => {
42
44
  class FakeSelector extends HTMLElement {
43
45
  }
44
46
  customElements.define('test-image-selector', FakeSelector);
45
47
  const el = await pattern({ image: 'https://example.com/c.png' });
46
- /* 요소가 먼저 그려지고 등록이 나중에 와도 자리가 글자 칸으로 남아 있으면 안 된다. */
47
48
  registerImageSelector('test-image-selector');
48
49
  await el.updateComplete;
49
50
  const field = el.renderRoot.querySelector('[value-key="image"]');
@@ -51,7 +52,7 @@ describe('그림 고르는 칸은 위 layer 가 채운다', () => {
51
52
  expect(field.value).to.equal('https://example.com/c.png');
52
53
  expect(field.properties).to.deep.equal({ category: 'image' });
53
54
  });
54
- it('등록된 칸이 올린 change 도 같은 길로 값이 담긴다', async () => {
55
+ it('takes a change raised by the registered field the same way', async () => {
55
56
  registerImageSelector('test-image-selector');
56
57
  const el = await pattern({});
57
58
  const field = el.renderRoot.querySelector('[value-key="image"]');
@@ -1 +1 @@
1
- {"version":3,"file":"image-selector.test.js","sourceRoot":"","sources":["../../test/image-selector.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { expect, fixture } from '@open-wc/testing'\nimport { html } from 'lit'\n\nimport '../src/ox-input-background-pattern.js'\nimport { registerImageSelector } from '../src/image-selector.js'\n\n/**\n * 그림 고르는 칸의 자리.\n *\n * ## 왜 시험이 필요한가\n *\n * `@operato/input` 이 `@operato/attachment` 를 직접 부르고 있었다. 가장 아래의 부품이 위의 기능을\n * 아는 모양이라 고리가 생겼고(input ▶ attachment ▶ data-grist ▶ input), 그 고리 때문에 이 package\n * 는 **빌드되지 않았다**(TS5055 일곱 건, noEmitOnError).\n *\n * 화살표를 뒤집으면서 두 가지가 지켜져야 한다.\n *\n * ```\n * 비지 않는다 등록이 없어도 칸이 있어야 한다. 등록 안 된 custom element 는 소리 없이 빈 상자다\n * 값이 담긴다 어느 갈래가 그려지든 value-key 와 change 로 값이 같은 길을 지나야 한다\n * ```\n */\ndescribe('그림 고르는 칸은 위 layer 가 채운다', () => {\n async function pattern(value: any = {}) {\n const el: any = await fixture(html`<ox-input-background-pattern .value=${value}></ox-input-background-pattern>`)\n await el.updateComplete\n return el\n }\n\n it('등록이 없으면 글자 칸이다 — 빈 자리로 두지 않는다', async () => {\n const el = await pattern({ image: 'https://example.com/a.png' })\n\n const field = el.renderRoot.querySelector('[value-key=\"image\"]') as HTMLInputElement\n\n expect(field, '그림 칸이 아예 없다').to.exist\n expect(field.tagName).to.equal('INPUT')\n expect(field.value, '적혀 있던 주소가 칸에 실리지 않았다').to.equal('https://example.com/a.png')\n })\n\n it('글자 칸에 적은 주소가 값으로 담긴다', async () => {\n const el = await pattern({})\n\n const field = el.renderRoot.querySelector('[value-key=\"image\"]') as HTMLInputElement\n field.value = 'https://example.com/b.png'\n field.dispatchEvent(new Event('change', { bubbles: true, composed: true }))\n\n expect(el.value.image).to.equal('https://example.com/b.png')\n })\n\n it('그림을 가진 쪽이 자기 칸을 등록하면 그것으로 바뀐다', async () => {\n class FakeSelector extends HTMLElement {\n value: any\n properties: any\n }\n customElements.define('test-image-selector', FakeSelector)\n\n const el = await pattern({ image: 'https://example.com/c.png' })\n\n /* 요소가 먼저 그려지고 등록이 나중에 와도 자리가 글자 칸으로 남아 있으면 안 된다. */\n registerImageSelector('test-image-selector')\n await el.updateComplete\n\n const field = el.renderRoot.querySelector('[value-key=\"image\"]') as any\n\n expect(field.tagName).to.equal('TEST-IMAGE-SELECTOR')\n expect(field.value).to.equal('https://example.com/c.png')\n expect(field.properties).to.deep.equal({ category: 'image' })\n })\n\n it('등록된 칸이 올린 change 도 같은 길로 값이 담긴다', async () => {\n registerImageSelector('test-image-selector')\n\n const el = await pattern({})\n const field = el.renderRoot.querySelector('[value-key=\"image\"]') as any\n\n field.value = 'https://example.com/d.png'\n field.dispatchEvent(new Event('change', { bubbles: true, composed: true }))\n\n expect(el.value.image).to.equal('https://example.com/d.png')\n })\n})\n"]}
1
+ {"version":3,"file":"image-selector.test.js","sourceRoot":"","sources":["../../test/image-selector.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { expect, fixture } from '@open-wc/testing'\nimport { html } from 'lit'\n\nimport '../src/ox-input-background-pattern.js'\nimport { registerImageSelector } from '../src/image-selector.js'\n\n/**\n * The image-picking slot.\n *\n * ## Why this is tested\n *\n * `@operato/input` used to import `@operato/attachment` directly. The bottom layer reached for a\n * feature above it, closing a cycle (input ▶ attachment ▶ data-grist ▶ input) that stopped this\n * package from building — TS5055 on seven files, with `noEmitOnError`.\n *\n * Reversing that arrow has to hold two things:\n *\n * ```\n * never empty with nothing registered there must still be a field; an unregistered\n * custom element renders as a silent empty box\n * value lands either branch carries value-key and raises change, so the value takes\n * the same path out\n * ```\n */\ndescribe('image-picking field', () => {\n async function pattern(value: any = {}) {\n const el: any = await fixture(html`<ox-input-background-pattern .value=${value}></ox-input-background-pattern>`)\n await el.updateComplete\n return el\n }\n\n it('falls back to a text box when nothing is registered', async () => {\n const el = await pattern({ image: 'https://example.com/a.png' })\n\n const field = el.renderRoot.querySelector('[value-key=\"image\"]') as HTMLInputElement\n\n expect(field, 'no image field at all').to.exist\n expect(field.tagName).to.equal('INPUT')\n expect(field.value, 'existing address did not reach the field').to.equal('https://example.com/a.png')\n })\n\n it('takes an address typed into the fallback text box', async () => {\n const el = await pattern({})\n\n const field = el.renderRoot.querySelector('[value-key=\"image\"]') as HTMLInputElement\n field.value = 'https://example.com/b.png'\n field.dispatchEvent(new Event('change', { bubbles: true, composed: true }))\n\n expect(el.value.image).to.equal('https://example.com/b.png')\n })\n\n it('swaps in the registered field, even when registration lands after render', async () => {\n class FakeSelector extends HTMLElement {\n value: any\n properties: any\n }\n customElements.define('test-image-selector', FakeSelector)\n\n const el = await pattern({ image: 'https://example.com/c.png' })\n\n registerImageSelector('test-image-selector')\n await el.updateComplete\n\n const field = el.renderRoot.querySelector('[value-key=\"image\"]') as any\n\n expect(field.tagName).to.equal('TEST-IMAGE-SELECTOR')\n expect(field.value).to.equal('https://example.com/c.png')\n expect(field.properties).to.deep.equal({ category: 'image' })\n })\n\n it('takes a change raised by the registered field the same way', async () => {\n registerImageSelector('test-image-selector')\n\n const el = await pattern({})\n const field = el.renderRoot.querySelector('[value-key=\"image\"]') as any\n\n field.value = 'https://example.com/d.png'\n field.dispatchEvent(new Event('change', { bubbles: true, composed: true }))\n\n expect(el.value.image).to.equal('https://example.com/d.png')\n })\n})\n"]}