@things-factory/board-ui 10.1.178 → 10.1.179

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,3 +1,4 @@
1
+ import { type UploadProgress } from '@operato/graphql';
1
2
  export interface AttachmentResult {
2
3
  id: string;
3
4
  name: string;
@@ -12,8 +13,9 @@ export interface AttachmentResult {
12
13
  * file 은 Browser File 객체. 결과로 새 Attachment id / 메타데이터 반환.
13
14
  *
14
15
  * board-import wizard 가 사용 — 이후 importBoardAsync(attachmentId=result.id) 로 변환 시작.
16
+ * progress 를 주면 올라간 정도(0‒1)를 알려 준다 — 선택기가 그것을 보인다.
15
17
  */
16
- export declare function createAttachmentForImport(file: File): Promise<AttachmentResult>;
18
+ export declare function createAttachmentForImport(file: File, progress?: UploadProgress): Promise<AttachmentResult>;
17
19
  /**
18
20
  * 기존 attachment 목록 조회 — wizard upload 스텝의 "기존 파일 선택" 탭에서 사용.
19
21
  * 도메인 스코프 내 attachment 만 반환되는 건 attachment-base resolver 가 보장.
@@ -6,14 +6,15 @@
6
6
  * → attachmentId 획득 → importBoardAsync 로 전달하는 짧은 흐름만 필요.
7
7
  */
8
8
  import gql from 'graphql-tag';
9
- import { client } from '@operato/graphql';
9
+ import { client, uploadContext } from '@operato/graphql';
10
10
  /**
11
11
  * 단일 파일을 Attachment 로 등록 — graphql-upload (Apollo client multipart) 사용.
12
12
  * file 은 Browser File 객체. 결과로 새 Attachment id / 메타데이터 반환.
13
13
  *
14
14
  * board-import wizard 가 사용 — 이후 importBoardAsync(attachmentId=result.id) 로 변환 시작.
15
+ * progress 를 주면 올라간 정도(0‒1)를 알려 준다 — 선택기가 그것을 보인다.
15
16
  */
16
- export async function createAttachmentForImport(file) {
17
+ export async function createAttachmentForImport(file, progress) {
17
18
  const response = await client.mutate({
18
19
  mutation: gql `
19
20
  mutation CreateAttachmentForImport($attachment: NewAttachment!) {
@@ -36,9 +37,7 @@ export async function createAttachmentForImport(file) {
36
37
  }
37
38
  },
38
39
  // multipart 업로드는 Apollo upload-link 가 처리. fetchPolicy 강제 무관.
39
- context: {
40
- hasUpload: true
41
- }
40
+ context: progress ? uploadContext(progress) : { hasUpload: true }
42
41
  });
43
42
  return response.data.createAttachment;
44
43
  }
@@ -1 +1 @@
1
- {"version":3,"file":"attachment.js","sourceRoot":"","sources":["../../client/graphql/attachment.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,GAAG,MAAM,aAAa,CAAA;AAE7B,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AAYzC;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAAC,IAAU;IACxD,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC;QACnC,QAAQ,EAAE,GAAG,CAAA;;;;;;;;;;;KAWZ;QACD,SAAS,EAAE;YACT,UAAU,EAAE;gBACV,IAAI;gBACJ,QAAQ,EAAE,QAAQ;gBAClB,OAAO,EAAE,cAAc;gBACvB,WAAW,EAAE,4BAA4B;aAC1C;SACF;QACD,6DAA6D;QAC7D,OAAO,EAAE;YACP,SAAS,EAAE,IAAI;SAChB;KACF,CAAC,CAAA;IACF,OAAO,QAAQ,CAAC,IAAI,CAAC,gBAAgB,CAAA;AACvC,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAAC,OAK/C;IACC,MAAM,OAAO,GAA6D,EAAE,CAAA;IAC5E,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;QACnB,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,OAAO,CAAC,MAAM,GAAG,EAAE,CAAC,CAAA;IAClF,CAAC;IACD,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;QAC3B,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC,cAAc,GAAG,EAAE,CAAC,CAAA;IAC7F,CAAC;IACD,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC;QAClC,KAAK,EAAE,GAAG,CAAA;;;;;;;;;;;;;;;KAeT;QACD,SAAS,EAAE;YACT,OAAO;YACP,UAAU,EAAE,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,EAAE,EAAE;YACnE,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;SAC9C;QACD,WAAW,EAAE,UAAU;KACxB,CAAC,CAAA;IACF,OAAO,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAA;AAClC,CAAC","sourcesContent":["/**\n * Attachment GraphQL operations — board-import wizard 가 도면 파일 업로드 시 사용.\n *\n * 백엔드: @things-factory/attachment-base 의 createAttachment mutation.\n * 별도 ox-attachment-list 의 흐름과 독립 — wizard 는 단순 file input → createAttachment\n * → attachmentId 획득 → importBoardAsync 로 전달하는 짧은 흐름만 필요.\n */\nimport gql from 'graphql-tag'\n\nimport { client } from '@operato/graphql'\n\nexport interface AttachmentResult {\n id: string\n name: string\n mimetype?: string\n size?: number\n path?: string\n category?: string\n createdAt?: string\n}\n\n/**\n * 단일 파일을 Attachment 로 등록 — graphql-upload (Apollo client multipart) 사용.\n * file 은 Browser File 객체. 결과로 새 Attachment id / 메타데이터 반환.\n *\n * board-import wizard 가 사용 — 이후 importBoardAsync(attachmentId=result.id) 로 변환 시작.\n */\nexport async function createAttachmentForImport(file: File): Promise<AttachmentResult> {\n const response = await client.mutate({\n mutation: gql`\n mutation CreateAttachmentForImport($attachment: NewAttachment!) {\n createAttachment(attachment: $attachment) {\n id\n name\n mimetype\n size\n path\n category\n }\n }\n `,\n variables: {\n attachment: {\n file,\n category: 'import',\n refType: 'board-import',\n description: 'board-import wizard upload'\n }\n },\n // multipart 업로드는 Apollo upload-link 가 처리. fetchPolicy 강제 무관.\n context: {\n hasUpload: true\n }\n })\n return response.data.createAttachment\n}\n\n/**\n * 기존 attachment 목록 조회 — wizard upload 스텝의 \"기존 파일 선택\" 탭에서 사용.\n * 도메인 스코프 내 attachment 만 반환되는 건 attachment-base resolver 가 보장.\n *\n * @param search 파일명 부분 일치 (선택)\n * @param mimetypePrefix 'image' | 'application' 등 — wizard 는 이미지/dxf 만 보여주려고 사용\n * @param page / limit 페이지네이션 (기본 1, 50)\n */\nexport async function fetchAttachmentsForImport(options: {\n search?: string\n mimetypePrefix?: string\n page?: number\n limit?: number\n}): Promise<{ items: AttachmentResult[]; total: number }> {\n const filters: Array<{ name: string; operator: string; value: string }> = []\n if (options.search) {\n filters.push({ name: 'name', operator: 'i_like', value: `%${options.search}%` })\n }\n if (options.mimetypePrefix) {\n filters.push({ name: 'mimetype', operator: 'i_like', value: `${options.mimetypePrefix}%` })\n }\n const response = await client.query({\n query: gql`\n query AttachmentsForImport($filters: [Filter!], $pagination: Pagination, $sortings: [Sorting!]) {\n attachments(filters: $filters, pagination: $pagination, sortings: $sortings) {\n items {\n id\n name\n mimetype\n size\n path\n category\n createdAt\n }\n total\n }\n }\n `,\n variables: {\n filters,\n pagination: { page: options.page ?? 1, limit: options.limit ?? 50 },\n sortings: [{ name: 'createdAt', desc: true }]\n },\n fetchPolicy: 'no-cache'\n })\n return response.data.attachments\n}\n"]}
1
+ {"version":3,"file":"attachment.js","sourceRoot":"","sources":["../../client/graphql/attachment.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,GAAG,MAAM,aAAa,CAAA;AAE7B,OAAO,EAAE,MAAM,EAAE,aAAa,EAAuB,MAAM,kBAAkB,CAAA;AAY7E;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAAC,IAAU,EAAE,QAAyB;IACnF,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC;QACnC,QAAQ,EAAE,GAAG,CAAA;;;;;;;;;;;KAWZ;QACD,SAAS,EAAE;YACT,UAAU,EAAE;gBACV,IAAI;gBACJ,QAAQ,EAAE,QAAQ;gBAClB,OAAO,EAAE,cAAc;gBACvB,WAAW,EAAE,4BAA4B;aAC1C;SACF;QACD,6DAA6D;QAC7D,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE;KAClE,CAAC,CAAA;IACF,OAAO,QAAQ,CAAC,IAAI,CAAC,gBAAgB,CAAA;AACvC,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAAC,OAK/C;IACC,MAAM,OAAO,GAA6D,EAAE,CAAA;IAC5E,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;QACnB,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,OAAO,CAAC,MAAM,GAAG,EAAE,CAAC,CAAA;IAClF,CAAC;IACD,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;QAC3B,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC,cAAc,GAAG,EAAE,CAAC,CAAA;IAC7F,CAAC;IACD,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC;QAClC,KAAK,EAAE,GAAG,CAAA;;;;;;;;;;;;;;;KAeT;QACD,SAAS,EAAE;YACT,OAAO;YACP,UAAU,EAAE,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,EAAE,EAAE;YACnE,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;SAC9C;QACD,WAAW,EAAE,UAAU;KACxB,CAAC,CAAA;IACF,OAAO,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAA;AAClC,CAAC","sourcesContent":["/**\n * Attachment GraphQL operations — board-import wizard 가 도면 파일 업로드 시 사용.\n *\n * 백엔드: @things-factory/attachment-base 의 createAttachment mutation.\n * 별도 ox-attachment-list 의 흐름과 독립 — wizard 는 단순 file input → createAttachment\n * → attachmentId 획득 → importBoardAsync 로 전달하는 짧은 흐름만 필요.\n */\nimport gql from 'graphql-tag'\n\nimport { client, uploadContext, type UploadProgress } from '@operato/graphql'\n\nexport interface AttachmentResult {\n id: string\n name: string\n mimetype?: string\n size?: number\n path?: string\n category?: string\n createdAt?: string\n}\n\n/**\n * 단일 파일을 Attachment 로 등록 — graphql-upload (Apollo client multipart) 사용.\n * file 은 Browser File 객체. 결과로 새 Attachment id / 메타데이터 반환.\n *\n * board-import wizard 가 사용 — 이후 importBoardAsync(attachmentId=result.id) 로 변환 시작.\n * progress 를 주면 올라간 정도(0‒1)를 알려 준다 — 선택기가 그것을 보인다.\n */\nexport async function createAttachmentForImport(file: File, progress?: UploadProgress): Promise<AttachmentResult> {\n const response = await client.mutate({\n mutation: gql`\n mutation CreateAttachmentForImport($attachment: NewAttachment!) {\n createAttachment(attachment: $attachment) {\n id\n name\n mimetype\n size\n path\n category\n }\n }\n `,\n variables: {\n attachment: {\n file,\n category: 'import',\n refType: 'board-import',\n description: 'board-import wizard upload'\n }\n },\n // multipart 업로드는 Apollo upload-link 가 처리. fetchPolicy 강제 무관.\n context: progress ? uploadContext(progress) : { hasUpload: true }\n })\n return response.data.createAttachment\n}\n\n/**\n * 기존 attachment 목록 조회 — wizard upload 스텝의 \"기존 파일 선택\" 탭에서 사용.\n * 도메인 스코프 내 attachment 만 반환되는 건 attachment-base resolver 가 보장.\n *\n * @param search 파일명 부분 일치 (선택)\n * @param mimetypePrefix 'image' | 'application' 등 — wizard 는 이미지/dxf 만 보여주려고 사용\n * @param page / limit 페이지네이션 (기본 1, 50)\n */\nexport async function fetchAttachmentsForImport(options: {\n search?: string\n mimetypePrefix?: string\n page?: number\n limit?: number\n}): Promise<{ items: AttachmentResult[]; total: number }> {\n const filters: Array<{ name: string; operator: string; value: string }> = []\n if (options.search) {\n filters.push({ name: 'name', operator: 'i_like', value: `%${options.search}%` })\n }\n if (options.mimetypePrefix) {\n filters.push({ name: 'mimetype', operator: 'i_like', value: `${options.mimetypePrefix}%` })\n }\n const response = await client.query({\n query: gql`\n query AttachmentsForImport($filters: [Filter!], $pagination: Pagination, $sortings: [Sorting!]) {\n attachments(filters: $filters, pagination: $pagination, sortings: $sortings) {\n items {\n id\n name\n mimetype\n size\n path\n category\n createdAt\n }\n total\n }\n }\n `,\n variables: {\n filters,\n pagination: { page: options.page ?? 1, limit: options.limit ?? 50 },\n sortings: [{ name: 'createdAt', desc: true }]\n },\n fetchPolicy: 'no-cache'\n })\n return response.data.attachments\n}\n"]}
@@ -7,6 +7,7 @@
7
7
  * 기존 파일 (existing) — 이미 업로드된 Attachment 에서 보드 생성.
8
8
  */
9
9
  import '@material/web/icon/icon.js';
10
+ import '@operato/input/ox-input-file.js';
10
11
  import { type PropertyValues } from 'lit';
11
12
  import { PageView } from '@operato/shell';
12
13
  import '@operato/board/ox-board-preview.js';
@@ -21,6 +22,7 @@ export declare class BoardCreateWizardPage extends BoardCreateWizardPage_base {
21
22
  private _session?;
22
23
  /** 현재 업로드 진행 중 — UI lock + spinner. */
23
24
  private _uploading;
25
+ private _uploadProgress?;
24
26
  /** 업로드 에러 메시지 — UI 표시 + 사용자가 다시 시도 가능. */
25
27
  private _uploadError?;
26
28
  /** upload 스텝 안의 모드 — 새 파일 업로드 vs 기존 attachment 선택. */
@@ -97,8 +99,6 @@ export declare class BoardCreateWizardPage extends BoardCreateWizardPage_base {
97
99
  */
98
100
  private _refreshBrowse;
99
101
  private _selectExisting;
100
- private _onFileChange;
101
- private _onDrop;
102
102
  private _uploadFile;
103
103
  private _resetUpload;
104
104
  private _proceedToImport;
@@ -8,6 +8,7 @@ import { __decorate, __metadata } from "tslib";
8
8
  * 기존 파일 (existing) — 이미 업로드된 Attachment 에서 보드 생성.
9
9
  */
10
10
  import '@material/web/icon/icon.js';
11
+ import '@operato/input/ox-input-file.js';
11
12
  import { css, html } from 'lit';
12
13
  import { customElement, state } from 'lit/decorators.js';
13
14
  import { i18next, localize } from '@operato/i18n';
@@ -186,25 +187,12 @@ let BoardCreateWizardPage = class BoardCreateWizardPage extends localize(i18next
186
187
  transition: border-color 0.15s, background 0.15s;
187
188
  }
188
189
  .drop-zone:hover,
189
- .drop-zone.dragover {
190
+ .drop-zone.candrop {
190
191
  border-color: var(--md-sys-color-primary, #2a64d8);
191
192
  background: var(--md-sys-color-primary-container, #eaf2ff);
192
193
  }
193
- .drop-zone .icon {
194
- font-size: 36px;
195
- --md-icon-size: 36px;
196
- opacity: 0.5;
197
- }
198
- .drop-zone .title {
199
- font-size: 15px;
200
- font-weight: 500;
201
- }
202
- .drop-zone .hint {
203
- font-size: 12px;
204
- color: var(--md-sys-color-on-secondary-container, #777);
205
- }
206
- input[type='file'] {
207
- display: none;
194
+ .drop-zone {
195
+ --file-uploader-icon-size: 36px;
208
196
  }
209
197
 
210
198
  .uploaded {
@@ -1128,28 +1116,15 @@ let BoardCreateWizardPage = class BoardCreateWizardPage extends localize(i18next
1128
1116
  }
1129
1117
  _renderNewUpload() {
1130
1118
  return html `
1131
- <label
1119
+ <ox-input-file
1132
1120
  class="drop-zone"
1133
- @dragover=${(e) => {
1134
- e.preventDefault();
1135
- e.currentTarget.classList.add('dragover');
1136
- }}
1137
- @dragleave=${(e) => {
1138
- ;
1139
- e.currentTarget.classList.remove('dragover');
1140
- }}
1141
- @drop=${this._onDrop}
1142
- >
1143
- <md-icon class="icon">upload</md-icon>
1144
- <div class="title">${this._uploading ? i18next.t('text.uploading') || 'Uploading...' : i18next.t('text.drop or click to upload') || 'Drop a drawing here or click to browse'}</div>
1145
- <div class="hint">${ACCEPT}</div>
1146
- <input
1147
- type="file"
1148
- accept=${ACCEPT}
1149
- ?disabled=${this._uploading}
1150
- @change=${this._onFileChange}
1151
- />
1152
- </label>
1121
+ accept=${ACCEPT}
1122
+ hide-filelist
1123
+ .description=${i18next.t('text.drop or click to upload') || 'Drop a drawing here or click to browse'}
1124
+ .progress=${this._uploadProgress}
1125
+ ?disabled=${this._uploading && this._uploadProgress === undefined}
1126
+ @change=${(e) => this._uploadFile(e.detail?.[0])}
1127
+ ></ox-input-file>
1153
1128
  `;
1154
1129
  }
1155
1130
  _renderBrowseExisting() {
@@ -1359,26 +1334,14 @@ let BoardCreateWizardPage = class BoardCreateWizardPage extends localize(i18next
1359
1334
  this._attachment = item;
1360
1335
  this._uploadError = undefined;
1361
1336
  }
1362
- async _onFileChange(e) {
1363
- const input = e.target;
1364
- const file = input.files?.[0];
1365
- if (file)
1366
- await this._uploadFile(file);
1367
- // value 초기화 — 같은 파일 재선택 가능
1368
- input.value = '';
1369
- }
1370
- async _onDrop(e) {
1371
- e.preventDefault();
1372
- e.currentTarget.classList.remove('dragover');
1373
- const file = e.dataTransfer?.files?.[0];
1374
- if (file)
1375
- await this._uploadFile(file);
1376
- }
1337
+ /* The picker (ox-input-file) has refused what the server would refuse; what it hands over goes up. */
1377
1338
  async _uploadFile(file) {
1339
+ if (!file || this._uploading)
1340
+ return;
1378
1341
  this._uploading = true;
1379
1342
  this._uploadError = undefined;
1380
1343
  try {
1381
- const result = await createAttachmentForImport(file);
1344
+ const result = await createAttachmentForImport(file, fraction => (this._uploadProgress = fraction));
1382
1345
  this._attachment = result;
1383
1346
  }
1384
1347
  catch (err) {
@@ -1388,6 +1351,8 @@ let BoardCreateWizardPage = class BoardCreateWizardPage extends localize(i18next
1388
1351
  }
1389
1352
  finally {
1390
1353
  this._uploading = false;
1354
+ this._uploadProgress = undefined;
1355
+ this.renderRoot.querySelector('ox-input-file')?.reset();
1391
1356
  }
1392
1357
  }
1393
1358
  _resetUpload() {
@@ -2061,6 +2026,10 @@ __decorate([
2061
2026
  state(),
2062
2027
  __metadata("design:type", Object)
2063
2028
  ], BoardCreateWizardPage.prototype, "_uploading", void 0);
2029
+ __decorate([
2030
+ state(),
2031
+ __metadata("design:type", Number)
2032
+ ], BoardCreateWizardPage.prototype, "_uploadProgress", void 0);
2064
2033
  __decorate([
2065
2034
  state(),
2066
2035
  __metadata("design:type", String)