@operato/font 10.22.0 → 10.24.0

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.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,28 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [10.24.0](https://github.com/hatiolab/operato/compare/v10.23.0...v10.24.0) (2026-09-24)
7
+
8
+ **Note:** Version bump only for package @operato/font
9
+
10
+
11
+
12
+
13
+
14
+ ## [10.23.0](https://github.com/hatiolab/operato/compare/v10.22.0...v10.23.0) (2026-09-24)
15
+
16
+
17
+ ### :sparkles: Styling
18
+
19
+ * **property-panel,input,styles:** quiet inspector, one vocabulary in both schemes ([40e67e9](https://github.com/hatiolab/operato/commit/40e67e9aced202151a4f2f8d7ef96ec840defc18))
20
+
21
+
22
+ ### :rocket: New Features
23
+
24
+ * **font:** specimen cards for the font list; themeable danger buttons ([e3cafc0](https://github.com/hatiolab/operato/commit/e3cafc03eec6e5cb9e76ae640c05dd9129e4893f))
25
+
26
+
27
+
6
28
  ## [10.22.0](https://github.com/hatiolab/operato/compare/v10.21.0...v10.22.0) (2026-09-24)
7
29
 
8
30
  **Note:** Version bump only for package @operato/font
@@ -19,7 +19,9 @@ let FontCreationCard = class FontCreationCard extends localize(i18next)(LitEleme
19
19
  let isProviderGoogle = this.provider == 'google' && this.googleFonts.length > 0;
20
20
  let isFileAttached = this.files && this.files.length > 0 ? true : false;
21
21
  return html `
22
- <div @click=${(e) => this.onClickFlip(e)} front><md-icon>add_circle_outline</md-icon>create font</div>
22
+ <div @click=${(e) => this.onClickFlip(e)} front>
23
+ <md-icon>add</md-icon>${i18next.t('button.create font', { defaultValue: 'Create font' })}
24
+ </div>
23
25
 
24
26
  <div @click=${(e) => this.onClickFlip(e)} back>
25
27
  <form @submit=${(e) => this.onClickSubmit(e)}>
@@ -77,7 +79,6 @@ let FontCreationCard = class FontCreationCard extends localize(i18next)(LitEleme
77
79
  </label>
78
80
  <input id="checkbox-active" type="checkbox" name="active" checked />
79
81
  </div>
80
- <div></div>
81
82
  <input type="submit" value=${i18next.t('button.create')} />
82
83
  </form>
83
84
  </div>
@@ -119,6 +120,10 @@ let FontCreationCard = class FontCreationCard extends localize(i18next)(LitEleme
119
120
  };
120
121
  FontCreationCard.styles = [
121
122
  css `
123
+ /*
124
+ * The first card in the list: an offer on its front, the short form on its back. Both faces
125
+ * take the list's card shape; the front is dashed because nothing is there yet.
126
+ */
122
127
  :host {
123
128
  position: relative;
124
129
 
@@ -145,11 +150,10 @@ FontCreationCard.styles = [
145
150
  height: 100%;
146
151
  margin: 0;
147
152
  padding: 0;
148
-
149
- border: var(--card-list-create-border);
153
+ box-sizing: border-box;
154
+ border-radius: 12px;
150
155
 
151
156
  color: var(--md-sys-color-on-surface);
152
- background-color: var(--md-sys-color-surface);
153
157
 
154
158
  -webkit-backface-visibility: hidden;
155
159
  backface-visibility: hidden;
@@ -160,84 +164,131 @@ FontCreationCard.styles = [
160
164
  flex-direction: column;
161
165
  align-items: center;
162
166
  justify-content: center;
167
+ gap: 6px;
163
168
 
169
+ border: 1px dashed var(--md-sys-color-outline-variant);
170
+ background: transparent;
171
+ color: var(--md-sys-color-on-surface-variant);
164
172
  text-align: center;
165
- font-size: 0.8em;
173
+ font-size: 0.82rem;
166
174
  text-transform: capitalize;
175
+ cursor: pointer;
176
+ }
177
+
178
+ [front]:hover {
179
+ border-color: var(--md-sys-color-outline);
180
+ color: var(--md-sys-color-on-surface);
181
+ background: var(--md-sys-color-surface-container-low);
167
182
  }
168
183
 
169
184
  [front] md-icon {
170
- --md-icon-size: 42px;
171
- font-size: 3.5em;
185
+ --md-icon-size: 30px;
172
186
  color: var(--md-sys-color-primary);
187
+ font-variation-settings: 'wght' 300;
173
188
  }
174
189
 
175
190
  [back] {
176
191
  -webkit-transform: var(--card-list-flip-transform);
177
192
  transform: var(--card-list-flip-transform);
178
- box-sizing: border-box;
179
193
  display: grid;
194
+ border: 1px solid var(--md-sys-color-outline-variant);
195
+ background-color: var(--board-card-surface, var(--md-sys-color-surface-container-lowest));
180
196
  }
181
197
 
182
198
  [back] form {
183
- padding: var(--card-list-create-form-padding);
199
+ padding: 14px 16px;
184
200
  width: 100%;
185
201
  height: 100%;
186
202
  box-sizing: border-box;
187
203
  display: grid;
188
204
  grid-template-columns: 1fr;
189
- grid-template-rows: auto 1fr auto;
190
- justify-content: center;
191
- align-items: center;
205
+ grid-template-rows: 1fr auto;
206
+ gap: 10px;
207
+ align-items: start;
192
208
  }
193
209
 
194
210
  [back] form .props {
195
211
  width: 100%;
196
- height: 100%;
197
212
  box-sizing: border-box;
198
213
  display: grid;
199
- grid-template-columns: repeat(10, 1fr);
200
- grid-row-gap: 7px;
201
- justify-content: center;
214
+ grid-template-columns: 4fr 6fr;
215
+ gap: 6px 10px;
202
216
  align-items: center;
203
217
  }
204
218
 
205
219
  [back] form .props label {
206
- grid-column: span 4;
207
- font: var(--card-list-create-label-font);
208
- color: var(--card-list-create-label-color, var(--md-sys-color-secondary));
220
+ font-size: 0.74rem;
221
+ text-align: right;
222
+ color: var(--md-sys-color-on-surface-variant);
223
+ }
224
+
225
+ [back] form .props label[hidden],
226
+ [back] form .props input[hidden],
227
+ [back] form .props select[hidden] {
228
+ display: none;
209
229
  }
210
230
 
211
231
  input,
212
232
  select,
213
233
  ox-file-selector {
214
- background-color: var(--card-list-create-input-background-color, var(--md-sys-color-surface-variant));
215
- color: var(--card-list-create-input-color, var(--md-sys-color-on-surface));
234
+ color: var(--md-sys-color-on-surface);
216
235
  }
217
236
 
218
- [back] form .props input,
237
+ [back] form .props input:not([type='checkbox']),
219
238
  [back] form .props select {
220
- grid-column: span 6;
221
- border: var(--card-list-create-input-border);
222
- border-radius: var(--card-list-create-input-border-radius);
223
- font: var(--card-list-create-input-font);
224
- width: -moz-available;
239
+ min-width: 0;
240
+ width: 100%;
241
+ height: 26px;
242
+ padding: 0 8px;
243
+ box-sizing: border-box;
244
+ border: 1px solid transparent;
245
+ border-radius: 6px;
246
+ background: var(--property-input-background, var(--md-sys-color-surface-container-high));
247
+ font: inherit;
248
+ font-size: 0.78rem;
249
+ outline: none;
250
+ }
251
+
252
+ [back] form .props input:not([type='checkbox']):hover,
253
+ [back] form .props select:hover {
254
+ border-color: var(--md-sys-color-outline-variant);
255
+ }
256
+
257
+ [back] form .props input:not([type='checkbox']):focus,
258
+ [back] form .props select:focus {
259
+ border-color: var(--md-sys-color-primary);
260
+ }
261
+
262
+ [back] form .props input[type='checkbox'] {
263
+ justify-self: start;
264
+ margin: 0;
265
+ accent-color: var(--md-sys-color-primary);
225
266
  }
226
267
 
227
268
  ox-file-selector {
228
- grid-column: span 6;
229
- font: var(--card-list-create-input-font);
269
+ font-size: 0.78rem;
230
270
  border: none;
231
271
  box-sizing: border-box;
232
272
  padding: 0;
273
+ min-width: 0;
233
274
  }
234
275
 
235
276
  [back] input[type='submit'] {
236
- font: var(--button-font);
237
- border-radius: var(--button-radius);
238
- border: var(--button-border);
239
- grid-column: span 10;
240
- grid-row: auto / -1;
277
+ justify-self: end;
278
+ height: 28px;
279
+ padding: 0 16px;
280
+ border: 0;
281
+ border-radius: 999px;
282
+ background: var(--md-sys-color-primary);
283
+ color: var(--md-sys-color-on-primary);
284
+ font: inherit;
285
+ font-size: 0.78rem;
286
+ font-weight: 500;
287
+ cursor: pointer;
288
+ }
289
+
290
+ [back] input[type='submit']:hover {
291
+ filter: brightness(1.08);
241
292
  }
242
293
 
243
294
  .hidden {
@@ -1 +1 @@
1
- {"version":3,"file":"font-creation-card.js","sourceRoot":"","sources":["../../src/font-creation-card.ts"],"names":[],"mappings":"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","sourcesContent":["import './ox-file-selector.js'\n\nimport { LitElement, css, html } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\nimport { i18next, localize } from '@operato/i18n'\n\n@customElement('font-creation-card')\nexport class FontCreationCard extends localize(i18next)(LitElement) {\n static styles = [\n css`\n :host {\n position: relative;\n\n padding: 0;\n margin: 0;\n height: 100%;\n\n -webkit-transform-style: preserve-3d;\n transform-style: preserve-3d;\n -webkit-transition: all 0.5s ease-in-out;\n transition: all 0.5s ease-in-out;\n }\n\n :host(.flipped) {\n -webkit-transform: var(--card-list-flip-transform);\n transform: var(--card-list-flip-transform);\n }\n\n [front],\n [back] {\n position: absolute;\n\n width: 100%;\n height: 100%;\n margin: 0;\n padding: 0;\n\n border: var(--card-list-create-border);\n\n color: var(--md-sys-color-on-surface);\n background-color: var(--md-sys-color-surface);\n\n -webkit-backface-visibility: hidden;\n backface-visibility: hidden;\n }\n\n [front] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n\n text-align: center;\n font-size: 0.8em;\n text-transform: capitalize;\n }\n\n [front] md-icon {\n --md-icon-size: 42px;\n font-size: 3.5em;\n color: var(--md-sys-color-primary);\n }\n\n [back] {\n -webkit-transform: var(--card-list-flip-transform);\n transform: var(--card-list-flip-transform);\n box-sizing: border-box;\n display: grid;\n }\n\n [back] form {\n padding: var(--card-list-create-form-padding);\n width: 100%;\n height: 100%;\n box-sizing: border-box;\n display: grid;\n grid-template-columns: 1fr;\n grid-template-rows: auto 1fr auto;\n justify-content: center;\n align-items: center;\n }\n\n [back] form .props {\n width: 100%;\n height: 100%;\n box-sizing: border-box;\n display: grid;\n grid-template-columns: repeat(10, 1fr);\n grid-row-gap: 7px;\n justify-content: center;\n align-items: center;\n }\n\n [back] form .props label {\n grid-column: span 4;\n font: var(--card-list-create-label-font);\n color: var(--card-list-create-label-color, var(--md-sys-color-secondary));\n }\n\n input,\n select,\n ox-file-selector {\n background-color: var(--card-list-create-input-background-color, var(--md-sys-color-surface-variant));\n color: var(--card-list-create-input-color, var(--md-sys-color-on-surface));\n }\n\n [back] form .props input,\n [back] form .props select {\n grid-column: span 6;\n border: var(--card-list-create-input-border);\n border-radius: var(--card-list-create-input-border-radius);\n font: var(--card-list-create-input-font);\n width: -moz-available;\n }\n\n ox-file-selector {\n grid-column: span 6;\n font: var(--card-list-create-input-font);\n border: none;\n box-sizing: border-box;\n padding: 0;\n }\n\n [back] input[type='submit'] {\n font: var(--button-font);\n border-radius: var(--button-radius);\n border: var(--button-border);\n grid-column: span 10;\n grid-row: auto / -1;\n }\n\n .hidden {\n display: none !important;\n }\n `\n ]\n\n @property({ type: String }) provider: string = 'google'\n @property({ type: Array }) googleFonts: Array<string> = []\n @property({ type: Array }) files?: Array<any>\n\n providers: Array<{ value: string; display: string }> = [\n { value: 'google', display: 'Google' },\n // TODO 구글 외 폰트 서비스 구현\n // { value: 'typekit', display: 'Typekit' },\n { value: 'custom', display: 'Custom' }\n ]\n\n render() {\n let isProviderGoogle = this.provider == 'google' && this.googleFonts.length > 0\n let isFileAttached = this.files && this.files.length > 0 ? true : false\n\n return html`\n <div @click=${(e: Event) => this.onClickFlip(e)} front><md-icon>add_circle_outline</md-icon>create font</div>\n\n <div @click=${(e: Event) => this.onClickFlip(e)} back>\n <form @submit=${(e: Event) => this.onClickSubmit(e)}>\n <div class=\"props\">\n <label>${i18next.t('label.provider')}</label>\n <select\n name=\"provider\"\n @change=${(e: Event) => {\n this.provider = (e.target as HTMLInputElement).value\n if (this.provider === 'google') {\n fetch(`/all-google-fonts`).then(async response => {\n if (response.ok) this.googleFonts = await response.json()\n else {\n console.warn(\n `(${response.url}) ${response.status} ${response.statusText}. Could not load Google fonts.`\n )\n }\n })\n }\n }}\n >\n ${this.providers.map(\n p => html` <option value=${p.value} ?selected=${this.provider == p.value}>${p.display}</option> `\n )}\n </select>\n\n <label>${i18next.t('label.name')}</label>\n <input type=\"text\" name=\"${isProviderGoogle ? '' : 'name'}\" ?hidden=${isProviderGoogle} />\n <select name=\"${isProviderGoogle ? 'name' : ''}\" ?hidden=${!isProviderGoogle}>\n <option value=\"\">&nbsp;</option>\n ${isProviderGoogle && this.googleFonts.map(f => html` <option value=${f}>${f}</option> `)}\n </select>\n\n <label ?hidden=${this.provider != 'custom'}>${i18next.t('label.uri')}</label>\n <input\n ?hidden=${this.provider != 'custom'}\n ?disabled=${isFileAttached}\n .value=${isFileAttached ? this.files![0].name : ''}\n type=\"text\"\n name=\"uri\"\n />\n <!-- display when attachment module is imported -->\n <label ?hidden=${this.provider != 'custom'}>${i18next.t('label.file')}</label>\n <ox-file-selector\n class=\"${this.provider != 'custom' ? 'hidden' : ''}\"\n name=\"file\"\n label=\"${i18next.t('label.select file')}\"\n accept=\".ttf,.otf,.woff,.woff2,.eot,.svg,.svgz\"\n multiple\n @file-change=${(e: CustomEvent) => {\n this.files = Array.from(e.detail.files)\n }}\n ></ox-file-selector>\n <!------------------------------------------------>\n\n <label for=\"checkbox-active\" @click=${(e: Event) => e.stopPropagation()}>\n ${i18next.t('label.active')}\n </label>\n <input id=\"checkbox-active\" type=\"checkbox\" name=\"active\" checked />\n </div>\n <div></div>\n <input type=\"submit\" value=${i18next.t('button.create')} />\n </form>\n </div>\n `\n }\n\n onClickFlip(e: Event) {\n const target = e.target as HTMLElement\n const currentTarget = e.currentTarget as HTMLElement\n\n if (!['INPUT', 'SELECT', 'OPTION'].find(tagName => tagName === target.tagName)) {\n if (currentTarget.hasAttribute('front')) this.reset() // 입력 폼으로 뒤집기 전에 한 번 리셋\n this.classList.toggle('flipped')\n }\n }\n\n async onClickSubmit(e: Event) {\n e.preventDefault()\n e.stopPropagation()\n\n var form = e.target as HTMLFormElement\n\n var detail = {} as any\n\n detail.name = (form.elements.namedItem('name') as HTMLInputElement).value\n detail.provider = (form.elements.namedItem('provider') as HTMLInputElement).value\n detail.active = (form.elements.namedItem('active') as HTMLInputElement).checked\n\n if (this.provider === 'custom') {\n detail.uri = (form.elements.namedItem('uri') as HTMLInputElement).value\n if (this.files && this.files.length > 0) {\n detail.files = this.files\n }\n }\n\n this.dispatchEvent(new CustomEvent('create-font', { detail }))\n }\n\n reset() {\n var form = this.renderRoot.querySelector('form')\n if (form) {\n form.reset()\n }\n\n this.files = []\n this.classList.remove('flipped')\n }\n}\n"]}
1
+ {"version":3,"file":"font-creation-card.js","sourceRoot":"","sources":["../../src/font-creation-card.ts"],"names":[],"mappings":"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","sourcesContent":["import './ox-file-selector.js'\n\nimport { LitElement, css, html } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\nimport { i18next, localize } from '@operato/i18n'\n\n@customElement('font-creation-card')\nexport class FontCreationCard extends localize(i18next)(LitElement) {\n static styles = [\n css`\n /*\n * The first card in the list: an offer on its front, the short form on its back. Both faces\n * take the list's card shape; the front is dashed because nothing is there yet.\n */\n :host {\n position: relative;\n\n padding: 0;\n margin: 0;\n height: 100%;\n\n -webkit-transform-style: preserve-3d;\n transform-style: preserve-3d;\n -webkit-transition: all 0.5s ease-in-out;\n transition: all 0.5s ease-in-out;\n }\n\n :host(.flipped) {\n -webkit-transform: var(--card-list-flip-transform);\n transform: var(--card-list-flip-transform);\n }\n\n [front],\n [back] {\n position: absolute;\n\n width: 100%;\n height: 100%;\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n border-radius: 12px;\n\n color: var(--md-sys-color-on-surface);\n\n -webkit-backface-visibility: hidden;\n backface-visibility: hidden;\n }\n\n [front] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 6px;\n\n border: 1px dashed var(--md-sys-color-outline-variant);\n background: transparent;\n color: var(--md-sys-color-on-surface-variant);\n text-align: center;\n font-size: 0.82rem;\n text-transform: capitalize;\n cursor: pointer;\n }\n\n [front]:hover {\n border-color: var(--md-sys-color-outline);\n color: var(--md-sys-color-on-surface);\n background: var(--md-sys-color-surface-container-low);\n }\n\n [front] md-icon {\n --md-icon-size: 30px;\n color: var(--md-sys-color-primary);\n font-variation-settings: 'wght' 300;\n }\n\n [back] {\n -webkit-transform: var(--card-list-flip-transform);\n transform: var(--card-list-flip-transform);\n display: grid;\n border: 1px solid var(--md-sys-color-outline-variant);\n background-color: var(--board-card-surface, var(--md-sys-color-surface-container-lowest));\n }\n\n [back] form {\n padding: 14px 16px;\n width: 100%;\n height: 100%;\n box-sizing: border-box;\n display: grid;\n grid-template-columns: 1fr;\n grid-template-rows: 1fr auto;\n gap: 10px;\n align-items: start;\n }\n\n [back] form .props {\n width: 100%;\n box-sizing: border-box;\n display: grid;\n grid-template-columns: 4fr 6fr;\n gap: 6px 10px;\n align-items: center;\n }\n\n [back] form .props label {\n font-size: 0.74rem;\n text-align: right;\n color: var(--md-sys-color-on-surface-variant);\n }\n\n [back] form .props label[hidden],\n [back] form .props input[hidden],\n [back] form .props select[hidden] {\n display: none;\n }\n\n input,\n select,\n ox-file-selector {\n color: var(--md-sys-color-on-surface);\n }\n\n [back] form .props input:not([type='checkbox']),\n [back] form .props select {\n min-width: 0;\n width: 100%;\n height: 26px;\n padding: 0 8px;\n box-sizing: border-box;\n border: 1px solid transparent;\n border-radius: 6px;\n background: var(--property-input-background, var(--md-sys-color-surface-container-high));\n font: inherit;\n font-size: 0.78rem;\n outline: none;\n }\n\n [back] form .props input:not([type='checkbox']):hover,\n [back] form .props select:hover {\n border-color: var(--md-sys-color-outline-variant);\n }\n\n [back] form .props input:not([type='checkbox']):focus,\n [back] form .props select:focus {\n border-color: var(--md-sys-color-primary);\n }\n\n [back] form .props input[type='checkbox'] {\n justify-self: start;\n margin: 0;\n accent-color: var(--md-sys-color-primary);\n }\n\n ox-file-selector {\n font-size: 0.78rem;\n border: none;\n box-sizing: border-box;\n padding: 0;\n min-width: 0;\n }\n\n [back] input[type='submit'] {\n justify-self: end;\n height: 28px;\n padding: 0 16px;\n border: 0;\n border-radius: 999px;\n background: var(--md-sys-color-primary);\n color: var(--md-sys-color-on-primary);\n font: inherit;\n font-size: 0.78rem;\n font-weight: 500;\n cursor: pointer;\n }\n\n [back] input[type='submit']:hover {\n filter: brightness(1.08);\n }\n\n .hidden {\n display: none !important;\n }\n `\n ]\n\n @property({ type: String }) provider: string = 'google'\n @property({ type: Array }) googleFonts: Array<string> = []\n @property({ type: Array }) files?: Array<any>\n\n providers: Array<{ value: string; display: string }> = [\n { value: 'google', display: 'Google' },\n // TODO 구글 외 폰트 서비스 구현\n // { value: 'typekit', display: 'Typekit' },\n { value: 'custom', display: 'Custom' }\n ]\n\n render() {\n let isProviderGoogle = this.provider == 'google' && this.googleFonts.length > 0\n let isFileAttached = this.files && this.files.length > 0 ? true : false\n\n return html`\n <div @click=${(e: Event) => this.onClickFlip(e)} front>\n <md-icon>add</md-icon>${i18next.t('button.create font', { defaultValue: 'Create font' })}\n </div>\n\n <div @click=${(e: Event) => this.onClickFlip(e)} back>\n <form @submit=${(e: Event) => this.onClickSubmit(e)}>\n <div class=\"props\">\n <label>${i18next.t('label.provider')}</label>\n <select\n name=\"provider\"\n @change=${(e: Event) => {\n this.provider = (e.target as HTMLInputElement).value\n if (this.provider === 'google') {\n fetch(`/all-google-fonts`).then(async response => {\n if (response.ok) this.googleFonts = await response.json()\n else {\n console.warn(\n `(${response.url}) ${response.status} ${response.statusText}. Could not load Google fonts.`\n )\n }\n })\n }\n }}\n >\n ${this.providers.map(\n p => html` <option value=${p.value} ?selected=${this.provider == p.value}>${p.display}</option> `\n )}\n </select>\n\n <label>${i18next.t('label.name')}</label>\n <input type=\"text\" name=\"${isProviderGoogle ? '' : 'name'}\" ?hidden=${isProviderGoogle} />\n <select name=\"${isProviderGoogle ? 'name' : ''}\" ?hidden=${!isProviderGoogle}>\n <option value=\"\">&nbsp;</option>\n ${isProviderGoogle && this.googleFonts.map(f => html` <option value=${f}>${f}</option> `)}\n </select>\n\n <label ?hidden=${this.provider != 'custom'}>${i18next.t('label.uri')}</label>\n <input\n ?hidden=${this.provider != 'custom'}\n ?disabled=${isFileAttached}\n .value=${isFileAttached ? this.files![0].name : ''}\n type=\"text\"\n name=\"uri\"\n />\n <!-- display when attachment module is imported -->\n <label ?hidden=${this.provider != 'custom'}>${i18next.t('label.file')}</label>\n <ox-file-selector\n class=\"${this.provider != 'custom' ? 'hidden' : ''}\"\n name=\"file\"\n label=\"${i18next.t('label.select file')}\"\n accept=\".ttf,.otf,.woff,.woff2,.eot,.svg,.svgz\"\n multiple\n @file-change=${(e: CustomEvent) => {\n this.files = Array.from(e.detail.files)\n }}\n ></ox-file-selector>\n <!------------------------------------------------>\n\n <label for=\"checkbox-active\" @click=${(e: Event) => e.stopPropagation()}>\n ${i18next.t('label.active')}\n </label>\n <input id=\"checkbox-active\" type=\"checkbox\" name=\"active\" checked />\n </div>\n <input type=\"submit\" value=${i18next.t('button.create')} />\n </form>\n </div>\n `\n }\n\n onClickFlip(e: Event) {\n const target = e.target as HTMLElement\n const currentTarget = e.currentTarget as HTMLElement\n\n if (!['INPUT', 'SELECT', 'OPTION'].find(tagName => tagName === target.tagName)) {\n if (currentTarget.hasAttribute('front')) this.reset() // 입력 폼으로 뒤집기 전에 한 번 리셋\n this.classList.toggle('flipped')\n }\n }\n\n async onClickSubmit(e: Event) {\n e.preventDefault()\n e.stopPropagation()\n\n var form = e.target as HTMLFormElement\n\n var detail = {} as any\n\n detail.name = (form.elements.namedItem('name') as HTMLInputElement).value\n detail.provider = (form.elements.namedItem('provider') as HTMLInputElement).value\n detail.active = (form.elements.namedItem('active') as HTMLInputElement).checked\n\n if (this.provider === 'custom') {\n detail.uri = (form.elements.namedItem('uri') as HTMLInputElement).value\n if (this.files && this.files.length > 0) {\n detail.files = this.files\n }\n }\n\n this.dispatchEvent(new CustomEvent('create-font', { detail }))\n }\n\n reset() {\n var form = this.renderRoot.querySelector('form')\n if (form) {\n form.reset()\n }\n\n this.files = []\n this.classList.remove('flipped')\n }\n}\n"]}
@@ -1,6 +1,4 @@
1
1
  import '@material/web/icon/icon.js';
2
- import '@operato/input/ox-select.js';
3
- import '@operato/popup/ox-popup-list.js';
4
2
  import './font-creation-card.js';
5
3
  import { LitElement } from 'lit';
6
4
  declare const FontSelector_base: (new (...args: any[]) => LitElement) & typeof LitElement;
@@ -1,7 +1,5 @@
1
1
  import { __decorate } from "tslib";
2
2
  import '@material/web/icon/icon.js';
3
- import '@operato/input/ox-select.js';
4
- import '@operato/popup/ox-popup-list.js';
5
3
  import './font-creation-card.js';
6
4
  import { css, html, LitElement } from 'lit';
7
5
  import { customElement, property, query } from 'lit/decorators.js';
@@ -22,20 +20,23 @@ let FontSelector = class FontSelector extends localize(i18next)(LitElement) {
22
20
  }
23
21
  render() {
24
22
  var fonts = this._font.fonts || [];
23
+ const shown = fonts.filter(font => (this.provider ? font.provider === this.provider : true));
24
+ const providers = ['', ...Array.from(new Set(fonts.map(font => font.provider).filter(Boolean)))];
25
25
  return html `
26
26
  <div class="header">
27
27
  <div class="filters">
28
- <ox-select
29
- .placeholder=${i18next.t('text.please choose a provider')}
30
- .value=${this.provider}
31
- @change=${(e) => {
32
- this.provider = e.currentTarget.value;
28
+ ${providers.map(provider => html `
29
+ <button
30
+ class="chip"
31
+ ?active=${this.provider === provider}
32
+ @click=${() => {
33
+ this.provider = provider;
33
34
  }}
34
- >
35
- <ox-popup-list>
36
- ${['', 'google', 'custom'].map(provider => html ` <div option value=${provider}>${provider}&nbsp;</div> `)}
37
- </ox-popup-list>
38
- </ox-select>
35
+ >
36
+ ${provider || i18next.t('label.all', { defaultValue: 'All' })}
37
+ </button>
38
+ `)}
39
+ <span class="count">${shown.length}</span>
39
40
  </div>
40
41
  </div>
41
42
 
@@ -48,37 +49,44 @@ let FontSelector = class FontSelector extends localize(i18next)(LitElement) {
48
49
  ></font-creation-card>
49
50
  `
50
51
  : html ``}
51
- ${fonts
52
- .filter(font => (this.provider ? font.provider === this.provider : true))
53
- .map(font => html `
54
- <div class="card" @click=${(e) => this.onClickSelect(font)}>
55
- <div face>
56
- <font .face=${font.name}>ABCDEFGHIJKLMNOPQRSTUVWXYZ</font>
57
- <br />
58
- <font .face=${font.name}>abcdefghijklmnopqrstuvwxyz</font>
59
- <br />
60
- <font .face=${font.name}>1234567890~!@#$%^&*</font>
61
- </div>
62
- <div name>${font.name}</div>
63
- <div provider>${font.provider}</div>
64
- <div class="button-container">
65
- <md-icon
66
- @click=${(e) => {
52
+ ${shown.map(font => html `
53
+ <div class="card" ?inactive=${!font.active} @click=${(e) => this.onClickSelect(font)}>
54
+ <div face style="font-family: '${font.name}'">
55
+ <span class="large">Aa Bb Cc</span>
56
+ <span class="line">ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz</span>
57
+ <span class="line">1234567890 ~!@#$%^&amp;*</span>
58
+ </div>
59
+ <div foot>
60
+ <span name title=${font.name}>${font.name}</span>
61
+ <span provider>${font.provider}</span>
62
+ </div>
63
+ <div class="button-container">
64
+ <md-icon
65
+ ?on=${font.active}
66
+ title=${font.active
67
+ ? i18next.t('text.font in use', { defaultValue: 'In use - click to stop offering it' })
68
+ : i18next.t('text.font not in use', { defaultValue: 'Not in use - click to offer it' })}
69
+ @click=${(e) => {
67
70
  e.stopPropagation();
68
71
  this.toggleActive(font);
69
72
  }}
70
- >${font.active ? 'check_box' : 'check_box_outline_blank'}</md-icon
71
- >
72
- <md-icon
73
- @click=${(e) => {
73
+ >${font.active ? 'check_circle' : 'radio_button_unchecked'}</md-icon
74
+ >
75
+ <md-icon
76
+ class="remove"
77
+ title=${i18next.t('button.delete', { defaultValue: 'Delete' })}
78
+ @click=${(e) => {
74
79
  e.stopPropagation();
75
80
  this.deleteFont(font);
76
81
  }}
77
- >delete</md-icon
78
- >
79
- </div>
82
+ >delete</md-icon
83
+ >
80
84
  </div>
81
- `)}
85
+ </div>
86
+ `)}
87
+ ${!shown.length && !this.creatable
88
+ ? html `<div empty>${i18next.t('text.no fonts', { defaultValue: 'No fonts yet.' })}</div>`
89
+ : ''}
82
90
  </div>
83
91
  `;
84
92
  }
@@ -143,6 +151,12 @@ FontSelector.styles = [
143
151
  ScrollbarStyles,
144
152
  CommonHeaderStyles,
145
153
  css `
154
+ /*
155
+ * A font is shown by what it looks like, so the card is mostly specimen: a big "Aa" in the
156
+ * face, the alphabet under it, and the name at the foot. No coloured bars - the name and the
157
+ * provider are plain text on the card - and the two actions (in use / delete) are quiet
158
+ * glyphs in the corner until the pointer reaches them.
159
+ */
146
160
  :host {
147
161
  display: flex;
148
162
  flex-direction: column;
@@ -153,89 +167,180 @@ FontSelector.styles = [
153
167
  position: relative;
154
168
  }
155
169
 
170
+ .filters {
171
+ display: flex;
172
+ align-items: center;
173
+ gap: 6px;
174
+ flex-wrap: wrap;
175
+ }
176
+
177
+ /* Provider filter, as the same chips the rest of the product filters with. */
178
+ .filters button.chip {
179
+ height: 28px;
180
+ padding: 0 12px;
181
+ border: 1px solid var(--md-sys-color-outline-variant);
182
+ border-radius: 999px;
183
+ background: transparent;
184
+ color: var(--md-sys-color-on-surface-variant);
185
+ font: inherit;
186
+ font-size: 0.78rem;
187
+ text-transform: capitalize;
188
+ cursor: pointer;
189
+ }
190
+
191
+ .filters button.chip:hover {
192
+ background: var(--md-sys-color-surface-container-high);
193
+ color: var(--md-sys-color-on-surface);
194
+ }
195
+
196
+ .filters button.chip[active] {
197
+ border-color: transparent;
198
+ background: var(--md-sys-color-secondary-container);
199
+ color: var(--md-sys-color-on-secondary-container);
200
+ }
201
+
202
+ .filters .count {
203
+ margin-left: 6px;
204
+ font-size: 0.78rem;
205
+ color: var(--md-sys-color-on-surface-variant);
206
+ }
207
+
156
208
  #main {
157
209
  overflow: auto;
158
210
  display: grid;
159
- grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
160
- grid-auto-rows: var(--card-list-rows-height);
161
- grid-gap: 20px;
211
+ grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
212
+ grid-auto-rows: 172px;
213
+ gap: 16px;
162
214
  box-sizing: border-box;
163
- padding: var(--spacing-medium);
215
+ padding: 4px var(--spacing-medium) var(--spacing-medium);
164
216
  }
165
217
 
166
218
  #main .card {
167
219
  display: flex;
168
220
  flex-direction: column;
169
- align-items: center;
170
221
  overflow: hidden;
171
- border: 1px solid rgba(0, 0, 0, 0.2);
172
- border-radius: var(--md-sys-shape-corner-small);
222
+ border: 1px solid var(--md-sys-color-outline-variant);
223
+ border-radius: 12px;
173
224
  color: var(--md-sys-color-on-surface);
174
- background-color: var(--md-sys-color-surface);
225
+ background-color: var(--board-card-surface, var(--md-sys-color-surface-container-lowest));
226
+ transition: border-color 0.15s ease, box-shadow 0.15s ease;
175
227
 
176
228
  position: relative;
177
229
  }
178
230
 
231
+ #main .card:hover {
232
+ cursor: pointer;
233
+ border-color: var(--md-sys-color-outline);
234
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.08);
235
+ }
236
+
179
237
  #main .card.create {
180
238
  overflow: visible;
239
+ border: 0;
240
+ background: transparent;
241
+ box-shadow: none;
181
242
  }
182
243
 
183
- .card .button-container {
184
- position: absolute;
185
- right: 0;
186
- height: 100%;
244
+ /* Not in use: the specimen fades, the card stays in its place so the list does not jump. */
245
+ #main .card[inactive] [face] {
246
+ opacity: 0.38;
247
+ }
248
+
249
+ [face] {
250
+ flex: 1;
251
+ min-height: 0;
252
+ padding: 18px 18px 0;
253
+ overflow: hidden;
254
+ line-height: 1.15;
255
+ }
256
+
257
+ [face] .large {
258
+ display: block;
259
+ font-size: 2.1rem;
260
+ letter-spacing: 0.01em;
261
+ white-space: nowrap;
262
+ overflow: hidden;
263
+ text-overflow: ellipsis;
264
+ }
265
+
266
+ [face] .line {
267
+ display: block;
268
+ margin-top: 8px;
269
+ font-size: 0.82rem;
270
+ color: var(--md-sys-color-on-surface-variant);
271
+ white-space: nowrap;
272
+ overflow: hidden;
273
+ text-overflow: ellipsis;
274
+ }
275
+
276
+ [foot] {
187
277
  display: flex;
188
- direction: rtl;
189
- flex-direction: column;
190
- flex-wrap: wrap;
278
+ align-items: baseline;
279
+ gap: 8px;
280
+ padding: 10px 18px 14px;
281
+ min-width: 0;
191
282
  }
192
283
 
193
- .card .button-container > md-icon {
194
- text-align: center;
195
- width: var(--font-selector-icon-size, 35px);
196
- height: var(--font-selector-icon-size, 35px);
197
- color: var(--md-sys-color-on-primary-container);
198
- background-color: var(--md-sys-color-primary-container);
284
+ [name] {
285
+ flex: 1;
286
+ min-width: 0;
287
+ font-size: 0.86rem;
288
+ font-weight: 600;
289
+ color: var(--md-sys-color-on-surface);
290
+ white-space: nowrap;
291
+ overflow: hidden;
292
+ text-overflow: ellipsis;
199
293
  }
200
294
 
201
- .card .button-container > md-icon:last-child {
202
- border-bottom-left-radius: var(--md-sys-shape-corner-medium);
295
+ [provider] {
296
+ flex: none;
297
+ font-size: 0.68rem;
298
+ letter-spacing: 0.02em;
299
+ color: var(--md-sys-color-outline);
203
300
  }
204
301
 
205
- .card .button-container > md-icon:hover,
206
- .card .button-container > md-icon:active {
207
- color: var(--md-sys-color-on-secondary);
208
- background-color: var(--md-sys-color-secondary);
302
+ .card .button-container {
303
+ position: absolute;
304
+ top: 8px;
305
+ right: 8px;
306
+ display: flex;
307
+ gap: 2px;
209
308
  }
210
309
 
211
- #main .card:hover {
310
+ .card .button-container > md-icon {
311
+ --md-icon-size: 18px;
312
+ width: 28px;
313
+ height: 28px;
314
+ display: inline-flex;
315
+ align-items: center;
316
+ justify-content: center;
317
+ border-radius: 8px;
318
+ color: var(--md-sys-color-outline);
212
319
  cursor: pointer;
213
320
  }
214
321
 
215
- [face] {
216
- flex: 1;
322
+ .card .button-container > md-icon:hover {
323
+ color: var(--md-sys-color-on-surface);
324
+ background-color: var(--md-sys-color-surface-container-high);
217
325
  }
218
326
 
219
- [name] {
220
- color: var(--md-sys-color-on-tertiary);
221
- background-color: var(--md-sys-color-tertiary);
222
- opacity: 0.8;
223
- margin-top: -35px;
224
- width: 100%;
225
- color: #fff;
226
- font-weight: bolder;
227
- font-size: 13px;
228
- text-indent: 7px;
327
+ /* The in-use mark is the one thing in colour on the card. */
328
+ .card .button-container > md-icon[on] {
329
+ color: var(--md-sys-color-primary);
229
330
  }
230
331
 
231
- [provider] {
232
- color: var(--md-sys-color-on-secondary);
233
- background-color: var(--md-sys-color-secondary);
234
- width: 100%;
235
- min-height: 15px;
236
- font-size: 0.6rem;
237
- color: #fff;
238
- text-indent: 7px;
332
+ /* Not the shared [danger] fill: this one is quiet until hovered. */
333
+ .card .button-container > md-icon.remove:hover {
334
+ color: var(--md-sys-color-error);
335
+ background-color: color-mix(in srgb, var(--md-sys-color-error) 12%, transparent);
336
+ }
337
+
338
+ [empty] {
339
+ grid-column: 1 / -1;
340
+ padding: 48px 0;
341
+ text-align: center;
342
+ color: var(--md-sys-color-on-surface-variant);
343
+ font-size: 0.86rem;
239
344
  }
240
345
  `
241
346
  ];