@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.
- package/dist-client/board-list/board-tile-list.d.ts +6 -1
- package/dist-client/board-list/board-tile-list.js +284 -38
- package/dist-client/board-list/board-tile-list.js.map +1 -1
- package/dist-client/pages/board-list-page.d.ts +1 -0
- package/dist-client/pages/board-list-page.js +24 -1
- package/dist-client/pages/board-list-page.js.map +1 -1
- package/dist-client/pages/play-list-page.js +2 -1
- package/dist-client/pages/play-list-page.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -4
|
@@ -10,8 +10,13 @@ export declare class BoardTileList extends LitElement {
|
|
|
10
10
|
creatable?: boolean;
|
|
11
11
|
searchText?: string;
|
|
12
12
|
reorderable: boolean;
|
|
13
|
-
|
|
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 {
|
|
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
|
-
|
|
120
|
-
|
|
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
|
-
|
|
125
|
-
const
|
|
126
|
-
if (
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
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
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
this.
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
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
|
-
${
|
|
168
|
-
|
|
169
|
-
|
|
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
|
-
|
|
172
|
-
|
|
400
|
+
<div name>${board.name}</div>
|
|
401
|
+
<!-- <div description>${board.description}</div> -->
|
|
173
402
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
403
|
+
<md-icon
|
|
404
|
+
iconBtn
|
|
405
|
+
info
|
|
406
|
+
@click=${e => {
|
|
178
407
|
this.infoBoard(board);
|
|
179
408
|
e.preventDefault();
|
|
180
409
|
}}
|
|
181
|
-
|
|
182
|
-
|
|
410
|
+
>info</md-icon
|
|
411
|
+
>
|
|
183
412
|
|
|
184
|
-
|
|
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
|
-
|
|
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"]}
|
|
@@ -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 (
|
|
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", [])
|