@operato/font 1.0.0-beta.16 → 1.0.0-beta.17

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,15 @@
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
+ ## [1.0.0-beta.17](https://github.com/hatiolab/operato/compare/v1.0.0-beta.16...v1.0.0-beta.17) (2022-05-25)
7
+
8
+
9
+ ### :bug: Bug Fix
10
+
11
+ * reform font ui ([fcfa5b0](https://github.com/hatiolab/operato/commit/fcfa5b06222579e45a79fa94e8823178cfdacca2))
12
+
13
+
14
+
6
15
  ## [1.0.0-beta.16](https://github.com/hatiolab/operato/compare/v1.0.0-beta.15...v1.0.0-beta.16) (2022-05-23)
7
16
 
8
17
  **Note:** Version bump only for package @operato/font
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "description": "User interface to select font and create font.",
4
4
  "license": "MIT",
5
5
  "author": "heartyoh",
6
- "version": "1.0.0-beta.16",
6
+ "version": "1.0.0-beta.17",
7
7
  "main": "src/index.js",
8
8
  "module": "src/index.js",
9
9
  "exports": {
@@ -24,15 +24,48 @@
24
24
  "scripts": {},
25
25
  "dependencies": {
26
26
  "@material/mwc-icon": "^0.25.3",
27
- "@operato/attachment": "^1.0.0-beta.16",
28
- "@operato/headroom": "^1.0.0-beta.16",
29
- "@operato/i18n": "^1.0.0-beta.16",
30
- "@operato/layout": "^1.0.0-beta.16",
31
- "@operato/property-editor": "^1.0.0-beta.16",
32
- "@operato/pull-to-refresh": "^1.0.0-beta.16",
33
- "@operato/shell": "^1.0.0-beta.16",
34
- "@operato/utils": "^1.0.0-beta.16",
35
- "uuid": "^3.4.0"
27
+ "@operato/attachment": "^1.0.0-beta.17",
28
+ "@operato/headroom": "^1.0.0-beta.17",
29
+ "@operato/i18n": "^1.0.0-beta.17",
30
+ "@operato/layout": "^1.0.0-beta.17",
31
+ "@operato/property-editor": "^1.0.0-beta.17",
32
+ "@operato/pull-to-refresh": "^1.0.0-beta.17",
33
+ "@operato/shell": "^1.0.0-beta.17",
34
+ "@operato/utils": "^1.0.0-beta.17",
35
+ "uuid": "^3.4.0",
36
+ "webfontloader": "^1.6.28"
36
37
  },
37
- "gitHead": "20c4d72532166fd49c9c666e5ba00e4c5766a432"
38
+ "devDependencies": {
39
+ "@custom-elements-manifest/analyzer": "^0.4.17",
40
+ "@hatiolab/prettier-config": "^1.0.0",
41
+ "@open-wc/eslint-config": "^4.3.0",
42
+ "@open-wc/testing": "^3.0.4",
43
+ "@typescript-eslint/eslint-plugin": "^4.33.0",
44
+ "@typescript-eslint/parser": "^4.33.0",
45
+ "@web/dev-server": "^0.1.29",
46
+ "@web/dev-server-storybook": "^0.5.0",
47
+ "@web/test-runner": "next",
48
+ "concurrently": "^5.3.0",
49
+ "eslint": "^7.32.0",
50
+ "eslint-config-prettier": "^8.3.0",
51
+ "husky": "^7.0.2",
52
+ "json5": "^2.2.0",
53
+ "lint-staged": "^10.5.4",
54
+ "prettier": "^2.4.1",
55
+ "tslib": "^2.3.1",
56
+ "typescript": "^4.5.2"
57
+ },
58
+ "prettier": "@hatiolab/prettier-config",
59
+ "husky": {
60
+ "hooks": {
61
+ "pre-commit": "lint-staged"
62
+ }
63
+ },
64
+ "lint-staged": {
65
+ "*.ts": [
66
+ "eslint --fix",
67
+ "prettier --write"
68
+ ]
69
+ },
70
+ "gitHead": "4894bb50eb5bf25722360a7786fb142f2dcf9b97"
38
71
  }
@@ -1,35 +1,34 @@
1
- import "@operato/attachment";
1
+ import './ox-file-selector'
2
2
 
3
- import { LitElement, css, html } from "lit";
4
- import { i18next, localize } from "@operato/i18n";
3
+ import { css, html, LitElement } from 'lit'
5
4
 
6
- import { FileDropHelper } from "@operato/utils";
5
+ import { i18next, localize } from '@operato/i18n'
7
6
 
8
7
  export class FontCreationCard extends localize(i18next)(LitElement) {
9
8
  static get properties() {
10
9
  return {
11
10
  provider: {
12
- type: String,
11
+ type: String
13
12
  },
14
13
  googleFonts: {
15
- type: Array,
14
+ type: Array
16
15
  },
17
- _files: {
18
- type: Array,
19
- },
20
- };
16
+ files: {
17
+ type: Array
18
+ }
19
+ }
21
20
  }
22
21
 
23
22
  constructor() {
24
- super();
25
- this.provider = "google";
23
+ super()
24
+ this.provider = 'google'
26
25
  this.providers = [
27
- { value: "google", display: "Google" },
26
+ { value: 'google', display: 'Google' },
28
27
  // TODO 구글 외 폰트 서비스 구현
29
28
  // { value: 'typekit', display: 'Typekit' },
30
- { value: "custom", display: "Custom" },
31
- ];
32
- this.googleFonts = [];
29
+ { value: 'custom', display: 'Custom' }
30
+ ]
31
+ this.googleFonts = []
33
32
  }
34
33
 
35
34
  static get styles() {
@@ -48,12 +47,6 @@ export class FontCreationCard extends localize(i18next)(LitElement) {
48
47
  transition: all 0.5s ease-in-out;
49
48
  }
50
49
 
51
- :host(.candrop) [front],
52
- :host(.candrop) [back] {
53
- border-width: 2px;
54
- background-color: #fffde9;
55
- }
56
-
57
50
  :host(.flipped) {
58
51
  -webkit-transform: var(--card-list-flip-transform);
59
52
  transform: var(--card-list-flip-transform);
@@ -141,7 +134,7 @@ export class FontCreationCard extends localize(i18next)(LitElement) {
141
134
  width: -moz-available;
142
135
  }
143
136
 
144
- file-selector {
137
+ ox-file-selector {
145
138
  grid-column: span 6;
146
139
  font: var(--card-list-create-input-font);
147
140
  border: none;
@@ -149,7 +142,7 @@ export class FontCreationCard extends localize(i18next)(LitElement) {
149
142
  padding: 0;
150
143
  }
151
144
 
152
- [back] input[type="submit"] {
145
+ [back] input[type='submit'] {
153
146
  background-color: var(--button-background-color) !important;
154
147
  font: var(--button-font);
155
148
  color: var(--button-color) !important;
@@ -162,150 +155,116 @@ export class FontCreationCard extends localize(i18next)(LitElement) {
162
155
  .hidden {
163
156
  display: none !important;
164
157
  }
165
- `,
166
- ];
167
- }
168
-
169
- async firstUpdated() {
170
- FileDropHelper.set(this);
158
+ `
159
+ ]
171
160
  }
172
161
 
173
162
  render() {
174
- let isProviderGoogle =
175
- this.provider == "google" && this.googleFonts.length > 0;
176
- let isFileAttached = this._files.length > 0 ? true : false;
163
+ let isProviderGoogle = this.provider == 'google' && this.googleFonts.length > 0
164
+ let isFileAttached = this.files.length > 0 ? true : false
177
165
  return html`
178
- <div @click=${(e) => this.onClickFlip(e)} front>
179
- <mwc-icon>add_circle_outline</mwc-icon>create font
180
- </div>
166
+ <div @click=${e => this.onClickFlip(e)} front><mwc-icon>add_circle_outline</mwc-icon>create font</div>
181
167
 
182
- <div @click=${(e) => this.onClickFlip(e)} back>
183
- <form @submit=${(e) => this.onClickSubmit(e)}>
168
+ <div @click=${e => this.onClickFlip(e)} back>
169
+ <form @submit=${e => this.onClickSubmit(e)}>
184
170
  <div class="props">
185
- <label>${i18next.t("label.provider")}</label>
171
+ <label>${i18next.t('label.provider')}</label>
186
172
  <select
187
173
  name="provider"
188
- @change=${(e) => {
189
- this.provider = e.target.value;
190
- if (e.target.value === "google") {
191
- fetch(`/all-google-fonts`).then(async (response) => {
192
- if (response.ok) this.googleFonts = await response.json();
174
+ @change=${e => {
175
+ this.provider = e.target.value
176
+ if (e.target.value === 'google') {
177
+ fetch(`/all-google-fonts`).then(async response => {
178
+ if (response.ok) this.googleFonts = await response.json()
193
179
  else {
194
180
  console.warn(
195
181
  `(${response.url}) ${response.status} ${response.statusText}. Could not load Google fonts.`
196
- );
182
+ )
197
183
  }
198
- });
184
+ })
199
185
  }
200
186
  }}
201
187
  >
202
188
  ${this.providers.map(
203
- (p) =>
204
- html`
205
- <option
206
- value=${p.value}
207
- ?selected=${this.provider == p.value}
208
- >
209
- ${p.display}
210
- </option>
211
- `
189
+ p => html` <option value=${p.value} ?selected=${this.provider == p.value}>${p.display}</option> `
212
190
  )}
213
191
  </select>
214
192
 
215
- <label>${i18next.t("label.name")}</label>
216
- <input
217
- type="text"
218
- name="${isProviderGoogle ? "" : "name"}"
219
- ?hidden=${isProviderGoogle}
220
- />
221
- <select
222
- name="${isProviderGoogle ? "name" : ""}"
223
- ?hidden=${!isProviderGoogle}
224
- >
225
- ${isProviderGoogle &&
226
- this.googleFonts.map(
227
- (f) => html` <option value=${f}>${f}</option> `
228
- )}
193
+ <label>${i18next.t('label.name')}</label>
194
+ <input type="text" name="${isProviderGoogle ? '' : 'name'}" ?hidden=${isProviderGoogle} />
195
+ <select name="${isProviderGoogle ? 'name' : ''}" ?hidden=${!isProviderGoogle}>
196
+ <option value="">&nbsp;</option>
197
+ ${isProviderGoogle && this.googleFonts.map(f => html` <option value=${f}>${f}</option> `)}
229
198
  </select>
230
199
 
231
- <label ?hidden=${this.provider != "custom"}
232
- >${i18next.t("label.uri")}</label
233
- >
200
+ <label ?hidden=${this.provider != 'custom'}>${i18next.t('label.uri')}</label>
234
201
  <input
235
- ?hidden=${this.provider != "custom"}
202
+ ?hidden=${this.provider != 'custom'}
236
203
  ?disabled=${isFileAttached}
237
- .value=${isFileAttached ? this._files[0].name : ""}
204
+ .value=${isFileAttached ? this.files[0].name : ''}
238
205
  type="text"
239
206
  name="uri"
240
207
  />
241
208
  <!-- display when attachment module is imported -->
242
- <label ?hidden=${this.provider != "custom"}
243
- >${i18next.t("label.file")}</label
244
- >
245
- <file-selector
246
- class="${this.provider != "custom" ? "hidden" : ""}"
209
+ <label ?hidden=${this.provider != 'custom'}>${i18next.t('label.file')}</label>
210
+ <ox-file-selector
211
+ class="${this.provider != 'custom' ? 'hidden' : ''}"
247
212
  name="file"
248
- label="${i18next.t("label.select file")}"
213
+ label="${i18next.t('label.select file')}"
249
214
  accept=".ttf,.otf,.woff,.woff2,.eot,.svg,.svgz"
250
215
  multiple
251
- @file-change=${(e) => {
252
- this._files = Array.from(e.detail.files);
216
+ @file-change=${e => {
217
+ this.files = Array.from(e.detail.files)
253
218
  }}
254
- ></file-selector>
219
+ ></ox-file-selector>
255
220
  <!------------------------------------------------>
256
221
 
257
- <label for="checkbox-active" @click=${(e) => e.stopPropagation()}>
258
- ${i18next.t("label.active")}
259
- </label>
222
+ <label for="checkbox-active" @click=${e => e.stopPropagation()}> ${i18next.t('label.active')} </label>
260
223
  <input id="checkbox-active" type="checkbox" name="active" checked />
261
224
  </div>
262
225
  <div></div>
263
- <input type="submit" value=${i18next.t("button.create")} />
226
+ <input type="submit" value=${i18next.t('button.create')} />
264
227
  </form>
265
228
  </div>
266
- `;
229
+ `
267
230
  }
268
231
 
269
232
  onClickFlip(e) {
270
- if (
271
- !["INPUT", "SELECT", "OPTION"].find(
272
- (tagName) => tagName === e.target.tagName
273
- )
274
- ) {
275
- if (e.currentTarget.hasAttribute("front")) this.reset(); // 입력 폼으로 뒤집기 전에 한 번 리셋
276
- this.classList.toggle("flipped");
233
+ if (!['INPUT', 'SELECT', 'OPTION'].find(tagName => tagName === e.target.tagName)) {
234
+ if (e.currentTarget.hasAttribute('front')) this.reset() // 입력 폼으로 뒤집기 전에 한 번 리셋
235
+ this.classList.toggle('flipped')
277
236
  }
278
237
  }
279
238
 
280
239
  async onClickSubmit(e) {
281
- e.preventDefault();
282
- e.stopPropagation();
283
-
284
- var form = e.target;
285
-
286
- var detail = {};
287
- detail.name = form.elements["name"].value;
288
- detail.provider = form.elements["provider"].value;
289
- detail.active = form.elements["active"].checked;
290
- if (this.provider === "custom") {
291
- detail.uri = form.elements["uri"].value;
292
- if (this._files?.length > 0) {
293
- detail._files = this._files;
240
+ e.preventDefault()
241
+ e.stopPropagation()
242
+
243
+ var form = e.target
244
+
245
+ var detail = {}
246
+ detail.name = form.elements['name'].value
247
+ detail.provider = form.elements['provider'].value
248
+ detail.active = form.elements['active'].checked
249
+ if (this.provider === 'custom') {
250
+ detail.uri = form.elements['uri'].value
251
+ if (this.files?.length > 0) {
252
+ detail.files = this.files
294
253
  }
295
254
  }
296
255
 
297
- this.dispatchEvent(new CustomEvent("create-font", { detail }));
256
+ this.dispatchEvent(new CustomEvent('create-font', { detail }))
298
257
  }
299
258
 
300
259
  reset() {
301
- var form = this.shadowRoot.querySelector("form");
260
+ var form = this.shadowRoot.querySelector('form')
302
261
  if (form) {
303
- form.reset();
262
+ form.reset()
304
263
  }
305
264
 
306
- this._files = [];
307
- this.classList.remove("flipped");
265
+ this.files = []
266
+ this.classList.remove('flipped')
308
267
  }
309
268
  }
310
269
 
311
- customElements.define("font-creation-card", FontCreationCard);
270
+ customElements.define('font-creation-card', FontCreationCard)
@@ -1,26 +1,18 @@
1
- import "./font-creation-card";
2
-
3
- import { HeadroomStyles, ScrollbarStyles } from "@operato/styles";
4
- import { LitElement, css, html } from "lit";
5
- import {
6
- createFont,
7
- deleteFont,
8
- fetchFontList,
9
- updateFont,
10
- } from "./graphql-client";
11
- import { i18next, localize } from "@operato/i18n";
12
-
13
- import Headroom from "@operato/headroom";
14
- import { client } from "@operato/graphql";
15
- import { connect } from "pwa-helpers/connect-mixin.js";
16
- import gql from "graphql-tag";
17
- import { pulltorefresh } from "@operato/pull-to-refresh";
18
- import { store } from "@operato/shell";
19
- import uuid from "uuid/v4";
20
-
21
- export class FontSelector extends localize(i18next)(
22
- connect(store)(LitElement)
23
- ) {
1
+ import './font-creation-card'
2
+
3
+ import { css, html, LitElement } from 'lit'
4
+ import { connect } from 'pwa-helpers/connect-mixin.js'
5
+
6
+ import Headroom from '@operato/headroom'
7
+ import { i18next, localize } from '@operato/i18n'
8
+ import { pulltorefresh } from '@operato/pull-to-refresh'
9
+ import { store } from '@operato/shell'
10
+ import { HeadroomStyles, ScrollbarStyles } from '@operato/styles'
11
+
12
+ import { createFont, deleteFont, updateFont } from './graphql-client'
13
+ import { actionUpdateFontList } from './redux-font-actions'
14
+
15
+ export class FontSelector extends localize(i18next)(connect(store)(LitElement)) {
24
16
  static get styles() {
25
17
  return [
26
18
  ScrollbarStyles,
@@ -35,11 +27,6 @@ export class FontSelector extends localize(i18next)(
35
27
  position: relative;
36
28
  }
37
29
 
38
- :host(.candrop) {
39
- background: orange;
40
- cursor: pointer;
41
- }
42
-
43
30
  #main {
44
31
  overflow: auto;
45
32
  padding: var(--popup-content-padding);
@@ -143,8 +130,8 @@ export class FontSelector extends localize(i18next)(
143
130
  text-transform: capitalize;
144
131
  float: right;
145
132
  }
146
- `,
147
- ];
133
+ `
134
+ ]
148
135
  }
149
136
 
150
137
  static get properties() {
@@ -152,43 +139,35 @@ export class FontSelector extends localize(i18next)(
152
139
  fonts: Array,
153
140
  _page: Number,
154
141
  _total: Number,
155
- creatable: Boolean,
156
- };
142
+ creatable: Boolean
143
+ }
157
144
  }
158
145
 
159
146
  render() {
160
- var fonts = this.fonts || [];
147
+ var fonts = this.fonts || []
161
148
 
162
149
  return html`
163
150
  <div id="filter">
164
151
  <select
165
- @change=${(e) => {
166
- this.provider = e.currentTarget.value;
167
- this.requestUpdate();
152
+ @change=${e => {
153
+ this.provider = e.currentTarget.value
154
+ this.requestUpdate()
168
155
  }}
169
156
  >
170
- <option value="">
171
- --${i18next.t("text.please choose a provider")}--
172
- </option>
173
- ${["google", "custom"].map(
174
- (provider) => html` <option value=${provider}>${provider}</option> `
175
- )}
157
+ <option value="">--${i18next.t('text.please choose a provider')}--</option>
158
+ ${['google', 'custom'].map(provider => html` <option value=${provider}>${provider}</option> `)}
176
159
  </select>
177
160
  </div>
178
161
 
179
162
  <div id="main">
180
163
  ${this.creatable
181
164
  ? html`
182
- <font-creation-card
183
- class="card create"
184
- @create-font=${(e) => this.onCreateFont(e)}
185
- @file-drop=${(e) => this.onAttachmentDropped(e)}
186
- ></font-creation-card>
165
+ <font-creation-card class="card create" @create-font=${e => this.onCreateFont(e)}></font-creation-card>
187
166
  `
188
167
  : html``}
189
168
  ${fonts.map(
190
- (font) => html`
191
- <div class="card" @click=${(e) => this.onClickSelect(font)}>
169
+ font => html`
170
+ <div class="card" @click=${e => this.onClickSelect(font)}>
192
171
  <div face>
193
172
  <font .face=${font.name}>ABCDEFGHIJKLMN</font>
194
173
  <font .face=${font.name}>abcdefghijklmn</font>
@@ -197,18 +176,16 @@ export class FontSelector extends localize(i18next)(
197
176
  <div provider>${font.provider}</div>
198
177
  <div class="button-container">
199
178
  <mwc-icon
200
- @click=${(e) => {
201
- e.stopPropagation();
202
- this.toggleActive(font);
179
+ @click=${e => {
180
+ e.stopPropagation()
181
+ this.toggleActive(font)
203
182
  }}
204
- >${font.active
205
- ? "check_box"
206
- : "check_box_outline_blank"}</mwc-icon
183
+ >${font.active ? 'check_box' : 'check_box_outline_blank'}</mwc-icon
207
184
  >
208
185
  <mwc-icon
209
- @click=${(e) => {
210
- e.stopPropagation();
211
- this.deleteFont(font);
186
+ @click=${e => {
187
+ e.stopPropagation()
188
+ this.deleteFont(font)
212
189
  }}
213
190
  >delete</mwc-icon
214
191
  >
@@ -217,249 +194,94 @@ export class FontSelector extends localize(i18next)(
217
194
  `
218
195
  )}
219
196
  </div>
220
- `;
197
+ `
221
198
  }
222
199
 
223
200
  async firstUpdated() {
224
- var list = this.shadowRoot.querySelector("#main");
201
+ var list = this.shadowRoot.querySelector('#main')
225
202
 
226
203
  pulltorefresh({
227
204
  container: this.shadowRoot,
228
205
  scrollable: list,
229
206
  refresh: () => {
230
- return this.refresh();
231
- },
232
- });
207
+ return this.refresh()
208
+ }
209
+ })
233
210
 
234
211
  /* for headroom */
235
- var main = this.renderRoot.querySelector("#main");
236
- main.addEventListener("scroll", (e) => {
237
- this.showGotoTop = e.target.scrollTop !== 0;
238
- });
212
+ var main = this.renderRoot.querySelector('#main')
213
+ main.addEventListener('scroll', e => {
214
+ this.showGotoTop = e.target.scrollTop !== 0
215
+ })
239
216
 
240
- var filter = this.renderRoot.querySelector("#filter");
217
+ var filter = this.renderRoot.querySelector('#filter')
241
218
 
242
- await this.requestUpdate();
219
+ await this.requestUpdate()
243
220
 
244
- var originPaddingTop = parseFloat(
245
- getComputedStyle(main, null).getPropertyValue("padding-top")
246
- );
247
- main.style.paddingTop = filter.clientHeight + originPaddingTop + "px";
221
+ var originPaddingTop = parseFloat(getComputedStyle(main, null).getPropertyValue('padding-top'))
222
+ main.style.paddingTop = filter.clientHeight + originPaddingTop + 'px'
248
223
  var headroom = new Headroom(filter, {
249
- scroller: main,
250
- });
251
- headroom.init();
224
+ scroller: main
225
+ })
226
+ headroom.init()
252
227
  }
253
228
 
254
229
  get creationCard() {
255
- return this.shadowRoot.querySelector("font-creation-card");
230
+ return this.shadowRoot.querySelector('font-creation-card')
256
231
  }
257
232
 
258
233
  updated(changes) {
259
- if (changes.has("fonts")) {
260
- var creationCard = this.creationCard;
261
- if (creationCard) {
262
- creationCard.reset();
263
- }
234
+ if (changes.has('fonts')) {
235
+ this.creationCard?.reset()
264
236
  }
265
237
  }
266
238
 
267
239
  stateChanged(state) {
268
- this.fonts = state.font;
240
+ this.fonts = state.font
269
241
  }
270
242
 
271
243
  refresh() {
272
- return store.dispatch(fetchFontList());
273
- }
274
-
275
- toggleActive(font) {
276
- store.dispatch(updateFont({ id: font.id, active: !font.active }));
244
+ return store.dispatch(actionUpdateFontList())
277
245
  }
278
246
 
279
- onCreateFont(e) {
280
- var font = e.detail;
281
- this.createFont(font);
282
- }
283
-
284
- async createFont(font) {
285
- if (font._files?.length > 0) {
286
- let attachment = await this.attachFile(font._files[0], [
287
- "fullpath",
288
- "refBy",
289
- ]);
290
-
291
- //font.id = attachment?.refBy
292
- font.uri = attachment?.fullpath;
293
- delete font._files;
247
+ async toggleActive(font) {
248
+ try {
249
+ await updateFont({ id: font.id, active: !font.active })
250
+ this.refresh()
251
+ } catch (e) {
252
+ console.error(e)
294
253
  }
295
-
296
- store.dispatch(createFont(font));
297
254
  }
298
255
 
299
- async onAttachmentDropped(e) {
300
- var isNonFontIncluded = false;
301
- var files = e.detail.filter((file) => {
302
- var isFontFormat = !![
303
- ".woff",
304
- ".woff2",
305
- ".eot",
306
- ".svg",
307
- ".svgz",
308
- ".ttf",
309
- ".otf",
310
- ].find((ext) => file.name.endsWith(ext));
311
- if (!isFontFormat) {
312
- isNonFontIncluded = true;
313
- return false;
314
- }
315
- var alreadyExist = !!this.fonts.find(
316
- (font) =>
317
- font.name == file.name.replace(/\.[^/.]+$/, "").replace(".", "_")
318
- );
319
- if (alreadyExist) return false;
320
- return true;
321
- });
322
- // TODO alert if non-font file is included. ex) Non-font file is excluded in upload list.
323
-
324
- if (files.length > 0) {
325
- var attached = await this.attachFiles(files, [
326
- "name",
327
- "fullpath",
328
- "refBy",
329
- ]);
330
- attached.forEach((attachment) => {
331
- this.createFont({
332
- id: attachment.refBy,
333
- name: attachment.name.replace(/\.[^/.]+$/, "").replace(".", "_"), // cannot apply font correctly if '.' exists in name
334
- provider: "custom",
335
- active: true,
336
- uri: attachment.fullpath,
337
- });
338
- });
256
+ async onCreateFont(e) {
257
+ try {
258
+ await createFont(e.detail)
259
+ this.refresh()
260
+ } catch (e) {
261
+ console.error(e)
339
262
  }
340
263
  }
341
264
 
342
- /**
343
- * attach a file
344
- *
345
- * @param { File } file file
346
- * @param { Array<String> } fields fields to select from return
347
- */
348
- async attachFile(file, fields = []) {
349
- var attaching = await client.mutate({
350
- mutation: gql`
351
- mutation ($attachment: NewAttachment!) {
352
- createAttachment(attachment: $attachment) {
353
- id
354
- }
355
- }
356
- `,
357
- variables: {
358
- attachment: { refBy: uuid(), category: "font", file },
359
- },
360
- context: {
361
- hasUpload: true,
362
- },
363
- });
364
- // TODO mutation 이후 query 호출 안 해도 되도록 수정
365
- // fullpath 값은 getter라서 그런지 뮤테이션에서 못 받아오는 듯
366
- var attached = await client.query({
367
- query: gql`
368
- query($id: String!) {
369
- attachment(id: $id) {
370
- id
371
- ${fields.join("\n")}
372
- }
373
- }
374
- `,
375
- variables: {
376
- id: attaching.data.createAttachment?.id,
377
- },
378
- });
379
- return attached.data.attachment;
380
- }
381
-
382
- /**
383
- * attach multiple files
384
- *
385
- * @param { Array<File> } files files
386
- * @param { Array<String> } fields fields to select from return
387
- */
388
- async attachFiles(files, fields = []) {
389
- var attaching = await client.mutate({
390
- mutation: gql`
391
- mutation ($attachments: [NewAttachment!]!) {
392
- createAttachments(attachments: $attachments) {
393
- id
394
- }
395
- }
396
- `,
397
- variables: {
398
- attachments: files.map((file) => ({
399
- refBy: uuid(),
400
- category: "",
401
- file,
402
- })),
403
- },
404
- context: {
405
- hasUpload: true,
406
- },
407
- });
408
- // TODO mutation 이후 query 호출 안 해도 되도록 수정
409
- // fullpath 값은 getter라서 그런지 뮤테이션에서 못 받아오는 듯
410
- var attached = await client.query({
411
- query: gql`
412
- query($filters: [Filter]) {
413
- attachments(filters: $filters) {
414
- items {
415
- id
416
- ${fields.join("\n")}
417
- }
418
- total
419
- }
420
- }
421
- `,
422
- variables: {
423
- filters: {
424
- name: "id",
425
- operator: "in",
426
- value: attaching.data.createAttachments.map(
427
- (attachment) => attachment.id
428
- ),
429
- },
430
- },
431
- });
432
- return attached.data.attachments.items;
433
- }
434
-
435
265
  async deleteFont(font) {
436
266
  try {
437
- client.mutate({
438
- mutation: gql`
439
- mutation ($refBys: [String!]!) {
440
- deleteAttachmentsByRef(refBys: $refBys)
441
- }
442
- `,
443
- variables: {
444
- refBys: [font.id],
445
- },
446
- });
447
- } catch (e) {}
448
-
449
- store.dispatch(deleteFont(font));
267
+ await deleteFont(font.id)
268
+ this.refresh()
269
+ } catch (e) {
270
+ console.error(e)
271
+ }
450
272
  }
451
273
 
452
274
  onClickSelect(font) {
453
275
  this.dispatchEvent(
454
- new CustomEvent("font-selected", {
276
+ new CustomEvent('font-selected', {
455
277
  composed: true,
456
278
  bubbles: true,
457
279
  detail: {
458
- font,
459
- },
280
+ font
281
+ }
460
282
  })
461
- );
283
+ )
462
284
  }
463
285
  }
464
286
 
465
- customElements.define("font-selector", FontSelector);
287
+ customElements.define('font-selector', FontSelector)
@@ -1,5 +1,6 @@
1
- import { client } from "@operato/graphql";
2
- import gql from "graphql-tag";
1
+ import gql from 'graphql-tag'
2
+
3
+ import { client } from '@operato/graphql'
3
4
 
4
5
  /**
5
6
  * @param {Object} listParam {filters, pagination, sortings}
@@ -7,11 +8,7 @@ import gql from "graphql-tag";
7
8
  export async function fetchFontList(listParam) {
8
9
  const response = await client.query({
9
10
  query: gql`
10
- query (
11
- $filters: [Filter!]
12
- $pagination: Pagination
13
- $sortings: [Sorting!]
14
- ) {
11
+ query ($filters: [Filter!], $pagination: Pagination, $sortings: [Sorting!]) {
15
12
  fonts(filters: $filters, pagination: $pagination, sortings: $sortings) {
16
13
  items {
17
14
  id
@@ -20,6 +17,10 @@ export async function fetchFontList(listParam) {
20
17
  uri
21
18
  path
22
19
  active
20
+ files {
21
+ name
22
+ fullpath
23
+ }
23
24
  createdAt
24
25
  updatedAt
25
26
  }
@@ -27,10 +28,10 @@ export async function fetchFontList(listParam) {
27
28
  }
28
29
  }
29
30
  `,
30
- variables: listParam,
31
- });
31
+ variables: listParam
32
+ })
32
33
 
33
- return response.data && response.data.fonts;
34
+ return response.data && response.data.fonts
34
35
  }
35
36
 
36
37
  /**
@@ -52,18 +53,21 @@ export async function createFont(font) {
52
53
  }
53
54
  `,
54
55
  variables: {
55
- font: { active: false, ...font },
56
+ font: { active: false, ...font }
56
57
  },
57
- });
58
+ context: {
59
+ hasUpload: true
60
+ }
61
+ })
58
62
 
59
- return response.data;
63
+ return response.data
60
64
  }
61
65
 
62
66
  /**
63
67
  * @param {Object} font Font patch
64
68
  */
65
69
  export async function updateFont(font) {
66
- var { id, ...patch } = font;
70
+ var { id, ...patch } = font
67
71
 
68
72
  const response = await client.mutate({
69
73
  mutation: gql`
@@ -72,7 +76,10 @@ export async function updateFont(font) {
72
76
  id
73
77
  name
74
78
  provider
75
- uri
79
+ files {
80
+ name
81
+ fullpath
82
+ }
76
83
  path
77
84
  active
78
85
  createdAt
@@ -82,11 +89,14 @@ export async function updateFont(font) {
82
89
  `,
83
90
  variables: {
84
91
  id,
85
- patch,
92
+ patch
86
93
  },
87
- });
94
+ context: {
95
+ hasUpload: true
96
+ }
97
+ })
88
98
 
89
- return response.data;
99
+ return response.data
90
100
  }
91
101
 
92
102
  /**
@@ -100,9 +110,9 @@ export async function deleteFont(id) {
100
110
  }
101
111
  `,
102
112
  variables: {
103
- id,
104
- },
105
- });
113
+ id
114
+ }
115
+ })
106
116
 
107
- return response.data;
117
+ return response.data
108
118
  }
package/src/index.js CHANGED
@@ -1,3 +1,6 @@
1
- export * from "./font-selector";
2
- export * from "./ox-font-selector";
3
- export * from "./ox-property-editor-font-selector";
1
+ export * from './font-selector'
2
+ export * from './ox-font-selector'
3
+ export * from './ox-property-editor-font-selector'
4
+
5
+ export * from './redux-font-actions.js'
6
+ export { default as ReducerFont } from './redux-font-reducers.js'
@@ -0,0 +1,138 @@
1
+ import '@material/mwc-icon'
2
+ import '@material/mwc-icon-button'
3
+
4
+ import { css, html, LitElement } from 'lit'
5
+
6
+ export class OxFileSelector extends LitElement {
7
+ static get styles() {
8
+ return [
9
+ css`
10
+ :host {
11
+ flex: 1;
12
+ display: flex;
13
+ flex-direction: column;
14
+ position: relative;
15
+ overflow: hidden;
16
+ }
17
+
18
+ #input-box {
19
+ display: flex;
20
+ width: 100%;
21
+ height: 100%;
22
+ }
23
+
24
+ input[type='file'] {
25
+ position: absolute;
26
+ width: 0px;
27
+ height: 0px;
28
+ padding: 0;
29
+ margin: -1px;
30
+ overflow: hidden;
31
+ clip: rect(0, 0, 0, 0);
32
+ border: 0;
33
+ }
34
+
35
+ label {
36
+ padding: unset;
37
+ width: 100%;
38
+ height: 100%;
39
+ box-sizing: border-box;
40
+ display: flex;
41
+ align-items: center;
42
+ justify-content: center;
43
+
44
+ color: var(--file-selector-color, #999);
45
+ font-size: inherit;
46
+ line-height: normal;
47
+ vertical-align: middle;
48
+ background-color: var(--file-selector-bg-color, #fdfdfd);
49
+ cursor: pointer;
50
+ border: var(--file-selector-border, 1px solid #ebebeb);
51
+ border-radius: var(--file-selector-border-radius, 0.25em);
52
+ }
53
+
54
+ /* named upload */
55
+ .upload-name {
56
+ flex: 1;
57
+ padding: 0.5em 0.75em; /* label의 패딩값과 일치 */
58
+ font-size: inherit;
59
+ font-family: inherit;
60
+ line-height: normal;
61
+ vertical-align: middle;
62
+ background-color: #f5f5f5;
63
+ border: 1px solid #ebebeb;
64
+ border-bottom-color: #e2e2e2;
65
+ border-radius: 0.25em;
66
+ -webkit-appearance: none; /* 네이티브 외형 감추기 */
67
+ -moz-appearance: none;
68
+ appearance: none;
69
+ }
70
+ `
71
+ ]
72
+ }
73
+
74
+ static get properties() {
75
+ return {
76
+ label: String,
77
+ accept: String,
78
+ showFilename: {
79
+ type: Boolean,
80
+ attribute: 'show-filename'
81
+ },
82
+ multiple: {
83
+ type: Boolean,
84
+ attribute: 'multiple'
85
+ },
86
+ _files: Array
87
+ }
88
+ }
89
+
90
+ constructor() {
91
+ super()
92
+
93
+ this._files = []
94
+ this.label = 'select file'
95
+ this.multiple = false
96
+ }
97
+
98
+ render() {
99
+ return html`
100
+ <div id="input-box">
101
+ ${this.showFilename
102
+ ? html`
103
+ <input class="upload-name" value=${this._files.map(f => f.name).join(', ') || this.label} disabled />
104
+ `
105
+ : html``}
106
+ <label for="input-file">${this.label}</label>
107
+ <input
108
+ id="input-file"
109
+ type="file"
110
+ accept="${this.accept}"
111
+ class="upload-hidden"
112
+ ?multiple=${this.multiple}
113
+ hidden
114
+ @change=${e => {
115
+ const el = e.currentTarget
116
+ this.dispatchEvent(
117
+ new CustomEvent('file-change', {
118
+ bubbles: true,
119
+ composed: true,
120
+ detail: {
121
+ files: el.files
122
+ }
123
+ })
124
+ )
125
+
126
+ el.value = null
127
+ }}
128
+ />
129
+ </div>
130
+ `
131
+ }
132
+
133
+ get fileInput() {
134
+ return this.renderRoot.querySelector('#input-file')
135
+ }
136
+ }
137
+
138
+ window.customElements.define('ox-file-selector', OxFileSelector)
@@ -2,20 +2,20 @@
2
2
  * @license Copyright © HatioLab Inc. All rights reserved.
3
3
  */
4
4
 
5
- import "@material/mwc-icon";
6
- import "./font-selector";
5
+ import '@material/mwc-icon'
6
+ import './font-selector'
7
7
 
8
- import { LitElement, css, html } from "lit";
8
+ import { css, html, LitElement } from 'lit'
9
9
 
10
- import { i18next } from "@operato/i18n";
11
- import { openPopup } from "@operato/layout";
10
+ import { i18next } from '@operato/i18n'
11
+ import { openPopup } from '@operato/layout'
12
12
 
13
13
  export default class OxFontSelector extends LitElement {
14
14
  static get properties() {
15
15
  return {
16
16
  value: String,
17
- properties: Object,
18
- };
17
+ properties: Object
18
+ }
19
19
  }
20
20
 
21
21
  static get styles() {
@@ -26,7 +26,7 @@ export default class OxFontSelector extends LitElement {
26
26
  display: inline-block;
27
27
  }
28
28
 
29
- input[type="text"] {
29
+ input[type='text'] {
30
30
  box-sizing: border-box;
31
31
  width: 100%;
32
32
  height: 100%;
@@ -38,8 +38,8 @@ export default class OxFontSelector extends LitElement {
38
38
  top: 0;
39
39
  right: 0;
40
40
  }
41
- `,
42
- ];
41
+ `
42
+ ]
43
43
  }
44
44
 
45
45
  render() {
@@ -47,25 +47,23 @@ export default class OxFontSelector extends LitElement {
47
47
  <input
48
48
  id="text"
49
49
  type="text"
50
- .value=${this.value || ""}
51
- @change=${(e) => this._onInputChanged(e)}
52
- .placeholder=${this.getAttribute("placeholder") || ""}
50
+ .value=${this.value || ''}
51
+ @change=${e => this._onInputChanged(e)}
52
+ .placeholder=${this.getAttribute('placeholder') || ''}
53
53
  />
54
54
 
55
- <mwc-icon @click=${(e) => this.openSelector(e)}>font_download</mwc-icon>
56
- `;
55
+ <mwc-icon @click=${e => this.openSelector(e)}>font_download</mwc-icon>
56
+ `
57
57
  }
58
58
 
59
59
  _onInputChanged(e) {
60
- this.value = e.target.value;
61
- this.dispatchEvent(
62
- new CustomEvent("change", { bubbles: true, composed: true })
63
- );
60
+ this.value = e.target.value
61
+ this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true }))
64
62
  }
65
63
 
66
64
  openSelector() {
67
65
  if (this.popup) {
68
- delete this.popup;
66
+ delete this.popup
69
67
  }
70
68
 
71
69
  /*
@@ -73,30 +71,28 @@ export default class OxFontSelector extends LitElement {
73
71
  * 주의. value는 object일 수도 있고, string일 수도 있다.
74
72
  * string인 경우에는 해당 보드의 id로 해석한다.
75
73
  */
76
- var value = this.value || {};
74
+ var value = this.value || {}
77
75
 
78
76
  var template = html`
79
77
  <font-selector
80
78
  .creatable=${true}
81
- @font-selected=${async (e) => {
82
- var font = e.detail.font;
83
- this.value = font.name;
79
+ @font-selected=${async e => {
80
+ var font = e.detail.font
81
+ this.value = font.name
84
82
 
85
- this.dispatchEvent(
86
- new CustomEvent("change", { bubbles: true, composed: true })
87
- );
83
+ this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true }))
88
84
 
89
- this.popup && this.popup.close();
85
+ this.popup && this.popup.close()
90
86
  }}
91
87
  ></font-selector>
92
- `;
88
+ `
93
89
 
94
90
  this.popup = openPopup(template, {
95
91
  backdrop: true,
96
- size: "large",
97
- title: i18next.t("title.select font"),
98
- });
92
+ size: 'large',
93
+ title: i18next.t('title.select font')
94
+ })
99
95
  }
100
96
  }
101
97
 
102
- customElements.define("ox-font-selector", OxFontSelector);
98
+ customElements.define('ox-font-selector', OxFontSelector)
@@ -2,24 +2,16 @@
2
2
  * @license Copyright © HatioLab Inc. All rights reserved.
3
3
  */
4
4
 
5
- import "./ox-font-selector";
5
+ import './ox-font-selector'
6
6
 
7
- import { OxPropertyEditor } from "@operato/property-editor";
8
- import { html } from "lit";
7
+ import { html } from 'lit'
8
+
9
+ import { OxPropertyEditor } from '@operato/property-editor'
9
10
 
10
11
  export class OxPropertyEditorFontSelector extends OxPropertyEditor {
11
12
  editorTemplate() {
12
- return html`
13
- <ox-font-selector
14
- id="editor"
15
- .value=${this.value}
16
- .properties=${this.property}
17
- ></ox-font-selector>
18
- `;
13
+ return html` <ox-font-selector id="editor" .value=${this.value} .properties=${this.property}></ox-font-selector> `
19
14
  }
20
15
  }
21
16
 
22
- customElements.define(
23
- "ox-property-editor-font-selector",
24
- OxPropertyEditorFontSelector
25
- );
17
+ customElements.define('ox-property-editor-font-selector', OxPropertyEditorFontSelector)
@@ -0,0 +1,20 @@
1
+ import * as client from './graphql-client'
2
+
3
+ export const UPDATE_FONT_LIST = 'UPDATE_FONT_LIST'
4
+ export const CLEAR_FONT_LIST = 'CLEAR_FONT_LIST'
5
+
6
+ export const actionUpdateFontList = listParams => async dispatch => {
7
+ try {
8
+ const fonts = await client.fetchFontList(listParams || { filters: [] })
9
+
10
+ dispatch({
11
+ type: UPDATE_FONT_LIST,
12
+ list: fonts && fonts.items
13
+ })
14
+ } catch (error) {
15
+ console.error(error)
16
+ dispatch({
17
+ type: CLEAR_FONT_LIST
18
+ })
19
+ }
20
+ }
@@ -0,0 +1,80 @@
1
+ import WebFont from 'webfontloader'
2
+
3
+ import { CLEAR_FONT_LIST, UPDATE_FONT_LIST } from './redux-font-actions.js'
4
+
5
+ const reducerFont = (state = [], action) => {
6
+ switch (action.type) {
7
+ case UPDATE_FONT_LIST:
8
+ let newState = action.list
9
+ let activatedFonts = newState.filter(font => font.active)
10
+
11
+ let googles = []
12
+ let customs = []
13
+ let customFontCSS = ''
14
+
15
+ activatedFonts.forEach(font => {
16
+ const { name, provider, files, uri } = font
17
+
18
+ if (provider === 'google') {
19
+ googles.push(name)
20
+ } else if (provider === 'custom') {
21
+ customs.push(name)
22
+
23
+ if (files && files.length > 0) {
24
+ customFontCSS += files
25
+ .map(file => {
26
+ const { name: filename, fullpath } = file
27
+ const bold = filename.toUpperCase().indexOf('BOLD') !== -1
28
+
29
+ return `@font-face {
30
+ font-family: '${name}';
31
+ src: local('${name}'), url(${fullpath});
32
+ font-weight: ${bold ? 'bold' : 'normal'};
33
+ }
34
+ `
35
+ })
36
+ .join('\n')
37
+ } else {
38
+ customFontCSS += `@font-face {
39
+ font-family: '${name}';
40
+ src: local('${name}')${uri ? `, url(${uri})` : ''};
41
+ }
42
+ `
43
+ }
44
+ }
45
+ })
46
+
47
+ let style = document.head.querySelector('#custom-fonts')
48
+ if (!style) {
49
+ style = document.createElement('style')
50
+ style.id = 'custom-fonts'
51
+ style.type = 'text/css'
52
+ document.head.appendChild(style)
53
+ }
54
+ style.innerHTML = customFontCSS
55
+
56
+ // TODO: typekit 등 타 서비스 지원
57
+ let WebFontConfig = {}
58
+ if (googles.length) {
59
+ WebFontConfig.google = {
60
+ families: googles
61
+ }
62
+ }
63
+ if (customs.length) {
64
+ WebFontConfig.custom = {
65
+ families: customs
66
+ }
67
+ }
68
+ if (Object.keys(WebFontConfig).length) WebFont.load(WebFontConfig)
69
+
70
+ return newState
71
+
72
+ case CLEAR_FONT_LIST:
73
+ return []
74
+
75
+ default:
76
+ return state
77
+ }
78
+ }
79
+
80
+ export default reducerFont