@things-factory/board-ui 10.0.0-beta.26 → 10.0.0-beta.28

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.
@@ -10,8 +10,13 @@ export declare class BoardTileList extends LitElement {
10
10
  creatable?: boolean;
11
11
  searchText?: string;
12
12
  reorderable: boolean;
13
- private draggedItem;
13
+ updatedIds: string[];
14
+ private _draggedId;
15
+ private _dropTargetId;
16
+ private _dropSide;
14
17
  connectedCallback(): void;
18
+ private _clearDropIndicator;
19
+ private _resetDragState;
15
20
  render(): import("lit-html").TemplateResult<1>;
16
21
  updated(changes: PropertyValues<this>): void;
17
22
  onCreateBoard(e: any): void;
@@ -4,7 +4,7 @@ import '@operato/board/ox-board-creation-card.js';
4
4
  import gql from 'graphql-tag';
5
5
  import { css, html, LitElement, nothing } from 'lit';
6
6
  import { customElement, property } from 'lit/decorators.js';
7
- import { keyed } from 'lit/directives/keyed.js';
7
+ import { repeat } from 'lit/directives/repeat.js';
8
8
  import { client } from '@operato/graphql';
9
9
  import { privileged } from '@things-factory/auth-base/dist-client/index.js';
10
10
  let BoardTileList = class BoardTileList extends LitElement {
@@ -15,6 +15,10 @@ let BoardTileList = class BoardTileList extends LitElement {
15
15
  this.groups = [];
16
16
  this.creatable = false;
17
17
  this.reorderable = false;
18
+ this.updatedIds = [];
19
+ this._draggedId = null;
20
+ this._dropTargetId = null;
21
+ this._dropSide = null;
18
22
  }
19
23
  static { this.styles = [
20
24
  css `
@@ -102,6 +106,107 @@ let BoardTileList = class BoardTileList extends LitElement {
102
106
 
103
107
  [draggable='true'] {
104
108
  cursor: grab;
109
+ transition: opacity 0.2s ease, transform 0.2s ease;
110
+ }
111
+
112
+ [card].dragging {
113
+ opacity: 0.3;
114
+ transform: scale(0.95);
115
+ }
116
+
117
+ [card].drop-before {
118
+ border-left: 3px solid var(--md-sys-color-primary);
119
+ }
120
+
121
+ [card].drop-after {
122
+ border-right: 3px solid var(--md-sys-color-primary);
123
+ }
124
+
125
+ [card].flip-moving {
126
+ transition: transform 1s cubic-bezier(0.25, 0.1, 0.25, 1);
127
+ }
128
+
129
+ @keyframes drop-land {
130
+ 0% {
131
+ opacity: 0;
132
+ transform: scale(0.9);
133
+ }
134
+ 100% {
135
+ opacity: 1;
136
+ transform: scale(1);
137
+ }
138
+ }
139
+
140
+ [card].flip-dropped-hidden {
141
+ opacity: 0;
142
+ }
143
+
144
+ [card].flip-dropped {
145
+ animation: drop-land 0.5s cubic-bezier(0.22, 0.6, 0.36, 1);
146
+ }
147
+
148
+ @keyframes highlight-sweep {
149
+ 0% {
150
+ background: linear-gradient(90deg, var(--md-sys-color-primary-container) 0%, transparent 0%);
151
+ box-shadow: 0 0 8px 2px var(--md-sys-color-primary);
152
+ }
153
+ 50% {
154
+ background: linear-gradient(90deg, transparent 0%, var(--md-sys-color-primary-container) 50%, transparent 100%);
155
+ box-shadow: 0 0 12px 3px var(--md-sys-color-primary);
156
+ }
157
+ 100% {
158
+ background: transparent;
159
+ box-shadow: none;
160
+ }
161
+ }
162
+
163
+ @keyframes border-pulse {
164
+ 0%,
165
+ 100% {
166
+ outline: 2px solid transparent;
167
+ }
168
+ 25%,
169
+ 75% {
170
+ outline: 2px solid var(--md-sys-color-primary);
171
+ }
172
+ }
173
+
174
+ @keyframes subtle-bounce {
175
+ 0% {
176
+ transform: scale(1);
177
+ }
178
+ 30% {
179
+ transform: scale(1.03);
180
+ }
181
+ 100% {
182
+ transform: scale(1);
183
+ }
184
+ }
185
+
186
+ @keyframes surface-flash {
187
+ 0% {
188
+ background-color: var(--md-sys-color-primary-container);
189
+ }
190
+ 100% {
191
+ background-color: transparent;
192
+ }
193
+ }
194
+
195
+ [card].anim-sweep {
196
+ animation: highlight-sweep 1.8s ease;
197
+ border-radius: 4px;
198
+ }
199
+
200
+ [card].anim-pulse {
201
+ animation: border-pulse 1.2s ease;
202
+ }
203
+
204
+ [card].anim-bounce {
205
+ animation: subtle-bounce 0.8s ease;
206
+ }
207
+
208
+ [card].anim-flash {
209
+ animation: surface-flash 1.5s ease-out;
105
210
  }
106
211
 
107
212
  @media screen and (max-width: 800px), screen and (max-height: 600px) {
@@ -115,41 +220,165 @@ let BoardTileList = class BoardTileList extends LitElement {
115
220
  super.connectedCallback();
116
221
  if (this.reorderable) {
117
222
  this.renderRoot.addEventListener('dragstart', (e) => {
118
- const target = e.target;
119
- this.draggedItem = target.closest('[card]');
120
- e.dataTransfer?.setData('text/plain', target.innerHTML);
223
+ const target = e.target.closest('[card]');
224
+ if (!target?.id)
225
+ return;
226
+ this._draggedId = target.id;
227
+ target.classList.add('dragging');
228
+ e.dataTransfer.effectAllowed = 'move';
229
+ e.dataTransfer?.setData('text/plain', target.id);
121
230
  });
122
231
  this.renderRoot.addEventListener('dragover', (e) => {
123
232
  e.preventDefault();
124
- const target = e.target;
125
- const targetItem = target.closest('[card]');
126
- if (targetItem && targetItem !== this.draggedItem) {
127
- const targetRect = targetItem.getBoundingClientRect();
128
- const mousePos = e.clientX - targetRect.left;
129
- if (mousePos < targetRect.width / 2) {
130
- // 마우스 위치가 아이템 좌측 반절에 있을
131
- this.renderRoot.insertBefore(this.draggedItem, targetItem);
132
- }
133
- else {
134
- // 마우스 위치가 아이템 우측 반절에 있을 때
135
- this.renderRoot.insertBefore(this.draggedItem, targetItem.nextSibling);
233
+ e.dataTransfer.dropEffect = 'move';
234
+ const target = e.target.closest('[card]');
235
+ if (!target?.id || target.id === this._draggedId)
236
+ return;
237
+ const targetRect = target.getBoundingClientRect();
238
+ const mousePos = e.clientX - targetRect.left;
239
+ const side = mousePos < targetRect.width / 2 ? 'before' : 'after';
240
+ if (this._dropTargetId !== target.id || this._dropSide !== side) {
241
+ this._clearDropIndicator();
242
+ this._dropTargetId = target.id;
243
+ this._dropSide = side;
244
+ target.classList.add(side === 'before' ? 'drop-before' : 'drop-after');
245
+ }
246
+ });
247
+ this.renderRoot.addEventListener('dragleave', (e) => {
248
+ const target = e.target.closest('[card]');
249
+ if (target?.id === this._dropTargetId) {
250
+ const related = e.relatedTarget;
251
+ if (!related || !target.contains(related)) {
252
+ this._clearDropIndicator();
136
253
  }
137
254
  }
138
255
  });
139
256
  this.renderRoot.addEventListener('drop', (e) => {
140
257
  e.preventDefault();
141
- const boardIds = Array.from(this.renderRoot.querySelectorAll('[card]'))
142
- .map(board => board.getAttribute('id'))
143
- .filter(Boolean);
144
- this.dispatchEvent(new CustomEvent('reordered', {
145
- detail: {
146
- groupId: this.group,
147
- boardIds
258
+ // drop 이벤트에서 직접 타겟과 방향을 계산 (dragleave가 먼저 발생해 상태를 지울 수 있으므로)
259
+ const dropTarget = e.target.closest('[card]');
260
+ let dropTargetId = this._dropTargetId;
261
+ let dropSide = this._dropSide;
262
+ if (dropTarget?.id && dropTarget.id !== this._draggedId) {
263
+ dropTargetId = dropTarget.id;
264
+ const rect = dropTarget.getBoundingClientRect();
265
+ dropSide = e.clientX - rect.left < rect.width / 2 ? 'before' : 'after';
266
+ }
267
+ this._clearDropIndicator();
268
+ const draggedEl = this._draggedId ? this.renderRoot.querySelector(`[card][id="${this._draggedId}"]`) : null;
269
+ if (draggedEl) {
270
+ draggedEl.classList.remove('dragging');
271
+ }
272
+ if (!this._draggedId || !dropTargetId || this._draggedId === dropTargetId) {
273
+ this._resetDragState();
274
+ return;
275
+ }
276
+ // 배열 재정렬
277
+ const boards = [...this.boards];
278
+ const draggedIndex = boards.findIndex(b => b.id === this._draggedId);
279
+ if (draggedIndex === -1) {
280
+ this._resetDragState();
281
+ return;
282
+ }
283
+ const draggedBoardId = this._draggedId;
284
+ const [dragged] = boards.splice(draggedIndex, 1);
285
+ let targetIndex = boards.findIndex(b => b.id === dropTargetId);
286
+ if (dropSide === 'after')
287
+ targetIndex++;
288
+ boards.splice(targetIndex, 0, dragged);
289
+ this._resetDragState();
290
+ // FLIP 애니메이션: 이전 위치 기록
291
+ const cards = this.renderRoot.querySelectorAll('[card][id]');
292
+ const firstPositions = new Map();
293
+ cards.forEach(card => {
294
+ firstPositions.set(card.id, card.getBoundingClientRect());
295
+ });
296
+ // optimistic update → Lit 렌더
297
+ this.boards = boards;
298
+ this.updateComplete.then(() => {
299
+ const updatedCards = this.renderRoot.querySelectorAll('[card][id]');
300
+ let droppedEl = null;
301
+ updatedCards.forEach(card => {
302
+ const first = firstPositions.get(card.id);
303
+ if (!first)
304
+ return;
305
+ const last = card.getBoundingClientRect();
306
+ const dx = first.left - last.left;
307
+ const dy = first.top - last.top;
308
+ const el = card;
309
+ if (card.id === draggedBoardId) {
310
+ // 드래그한 카드: 슬라이딩 완료 후 착지하도록 일단 숨김
311
+ el.classList.add('flip-dropped-hidden');
312
+ droppedEl = el;
313
+ }
314
+ else {
315
+ // 밀려나는 카드: 이전 위치에서 새 위치로 슬라이딩
316
+ if (dx === 0 && dy === 0)
317
+ return;
318
+ // Invert: 이전 위치로 되돌림
319
+ el.style.transform = `translate(${dx}px, ${dy}px)`;
320
+ el.style.transition = 'none';
321
+ requestAnimationFrame(() => {
322
+ el.classList.add('flip-moving');
323
+ el.style.transform = '';
324
+ el.style.transition = '';
325
+ el.addEventListener('transitionend', () => {
326
+ el.classList.remove('flip-moving');
327
+ }, { once: true });
328
+ });
329
+ }
330
+ });
331
+ // 밀려나는 카드 슬라이딩 완료 후 드래그한 카드 착지 → 착지 완료 후 이벤트 발송
332
+ const boardIds = boards.map(b => b.id);
333
+ const dispatchReordered = () => {
334
+ this.dispatchEvent(new CustomEvent('reordered', {
335
+ detail: {
336
+ groupId: this.group,
337
+ boardIds
338
+ }
339
+ }));
340
+ };
341
+ if (droppedEl) {
342
+ setTimeout(() => {
343
+ ;
344
+ droppedEl.classList.remove('flip-dropped-hidden');
345
+ droppedEl.classList.add('flip-dropped');
346
+ droppedEl.addEventListener('animationend', () => {
347
+ ;
348
+ droppedEl.classList.remove('flip-dropped');
349
+ dispatchReordered();
350
+ }, { once: true });
351
+ }, 500);
352
+ }
353
+ else {
354
+ dispatchReordered();
148
355
  }
149
- }));
356
+ });
357
+ });
358
+ this.renderRoot.addEventListener('dragend', (e) => {
359
+ this._clearDropIndicator();
360
+ const target = e.target.closest('[card]');
361
+ if (target)
362
+ target.classList.remove('dragging');
363
+ this._resetDragState();
150
364
  });
151
365
  }
152
366
  }
367
+ _clearDropIndicator() {
368
+ if (this._dropTargetId) {
369
+ const el = this.renderRoot.querySelector(`[card][id="${this._dropTargetId}"]`);
370
+ if (el) {
371
+ el.classList.remove('drop-before', 'drop-after');
372
+ }
373
+ }
374
+ this._dropTargetId = null;
375
+ this._dropSide = null;
376
+ }
377
+ _resetDragState() {
378
+ this._draggedId = null;
379
+ this._dropTargetId = null;
380
+ this._dropSide = null;
381
+ }
153
382
  render() {
154
383
  const boards = this.boards || [];
155
384
  return html `
@@ -164,28 +393,28 @@ let BoardTileList = class BoardTileList extends LitElement {
164
393
  ></ox-board-creation-card>
165
394
  `)
166
395
  : nothing}
167
- ${keyed(Date.now(), boards.map(board => html `
168
- <div card draggable="true" id=${board.id}>
169
- <a href="board-viewer/${board.id}"> <img src=${board.thumbnail} /> </a>
396
+ ${repeat(boards, board => board.id, board => html `
397
+ <div card draggable="true" id=${board.id}>
398
+ <a href="board-viewer/${board.id}"> <img src=${board.thumbnail} /> </a>
170
399
 
171
- <div name>${board.name}</div>
172
- <!-- <div description>${board.description}</div> -->
400
+ <div name>${board.name}</div>
401
+ <!-- <div description>${board.description}</div> -->
173
402
 
174
- <md-icon
175
- iconBtn
176
- info
177
- @click=${e => {
403
+ <md-icon
404
+ iconBtn
405
+ info
406
+ @click=${e => {
178
407
  this.infoBoard(board);
179
408
  e.preventDefault();
180
409
  }}
181
- >info</md-icon
182
- >
410
+ >info</md-icon
411
+ >
183
412
 
184
- ${(this.favorites || []).includes(board.id)
413
+ ${(this.favorites || []).includes(board.id)
185
414
  ? html ` <md-icon iconBtn favored @click=${e => this.removeFavorite(board.id)}>star</md-icon> `
186
415
  : html ` <md-icon iconBtn @click=${e => this.addFavorite(board.id)}>star_border</md-icon> `}
187
- </div>
188
- `))}
416
+ </div>
417
+ `)}
189
418
  `;
190
419
  }
191
420
  updated(changes) {
@@ -193,6 +422,19 @@ let BoardTileList = class BoardTileList extends LitElement {
193
422
  if (creationCard) {
194
423
  creationCard.reset();
195
424
  }
425
+ if (changes.has('updatedIds') && this.updatedIds.length > 0) {
426
+ const animClasses = ['anim-sweep', 'anim-pulse', 'anim-bounce', 'anim-flash'];
427
+ for (const id of this.updatedIds) {
428
+ const card = this.renderRoot.querySelector(`[card][id="${id}"]`);
429
+ if (!card)
430
+ continue;
431
+ const animClass = animClasses[Math.floor(Math.random() * animClasses.length)];
432
+ card.classList.add(animClass);
433
+ card.addEventListener('animationend', () => {
434
+ card.classList.remove(animClass);
435
+ }, { once: true });
436
+ }
437
+ }
196
438
  }
197
439
  onCreateBoard(e) {
198
440
  this.dispatchEvent(new CustomEvent('create-board', {
@@ -261,6 +503,10 @@ __decorate([
261
503
  property({ type: Boolean, attribute: 'reorderable' }),
262
504
  __metadata("design:type", Boolean)
263
505
  ], BoardTileList.prototype, "reorderable", void 0);
506
+ __decorate([
507
+ property({ type: Array }),
508
+ __metadata("design:type", Array)
509
+ ], BoardTileList.prototype, "updatedIds", void 0);
264
510
  BoardTileList = __decorate([
265
511
  customElement('board-tile-list')
266
512
  ], BoardTileList);
@@ -1 +1 @@
1
- {"version":3,"file":"board-tile-list.js","sourceRoot":"","sources":["../../client/board-list/board-tile-list.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/icon/icon.js'\nimport '@operato/board/ox-board-creation-card.js'\n\nimport gql from 'graphql-tag'\nimport { css, html, LitElement, PropertyValues, nothing } from 'lit'\nimport { customElement, property, state, query } from 'lit/decorators.js'\nimport { keyed } from 'lit/directives/keyed.js'\n\nimport { client } from '@operato/graphql'\n\nimport { privileged } from '@things-factory/auth-base/dist-client/index.js'\n\n@customElement('board-tile-list')\nexport class BoardTileList extends LitElement {\n static styles = [\n css`\n :host {\n overflow: auto;\n padding: var(--popup-content-padding);\n display: grid;\n background-color: var(--md-sys-color-background);\n\n grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));\n grid-auto-rows: var(--card-list-rows-height);\n grid-gap: 20px;\n }\n\n [card] {\n position: relative;\n align-items: center;\n overflow: hidden;\n }\n\n [card][create] {\n overflow: visible;\n background-color: initial;\n }\n\n [card] > a {\n display: flex;\n clip-path: border-box;\n }\n\n [card]:hover {\n cursor: pointer;\n }\n\n [name] {\n text-overflow: ellipsis;\n white-space: nowrap;\n overflow: hidden;\n margin-top: var(--spacing-small);\n width: calc(100% - 45px);\n color: var(--md-sys-color-on-background);\n font-weight: bolder;\n font-size: var(--fontsize-small);\n }\n\n img {\n flex: 1;\n object-fit: contain;\n }\n\n md-icon[iconBtn] {\n float: right;\n margin-top: -20px;\n margin-left: 2px;\n color: var(--board-list-star-color);\n font-size: 1.4em;\n }\n md-icon[info] {\n color: var(--md-sys-color-primary);\n }\n\n md-icon[iconBtn][favored],\n md-icon[info]:hover {\n color: var(--board-list-star-active-color);\n }\n\n a {\n display: block;\n border-radius: var(--card-list-border-radius);\n border: var(--border-dim-color);\n box-sizing: border-box;\n color: var(--card-list-color);\n background-color: var(--card-list-background-color);\n margin: 0px;\n height: calc(100% - 25px);\n }\n\n :host > *:hover [info] {\n opacity: 1;\n -webkit-transition: opacity 0.8s;\n -moz-transition: opacity 0.8s;\n -o-transition: opacity 0.8s;\n transition: opacity 0.8s;\n }\n\n [draggable='true'] {\n cursor: grab;\n }\n\n @media screen and (max-width: 800px), screen and (max-height: 600px) {\n ox-board-creation-card {\n display: none;\n }\n }\n `\n ]\n\n @property({ type: Array }) boards: any[] = []\n @property({ type: Array }) favorites: any[] = []\n @property({ type: Array }) groups: any[] = []\n @property({ type: String }) group?: string\n @property({ type: Boolean }) creatable?: boolean = false\n @property({ type: String, attribute: 'search-text' }) searchText?: string\n @property({ type: Boolean, attribute: 'reorderable' }) reorderable: boolean = false\n\n private draggedItem\n\n connectedCallback() {\n super.connectedCallback()\n\n if (this.reorderable) {\n this.renderRoot.addEventListener('dragstart', (e: Event) => {\n const target = e.target! as HTMLElement\n this.draggedItem = target.closest('[card]')\n ;(e as DragEvent).dataTransfer?.setData('text/plain', target.innerHTML)\n })\n\n this.renderRoot.addEventListener('dragover', (e: Event) => {\n e.preventDefault()\n\n const target = e.target! as HTMLElement\n const targetItem = target.closest('[card]')\n if (targetItem && targetItem !== this.draggedItem) {\n const targetRect = targetItem.getBoundingClientRect()\n const mousePos = (e as DragEvent).clientX - targetRect.left\n\n if (mousePos < targetRect.width / 2) {\n // 마우스 위치가 아이템 좌측 반절에 있을 때\n this.renderRoot.insertBefore(this.draggedItem, targetItem)\n } else {\n // 마우스 위치가 아이템 우측 반절에 있을 때\n this.renderRoot.insertBefore(this.draggedItem, targetItem.nextSibling)\n }\n }\n })\n\n this.renderRoot.addEventListener('drop', (e: Event) => {\n e.preventDefault()\n\n const boardIds = Array.from(this.renderRoot.querySelectorAll('[card]'))\n .map(board => board.getAttribute('id'))\n .filter(Boolean)\n\n this.dispatchEvent(\n new CustomEvent('reordered', {\n detail: {\n groupId: this.group,\n boardIds\n }\n })\n )\n })\n }\n }\n\n render() {\n const boards = this.boards || []\n\n return html`\n ${this.creatable\n ? privileged(\n { privilege: 'mutation', category: 'board' },\n html`\n <ox-board-creation-card\n .groups=${this.groups}\n .defaultGroup=${this.group}\n @create-board=${e => this.onCreateBoard(e)}\n card\n create\n ></ox-board-creation-card>\n `\n )\n : nothing}\n ${keyed(\n Date.now(),\n boards.map(\n board => html`\n <div card draggable=\"true\" id=${board.id}>\n <a href=\"board-viewer/${board.id}\"> <img src=${board.thumbnail} /> </a>\n\n <div name>${board.name}</div>\n <!-- <div description>${board.description}</div> -->\n\n <md-icon\n iconBtn\n info\n @click=${e => {\n this.infoBoard(board)\n e.preventDefault()\n }}\n >info</md-icon\n >\n\n ${(this.favorites || []).includes(board.id)\n ? html` <md-icon iconBtn favored @click=${e => this.removeFavorite(board.id)}>star</md-icon> `\n : html` <md-icon iconBtn @click=${e => this.addFavorite(board.id)}>star_border</md-icon> `}\n </div>\n `\n )\n )}\n `\n }\n\n updated(changes: PropertyValues<this>) {\n const creationCard = this.renderRoot.querySelector('ox-board-creation-card') as any\n if (creationCard) {\n creationCard.reset()\n }\n }\n\n onCreateBoard(e) {\n this.dispatchEvent(\n new CustomEvent('create-board', {\n detail: e.detail\n })\n )\n }\n\n infoBoard(board) {\n this.dispatchEvent(\n new CustomEvent('info-board', {\n detail: board\n })\n )\n }\n\n async removeFavorite(boardId) {\n await client.mutate({\n mutation: gql`\n mutation {\n deleteFavorite(routing: \"${boardId}\")\n }\n `\n })\n\n this.refreshFavorites()\n }\n\n async addFavorite(boardId) {\n await client.mutate({\n mutation: gql`\n mutation {\n createFavorite(favorite: {\n routing: \"${boardId}\"\n }) {\n id\n routing\n }\n }\n `\n })\n\n this.refreshFavorites()\n }\n\n async refreshFavorites() {\n this.dispatchEvent(new CustomEvent('refresh-favorites'))\n }\n}\n"]}
1
+ {"version":3,"file":"board-tile-list.js","sourceRoot":"","sources":["../../client/board-list/board-tile-list.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/icon/icon.js'\nimport '@operato/board/ox-board-creation-card.js'\n\nimport gql from 'graphql-tag'\nimport { css, html, LitElement, PropertyValues, nothing } from 'lit'\nimport { customElement, property, state, query } from 'lit/decorators.js'\nimport { repeat } from 'lit/directives/repeat.js'\n\nimport { client } from '@operato/graphql'\n\nimport { privileged } from '@things-factory/auth-base/dist-client/index.js'\n\n@customElement('board-tile-list')\nexport class BoardTileList extends LitElement {\n static styles = [\n css`\n :host {\n overflow: auto;\n padding: var(--popup-content-padding);\n display: grid;\n background-color: var(--md-sys-color-background);\n\n grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));\n grid-auto-rows: var(--card-list-rows-height);\n grid-gap: 20px;\n }\n\n [card] {\n position: relative;\n align-items: center;\n overflow: hidden;\n }\n\n [card][create] {\n overflow: visible;\n background-color: initial;\n }\n\n [card] > a {\n display: flex;\n clip-path: border-box;\n }\n\n [card]:hover {\n cursor: pointer;\n }\n\n [name] {\n text-overflow: ellipsis;\n white-space: nowrap;\n overflow: hidden;\n margin-top: var(--spacing-small);\n width: calc(100% - 45px);\n color: var(--md-sys-color-on-background);\n font-weight: bolder;\n font-size: var(--fontsize-small);\n }\n\n img {\n flex: 1;\n object-fit: contain;\n }\n\n md-icon[iconBtn] {\n float: right;\n margin-top: -20px;\n margin-left: 2px;\n color: var(--board-list-star-color);\n font-size: 1.4em;\n }\n md-icon[info] {\n color: var(--md-sys-color-primary);\n }\n\n md-icon[iconBtn][favored],\n md-icon[info]:hover {\n color: var(--board-list-star-active-color);\n }\n\n a {\n display: block;\n border-radius: var(--card-list-border-radius);\n border: var(--border-dim-color);\n box-sizing: border-box;\n color: var(--card-list-color);\n background-color: var(--card-list-background-color);\n margin: 0px;\n height: calc(100% - 25px);\n }\n\n :host > *:hover [info] {\n opacity: 1;\n -webkit-transition: opacity 0.8s;\n -moz-transition: opacity 0.8s;\n -o-transition: opacity 0.8s;\n transition: opacity 0.8s;\n }\n\n [draggable='true'] {\n cursor: grab;\n transition: opacity 0.2s ease, transform 0.2s ease;\n }\n\n [card].dragging {\n opacity: 0.3;\n transform: scale(0.95);\n }\n\n [card].drop-before {\n border-left: 3px solid var(--md-sys-color-primary);\n }\n\n [card].drop-after {\n border-right: 3px solid var(--md-sys-color-primary);\n }\n\n [card].flip-moving {\n transition: transform 1s cubic-bezier(0.25, 0.1, 0.25, 1);\n }\n\n @keyframes drop-land {\n 0% {\n opacity: 0;\n transform: scale(0.9);\n }\n 100% {\n opacity: 1;\n transform: scale(1);\n }\n }\n\n [card].flip-dropped-hidden {\n opacity: 0;\n }\n\n [card].flip-dropped {\n animation: drop-land 0.5s cubic-bezier(0.22, 0.6, 0.36, 1);\n }\n\n @keyframes highlight-sweep {\n 0% {\n background: linear-gradient(90deg, var(--md-sys-color-primary-container) 0%, transparent 0%);\n box-shadow: 0 0 8px 2px var(--md-sys-color-primary);\n }\n 50% {\n background: linear-gradient(90deg, transparent 0%, var(--md-sys-color-primary-container) 50%, transparent 100%);\n box-shadow: 0 0 12px 3px var(--md-sys-color-primary);\n }\n 100% {\n background: transparent;\n box-shadow: none;\n }\n }\n\n @keyframes border-pulse {\n 0%,\n 100% {\n outline: 2px solid transparent;\n }\n 25%,\n 75% {\n outline: 2px solid var(--md-sys-color-primary);\n }\n }\n\n @keyframes subtle-bounce {\n 0% {\n transform: scale(1);\n }\n 30% {\n transform: scale(1.03);\n }\n 100% {\n transform: scale(1);\n }\n }\n\n @keyframes surface-flash {\n 0% {\n background-color: var(--md-sys-color-primary-container);\n }\n 100% {\n background-color: transparent;\n }\n }\n\n [card].anim-sweep {\n animation: highlight-sweep 1.8s ease;\n border-radius: 4px;\n }\n\n [card].anim-pulse {\n animation: border-pulse 1.2s ease;\n }\n\n [card].anim-bounce {\n animation: subtle-bounce 0.8s ease;\n }\n\n [card].anim-flash {\n animation: surface-flash 1.5s ease-out;\n }\n\n @media screen and (max-width: 800px), screen and (max-height: 600px) {\n ox-board-creation-card {\n display: none;\n }\n }\n `\n ]\n\n @property({ type: Array }) boards: any[] = []\n @property({ type: Array }) favorites: any[] = []\n @property({ type: Array }) groups: any[] = []\n @property({ type: String }) group?: string\n @property({ type: Boolean }) creatable?: boolean = false\n @property({ type: String, attribute: 'search-text' }) searchText?: string\n @property({ type: Boolean, attribute: 'reorderable' }) reorderable: boolean = false\n @property({ type: Array }) updatedIds: string[] = []\n\n private _draggedId: string | null = null\n private _dropTargetId: string | null = null\n private _dropSide: 'before' | 'after' | null = null\n\n connectedCallback() {\n super.connectedCallback()\n\n if (this.reorderable) {\n this.renderRoot.addEventListener('dragstart', (e: Event) => {\n const target = (e.target as HTMLElement).closest('[card]') as HTMLElement | null\n if (!target?.id) return\n\n this._draggedId = target.id\n target.classList.add('dragging')\n ;(e as DragEvent).dataTransfer!.effectAllowed = 'move'\n ;(e as DragEvent).dataTransfer?.setData('text/plain', target.id)\n })\n\n this.renderRoot.addEventListener('dragover', (e: Event) => {\n e.preventDefault()\n ;(e as DragEvent).dataTransfer!.dropEffect = 'move'\n\n const target = (e.target as HTMLElement).closest('[card]') as HTMLElement | null\n if (!target?.id || target.id === this._draggedId) return\n\n const targetRect = target.getBoundingClientRect()\n const mousePos = (e as DragEvent).clientX - targetRect.left\n const side = mousePos < targetRect.width / 2 ? 'before' : 'after'\n\n if (this._dropTargetId !== target.id || this._dropSide !== side) {\n this._clearDropIndicator()\n this._dropTargetId = target.id\n this._dropSide = side\n target.classList.add(side === 'before' ? 'drop-before' : 'drop-after')\n }\n })\n\n this.renderRoot.addEventListener('dragleave', (e: Event) => {\n const target = (e.target as HTMLElement).closest('[card]') as HTMLElement | null\n if (target?.id === this._dropTargetId) {\n const related = (e as DragEvent).relatedTarget as HTMLElement | null\n if (!related || !target.contains(related)) {\n this._clearDropIndicator()\n }\n }\n })\n\n this.renderRoot.addEventListener('drop', (e: Event) => {\n e.preventDefault()\n\n // drop 이벤트에서 직접 타겟과 방향을 계산 (dragleave가 먼저 발생해 상태를 지울 수 있으므로)\n const dropTarget = (e.target as HTMLElement).closest('[card]') as HTMLElement | null\n let dropTargetId = this._dropTargetId\n let dropSide = this._dropSide\n\n if (dropTarget?.id && dropTarget.id !== this._draggedId) {\n dropTargetId = dropTarget.id\n const rect = dropTarget.getBoundingClientRect()\n dropSide = (e as DragEvent).clientX - rect.left < rect.width / 2 ? 'before' : 'after'\n }\n\n this._clearDropIndicator()\n\n const draggedEl = this._draggedId ? this.renderRoot.querySelector(`[card][id=\"${this._draggedId}\"]`) : null\n if (draggedEl) {\n draggedEl.classList.remove('dragging')\n }\n\n if (!this._draggedId || !dropTargetId || this._draggedId === dropTargetId) {\n this._resetDragState()\n return\n }\n\n // 배열 재정렬\n const boards = [...this.boards]\n const draggedIndex = boards.findIndex(b => b.id === this._draggedId)\n if (draggedIndex === -1) {\n this._resetDragState()\n return\n }\n\n const draggedBoardId = this._draggedId\n const [dragged] = boards.splice(draggedIndex, 1)\n let targetIndex = boards.findIndex(b => b.id === dropTargetId)\n if (dropSide === 'after') targetIndex++\n boards.splice(targetIndex, 0, dragged)\n\n this._resetDragState()\n\n // FLIP 애니메이션: 이전 위치 기록\n const cards = this.renderRoot.querySelectorAll('[card][id]')\n const firstPositions = new Map<string, DOMRect>()\n cards.forEach(card => {\n firstPositions.set(card.id, card.getBoundingClientRect())\n })\n\n // optimistic update → Lit 렌더\n this.boards = boards\n\n this.updateComplete.then(() => {\n const updatedCards = this.renderRoot.querySelectorAll('[card][id]')\n let droppedEl: HTMLElement | null = null\n\n updatedCards.forEach(card => {\n const first = firstPositions.get(card.id)\n if (!first) return\n\n const last = card.getBoundingClientRect()\n const dx = first.left - last.left\n const dy = first.top - last.top\n\n const el = card as HTMLElement\n\n if (card.id === draggedBoardId) {\n // 드래그한 카드: 슬라이딩 완료 후 착지하도록 일단 숨김\n el.classList.add('flip-dropped-hidden')\n droppedEl = el\n } else {\n // 밀려나는 카드: 이전 위치에서 새 위치로 슬라이딩\n if (dx === 0 && dy === 0) return\n\n // Invert: 이전 위치로 되돌림\n el.style.transform = `translate(${dx}px, ${dy}px)`\n el.style.transition = 'none'\n\n requestAnimationFrame(() => {\n el.classList.add('flip-moving')\n el.style.transform = ''\n el.style.transition = ''\n\n el.addEventListener(\n 'transitionend',\n () => {\n el.classList.remove('flip-moving')\n },\n { once: true }\n )\n })\n }\n })\n\n // 밀려나는 카드 슬라이딩 완료 후 드래그한 카드 착지 → 착지 완료 후 이벤트 발송\n const boardIds = boards.map(b => b.id)\n const dispatchReordered = () => {\n this.dispatchEvent(\n new CustomEvent('reordered', {\n detail: {\n groupId: this.group,\n boardIds\n }\n })\n )\n }\n\n if (droppedEl) {\n setTimeout(() => {\n ;(droppedEl as HTMLElement).classList.remove('flip-dropped-hidden')\n ;(droppedEl as HTMLElement).classList.add('flip-dropped')\n ;(droppedEl as HTMLElement).addEventListener(\n 'animationend',\n () => {\n ;(droppedEl as HTMLElement).classList.remove('flip-dropped')\n dispatchReordered()\n },\n { once: true }\n )\n }, 500)\n } else {\n dispatchReordered()\n }\n })\n })\n\n this.renderRoot.addEventListener('dragend', (e: Event) => {\n this._clearDropIndicator()\n const target = (e.target as HTMLElement).closest('[card]')\n if (target) target.classList.remove('dragging')\n this._resetDragState()\n })\n }\n }\n\n private _clearDropIndicator() {\n if (this._dropTargetId) {\n const el = this.renderRoot.querySelector(`[card][id=\"${this._dropTargetId}\"]`)\n if (el) {\n el.classList.remove('drop-before', 'drop-after')\n }\n }\n this._dropTargetId = null\n this._dropSide = null\n }\n\n private _resetDragState() {\n this._draggedId = null\n this._dropTargetId = null\n this._dropSide = null\n }\n\n render() {\n const boards = this.boards || []\n\n return html`\n ${this.creatable\n ? privileged(\n { privilege: 'mutation', category: 'board' },\n html`\n <ox-board-creation-card\n .groups=${this.groups}\n .defaultGroup=${this.group}\n @create-board=${e => this.onCreateBoard(e)}\n card\n create\n ></ox-board-creation-card>\n `\n )\n : nothing}\n ${repeat(\n boards,\n board => board.id,\n board => html`\n <div card draggable=\"true\" id=${board.id}>\n <a href=\"board-viewer/${board.id}\"> <img src=${board.thumbnail} /> </a>\n\n <div name>${board.name}</div>\n <!-- <div description>${board.description}</div> -->\n\n <md-icon\n iconBtn\n info\n @click=${e => {\n this.infoBoard(board)\n e.preventDefault()\n }}\n >info</md-icon\n >\n\n ${(this.favorites || []).includes(board.id)\n ? html` <md-icon iconBtn favored @click=${e => this.removeFavorite(board.id)}>star</md-icon> `\n : html` <md-icon iconBtn @click=${e => this.addFavorite(board.id)}>star_border</md-icon> `}\n </div>\n `\n )}\n `\n }\n\n updated(changes: PropertyValues<this>) {\n const creationCard = this.renderRoot.querySelector('ox-board-creation-card') as any\n if (creationCard) {\n creationCard.reset()\n }\n\n if (changes.has('updatedIds') && this.updatedIds.length > 0) {\n const animClasses = ['anim-sweep', 'anim-pulse', 'anim-bounce', 'anim-flash']\n\n for (const id of this.updatedIds) {\n const card = this.renderRoot.querySelector(`[card][id=\"${id}\"]`)\n if (!card) continue\n\n const animClass = animClasses[Math.floor(Math.random() * animClasses.length)]\n card.classList.add(animClass)\n card.addEventListener(\n 'animationend',\n () => {\n card.classList.remove(animClass)\n },\n { once: true }\n )\n }\n }\n }\n\n onCreateBoard(e) {\n this.dispatchEvent(\n new CustomEvent('create-board', {\n detail: e.detail\n })\n )\n }\n\n infoBoard(board) {\n this.dispatchEvent(\n new CustomEvent('info-board', {\n detail: board\n })\n )\n }\n\n async removeFavorite(boardId) {\n await client.mutate({\n mutation: gql`\n mutation {\n deleteFavorite(routing: \"${boardId}\")\n }\n `\n })\n\n this.refreshFavorites()\n }\n\n async addFavorite(boardId) {\n await client.mutate({\n mutation: gql`\n mutation {\n createFavorite(favorite: {\n routing: \"${boardId}\"\n }) {\n id\n routing\n }\n }\n `\n })\n\n this.refreshFavorites()\n }\n\n async refreshFavorites() {\n this.dispatchEvent(new CustomEvent('refresh-favorites'))\n }\n}\n"]}
@@ -17,6 +17,7 @@ export declare class BoardListPage extends BoardListPageBase {
17
17
  private _total;
18
18
  private page?;
19
19
  private _lastFetchTime?;
20
+ private _updatedBoardIds;
20
21
  get context(): {
21
22
  title: {
22
23
  icon: string;
@@ -59,6 +59,15 @@ let BoardListPage = class BoardListPage extends BoardListPageBase {
59
59
  handler: search => {
60
60
  this.searchText = search;
61
61
  this.refreshBoards();
62
+ // URL에 검색어 반영 → 페이지 복귀 시 유지
63
+ const url = new URL(window.location.href);
64
+ if (search) {
65
+ url.searchParams.set('search', search);
66
+ }
67
+ else {
68
+ url.searchParams.delete('search');
69
+ }
70
+ history.replaceState(null, '', url.href);
62
71
  },
63
72
  value: this.searchText || ''
64
73
  },
@@ -74,6 +83,7 @@ let BoardListPage = class BoardListPage extends BoardListPageBase {
74
83
  this.searchText = '';
75
84
  this._page = 1;
76
85
  this._total = 0; /* required for infinite-scrolling */
86
+ this._updatedBoardIds = [];
77
87
  /**
78
88
  * 컨텐츠가 스크롤 영역을 채우지 못하면 다음 페이지를 자동 로드한다.
79
89
  */
@@ -96,6 +106,7 @@ let BoardListPage = class BoardListPage extends BoardListPageBase {
96
106
  .favorites=${this.favorites}
97
107
  .groups=${this.groups}
98
108
  .group=${this.groupId}
109
+ .updatedIds=${this._updatedBoardIds}
99
110
  search-text=${this.searchText}
100
111
  @info-board=${e => this.onInfoBoard(e.detail)}
101
112
  @scroll=${e => {
@@ -248,6 +259,7 @@ let BoardListPage = class BoardListPage extends BoardListPageBase {
248
259
  const updatedBoards = await fetchBoardsUpdatedSince(this._lastFetchTime);
249
260
  if (updatedBoards && updatedBoards.length > 0) {
250
261
  const updatedMap = new Map(updatedBoards.map((b) => [b.id, b]));
262
+ const changedIds = [];
251
263
  this.boards = this.boards
252
264
  .map(board => {
253
265
  const updated = updatedMap.get(board.id);
@@ -256,9 +268,11 @@ let BoardListPage = class BoardListPage extends BoardListPageBase {
256
268
  // 삭제되었거나 현재 그룹에서 벗어난 경우 제거
257
269
  if (updated.deletedAt || (this.groupId && updated.group?.id !== this.groupId))
258
270
  return null;
271
+ changedIds.push(updated.id);
259
272
  return updated;
260
273
  })
261
274
  .filter(Boolean);
275
+ this._updatedBoardIds = changedIds;
262
276
  }
263
277
  this._lastFetchTime = new Date();
264
278
  }
@@ -284,12 +298,17 @@ let BoardListPage = class BoardListPage extends BoardListPageBase {
284
298
  async pageUpdated(changes, lifecycle) {
285
299
  if (this.active) {
286
300
  const prevGroupId = this.groupId;
301
+ const prevSearchText = this.searchText;
287
302
  this.page = lifecycle.page;
288
303
  this.groupId = lifecycle.resourceId;
289
304
  this.searchText = lifecycle.params?.search || '';
290
305
  await this.updateComplete;
306
+ // 검색어가 변경되었으면 전체 갱신
291
307
  // 같은 그룹으로 복귀하고 기존 데이터가 있으면 변경분만 갱신 (스크롤 유지)
292
- if (prevGroupId === this.groupId && this.boards.length > 0 && this._lastFetchTime) {
308
+ if (prevSearchText !== this.searchText) {
309
+ this.refreshBoards();
310
+ }
311
+ else if (prevGroupId === this.groupId && this.boards.length > 0 && this._lastFetchTime) {
293
312
  this.incrementalRefresh();
294
313
  }
295
314
  else {
@@ -622,6 +641,10 @@ __decorate([
622
641
  query('board-tile-list'),
623
642
  __metadata("design:type", HTMLElement)
624
643
  ], BoardListPage.prototype, "scrollTargetEl", void 0);
644
+ __decorate([
645
+ state(),
646
+ __metadata("design:type", Array)
647
+ ], BoardListPage.prototype, "_updatedBoardIds", void 0);
625
648
  BoardListPage = __decorate([
626
649
  customElement('board-list-page'),
627
650
  __metadata("design:paramtypes", [])