@things-factory/board-ui 10.1.40 → 10.1.42
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-thumb.d.ts +33 -0
- package/dist-client/board-list/board-thumb.js +119 -0
- package/dist-client/board-list/board-thumb.js.map +1 -0
- package/dist-client/graphql/favorite-board.js +3 -0
- package/dist-client/graphql/favorite-board.js.map +1 -1
- package/dist-client/graphql/my-board.js +3 -0
- package/dist-client/graphql/my-board.js.map +1 -1
- package/dist-client/graphql/play-group.js +2 -0
- package/dist-client/graphql/play-group.js.map +1 -1
- package/dist-client/index.d.ts +1 -0
- package/dist-client/index.js +1 -0
- package/dist-client/index.js.map +1 -1
- package/dist-client/pages/board-list-page.d.ts +39 -42
- package/dist-client/pages/board-list-page.js +388 -284
- package/dist-client/pages/board-list-page.js.map +1 -1
- package/dist-client/pages/board-modeller-page.d.ts +2 -0
- package/dist-client/pages/board-modeller-page.js +18 -1
- package/dist-client/pages/board-modeller-page.js.map +1 -1
- package/dist-client/pages/board-template/board-template-list-page.d.ts +21 -10
- package/dist-client/pages/board-template/board-template-list-page.js +147 -136
- package/dist-client/pages/board-template/board-template-list-page.js.map +1 -1
- package/dist-client/pages/play-list-page.d.ts +22 -12
- package/dist-client/pages/play-list-page.js +264 -113
- package/dist-client/pages/play-list-page.js.map +1 -1
- package/dist-client/route.d.ts +1 -1
- package/dist-client/setting-let/board-modeller-setting-let.d.ts +21 -0
- package/dist-client/setting-let/board-modeller-setting-let.js +144 -0
- package/dist-client/setting-let/board-modeller-setting-let.js.map +1 -0
- package/dist-client/styles/board-card-styles.d.ts +11 -0
- package/dist-client/styles/board-card-styles.js +90 -0
- package/dist-client/styles/board-card-styles.js.map +1 -0
- package/dist-client/themes/board-theme.css +32 -19
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-client/utils/board-meta.d.ts +10 -0
- package/dist-client/utils/board-meta.js +34 -0
- package/dist-client/utils/board-meta.js.map +1 -0
- package/dist-client/viewparts/board-basic-info.d.ts +38 -7
- package/dist-client/viewparts/board-basic-info.js +378 -349
- package/dist-client/viewparts/board-basic-info.js.map +1 -1
- package/dist-client/viewparts/board-info-link.d.ts +5 -1
- package/dist-client/viewparts/board-info-link.js +79 -21
- package/dist-client/viewparts/board-info-link.js.map +1 -1
- package/dist-client/viewparts/board-info-template.d.ts +15 -0
- package/dist-client/viewparts/board-info-template.js +97 -0
- package/dist-client/viewparts/board-info-template.js.map +1 -0
- package/dist-client/viewparts/board-info.d.ts +5 -0
- package/dist-client/viewparts/board-info.js +24 -73
- package/dist-client/viewparts/board-info.js.map +1 -1
- package/dist-client/viewparts/board-template-builder.d.ts +1 -0
- package/dist-client/viewparts/board-template-builder.js +37 -65
- package/dist-client/viewparts/board-template-builder.js.map +1 -1
- package/dist-client/viewparts/board-template-info.d.ts +3 -0
- package/dist-client/viewparts/board-template-info.js +75 -134
- package/dist-client/viewparts/board-template-info.js.map +1 -1
- package/dist-client/viewparts/group-info-basic.d.ts +8 -3
- package/dist-client/viewparts/group-info-basic.js +54 -190
- package/dist-client/viewparts/group-info-basic.js.map +1 -1
- package/dist-client/viewparts/group-info-import.d.ts +3 -1
- package/dist-client/viewparts/group-info-import.js +29 -62
- package/dist-client/viewparts/group-info-import.js.map +1 -1
- package/dist-client/viewparts/group-info.d.ts +4 -0
- package/dist-client/viewparts/group-info.js +21 -55
- package/dist-client/viewparts/group-info.js.map +1 -1
- package/dist-client/viewparts/info-dialog-styles.d.ts +19 -0
- package/dist-client/viewparts/info-dialog-styles.js +194 -0
- package/dist-client/viewparts/info-dialog-styles.js.map +1 -0
- package/dist-client/viewparts/link-builder.d.ts +3 -0
- package/dist-client/viewparts/link-builder.js +87 -81
- package/dist-client/viewparts/link-builder.js.map +1 -1
- package/dist-client/viewparts/play-group-info-basic.d.ts +4 -3
- package/dist-client/viewparts/play-group-info-basic.js +49 -188
- package/dist-client/viewparts/play-group-info-basic.js.map +1 -1
- package/dist-client/viewparts/play-group-info.d.ts +4 -0
- package/dist-client/viewparts/play-group-info.js +22 -53
- package/dist-client/viewparts/play-group-info.js.map +1 -1
- package/package.json +4 -4
- package/translations/en.json +32 -0
- package/translations/ja.json +10 -0
- package/translations/ko.json +32 -0
- package/translations/ms.json +8 -0
- package/translations/zh.json +10 -0
|
@@ -1,65 +1,74 @@
|
|
|
1
1
|
import { __decorate, __metadata } from "tslib";
|
|
2
|
+
import '@operato/data-grist';
|
|
3
|
+
import '@material/web/icon/icon.js';
|
|
4
|
+
import '@material/web/button/text-button.js';
|
|
5
|
+
import '../board-list/board-thumb.js';
|
|
2
6
|
import '../viewparts/board-info.js';
|
|
3
7
|
import '../viewparts/group-info.js';
|
|
4
|
-
import '../board-list/board-tile-list.js';
|
|
5
|
-
import '../board-list/group-bar.js';
|
|
6
8
|
import gql from 'graphql-tag';
|
|
7
9
|
import { css, html } from 'lit';
|
|
8
10
|
import { customElement, property, query, state } from 'lit/decorators.js';
|
|
11
|
+
import { DataGrist } from '@operato/data-grist';
|
|
9
12
|
import { client } from '@operato/graphql';
|
|
10
13
|
import { i18next, localize } from '@operato/i18n';
|
|
11
|
-
import {
|
|
14
|
+
import { openPopup } from '@operato/layout';
|
|
12
15
|
import { pulltorefresh } from '@operato/pull-to-refresh';
|
|
13
16
|
import { navigate, PageView, store } from '@operato/shell';
|
|
14
|
-
import { ScrollbarStyles } from '@operato/styles';
|
|
15
|
-
import
|
|
17
|
+
import { CommonGristStyles, CommonHeaderStyles, ScrollbarStyles, WorkSurfacePageStyles } from '@operato/styles';
|
|
18
|
+
import { hasPrivilege } from '@things-factory/auth-base/dist-client/index.js';
|
|
16
19
|
import { UPDATE_FAVORITES } from '@things-factory/fav-base/client/index.js';
|
|
17
|
-
import { swipe } from '@things-factory/utils/src/index.js';
|
|
18
20
|
import { notify } from '../utils/notify-helper.js';
|
|
19
|
-
import {
|
|
20
|
-
|
|
21
|
+
import { boardMeta } from '../utils/board-meta.js';
|
|
22
|
+
import { BoardCardStyles } from '../styles/board-card-styles.js';
|
|
23
|
+
import { cloneBoard, releaseBoard, revertBoardVersion, createBoard, createGroup, deleteBoard, deleteGroup, fetchBoardList, fetchFavoriteBoardList, fetchMyBoardList, fetchGroupList, updateBoard, updateGroup, importBoards } from '../graphql';
|
|
24
|
+
const BoardListPageBase = localize(i18next)(PageView);
|
|
25
|
+
/*
|
|
26
|
+
* ox-grist (CARD mode) like figure-list-page and board-template-list-page, instead of the
|
|
27
|
+
* board-only board-tile-list fork - see ADR on shared-component-over-app-fork. Drag-reorder comes
|
|
28
|
+
* from `rows.reorderable`, a capability added to data-grist for this (2026-09-23); it dispatches a
|
|
29
|
+
* `reorder` event and leaves persisting it to us, same contract board-tile-list's `reordered` used.
|
|
30
|
+
*
|
|
31
|
+
* Traded away in the move: the delta-merge `incrementalRefresh()` that patched only changed boards
|
|
32
|
+
* in place with a highlight-flash animation. ox-grist's own pagination (`attach-page`, handled
|
|
33
|
+
* internally by data-provider.ts) replaces the hand-rolled infinite scroll, but a group/search change
|
|
34
|
+
* now does a plain full refetch rather than an in-place merge. Worth revisiting if the flash-on-update
|
|
35
|
+
* cue turns out to matter in practice.
|
|
36
|
+
*/
|
|
21
37
|
let BoardListPage = class BoardListPage extends BoardListPageBase {
|
|
38
|
+
constructor() {
|
|
39
|
+
super(...arguments);
|
|
40
|
+
this.groups = [];
|
|
41
|
+
this.favorites = [];
|
|
42
|
+
this.searchText = '';
|
|
43
|
+
this.creatable = false;
|
|
44
|
+
/* Kept only to prefill board-info's "which group" context - the filter itself is the source of
|
|
45
|
+
* truth for what fetchHandler actually asks the server for. */
|
|
46
|
+
this.currentGroupId = '';
|
|
47
|
+
}
|
|
22
48
|
static { this.styles = [
|
|
49
|
+
/* The frame owns the page shell; the grist is the content it sizes (ADR-0060 decision 8). */
|
|
50
|
+
WorkSurfacePageStyles,
|
|
23
51
|
ScrollbarStyles,
|
|
52
|
+
CommonGristStyles,
|
|
53
|
+
CommonHeaderStyles,
|
|
54
|
+
/* The card itself - shared with the other catalogs, see styles/board-card-styles.ts. */
|
|
55
|
+
BoardCardStyles,
|
|
24
56
|
css `
|
|
25
|
-
|
|
26
|
-
display: flex;
|
|
27
|
-
flex-direction: column;
|
|
28
|
-
position: relative;
|
|
29
|
-
|
|
30
|
-
overflow: hidden;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
board-tile-list {
|
|
57
|
+
ox-filters-form {
|
|
34
58
|
flex: 1;
|
|
35
|
-
overflow-y: auto;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
#create {
|
|
39
|
-
position: absolute;
|
|
40
|
-
bottom: 15px;
|
|
41
|
-
right: 16px;
|
|
42
59
|
}
|
|
43
60
|
`
|
|
44
61
|
]; }
|
|
45
62
|
get context() {
|
|
46
|
-
const groupId = this.groupId;
|
|
47
|
-
const group = this.groups && this.groups.find(group => group.id === groupId);
|
|
48
63
|
return {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
? 'Group ' + group.name
|
|
53
|
-
: groupId == 'favor'
|
|
54
|
-
? 'Favorite'
|
|
55
|
-
: groupId == 'mywork'
|
|
56
|
-
? 'My works'
|
|
57
|
-
: 'Board List'
|
|
58
|
-
},
|
|
64
|
+
/* A plain name, no icon - the rail already carries the icon for this screen, and figure's
|
|
65
|
+
* title bar is bare text too. */
|
|
66
|
+
title: i18next.t('title.board list', { defaultValue: 'Board List' }),
|
|
59
67
|
search: {
|
|
68
|
+
placeholder: i18next.t('text.search boards by name', { defaultValue: 'Search boards by name' }),
|
|
60
69
|
handler: search => {
|
|
61
70
|
this.searchText = search;
|
|
62
|
-
this.
|
|
71
|
+
this.grist?.fetch();
|
|
63
72
|
// URL에 검색어 반영 → 페이지 복귀 시 유지
|
|
64
73
|
const url = new URL(window.location.href);
|
|
65
74
|
if (search) {
|
|
@@ -75,82 +84,215 @@ let BoardListPage = class BoardListPage extends BoardListPageBase {
|
|
|
75
84
|
board_topmenu: true
|
|
76
85
|
};
|
|
77
86
|
}
|
|
78
|
-
constructor() {
|
|
79
|
-
super();
|
|
80
|
-
this.groups = [];
|
|
81
|
-
this.boards = [];
|
|
82
|
-
this.favorites = [];
|
|
83
|
-
this.showMainBoardOnly = true;
|
|
84
|
-
this.searchText = '';
|
|
85
|
-
this._page = 1;
|
|
86
|
-
this._total = 0; /* required for infinite-scrolling */
|
|
87
|
-
this._updatedBoardIds = [];
|
|
88
|
-
/**
|
|
89
|
-
* 컨텐츠가 스크롤 영역을 채우지 못하면 다음 페이지를 자동 로드한다.
|
|
90
|
-
*/
|
|
91
|
-
this._filling = false;
|
|
92
|
-
this._infiniteScrollOptions.limit = 50;
|
|
93
|
-
}
|
|
94
87
|
render() {
|
|
95
88
|
return html `
|
|
96
|
-
<
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
@
|
|
102
|
-
|
|
89
|
+
<ox-grist
|
|
90
|
+
class="page-fill"
|
|
91
|
+
mode="CARD"
|
|
92
|
+
.config=${this.gristConfig}
|
|
93
|
+
.fetchHandler=${this.fetchHandler.bind(this)}
|
|
94
|
+
@reorder=${e => this.onReorderBoards(e.detail)}
|
|
95
|
+
>
|
|
96
|
+
<div slot="headroom">
|
|
97
|
+
<ox-filters-form autofocus without-search></ox-filters-form>
|
|
98
|
+
${this.groups.some(group => group.id === this.currentGroupId)
|
|
99
|
+
? html `
|
|
100
|
+
<md-text-button @click=${() => this.onInfoGroup(this.currentGroupId)}>
|
|
101
|
+
<md-icon slot="icon">edit</md-icon>
|
|
102
|
+
${i18next.t('text.edit group', { defaultValue: 'Edit' })}
|
|
103
|
+
</md-text-button>
|
|
104
|
+
`
|
|
105
|
+
: ''}
|
|
106
|
+
<md-text-button @click=${() => this.onInfoGroup(undefined)}>
|
|
107
|
+
<md-icon slot="icon">create_new_folder</md-icon>
|
|
108
|
+
${i18next.t('text.new group', { defaultValue: 'New Group' })}
|
|
109
|
+
</md-text-button>
|
|
110
|
+
</div>
|
|
111
|
+
</ox-grist>
|
|
103
112
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
search-text=${this.searchText}
|
|
112
|
-
@info-board=${e => this.onInfoBoard(e.detail)}
|
|
113
|
-
@scroll=${e => {
|
|
114
|
-
;
|
|
115
|
-
this.onScroll(e);
|
|
116
|
-
}}
|
|
117
|
-
@create-board=${e => this.onCreateBoard(e.detail)}
|
|
118
|
-
@refresh-favorites=${e => this.refreshFavorites()}
|
|
119
|
-
@reordered=${e => this.onReorderBoards(e.detail)}
|
|
120
|
-
creatable
|
|
121
|
-
reorderable
|
|
122
|
-
></board-tile-list>
|
|
113
|
+
${this.creatable
|
|
114
|
+
? html `
|
|
115
|
+
<button fab title=${i18next.t('text.create board', { defaultValue: 'Create Board' })} @click=${() => navigate('board-create')}>
|
|
116
|
+
<md-icon>add</md-icon>${i18next.t('text.create board', { defaultValue: 'Create Board' })}
|
|
117
|
+
</button>
|
|
118
|
+
`
|
|
119
|
+
: ''}
|
|
123
120
|
`;
|
|
124
121
|
}
|
|
122
|
+
async pageInitialized() {
|
|
123
|
+
this.creatable = await hasPrivilege({ privilege: 'mutation', category: 'board', domainOwnerGranted: true });
|
|
124
|
+
this.gristConfig = {
|
|
125
|
+
list: {
|
|
126
|
+
thumbnail: 'thumbnail',
|
|
127
|
+
/* name, then a quieter "v3 · 2 days ago" line - the same two-line rhythm as figure's
|
|
128
|
+
* name-over-type, using what a board actually has (utils/board-meta.ts). */
|
|
129
|
+
fields: ['name', 'meta'],
|
|
130
|
+
details: []
|
|
131
|
+
},
|
|
132
|
+
columns: [
|
|
133
|
+
{
|
|
134
|
+
type: 'image',
|
|
135
|
+
name: 'thumbnail',
|
|
136
|
+
record: {
|
|
137
|
+
editable: false,
|
|
138
|
+
align: 'center',
|
|
139
|
+
renderEmpty: true,
|
|
140
|
+
/* board-thumb.ts owns the picture and the draft badge; info/favorite are the two gutter
|
|
141
|
+
* columns below, in the information area rather than overlaid on the image - see
|
|
142
|
+
* board-thumb.ts's own header comment for why board's case differs from figure's. */
|
|
143
|
+
renderer: (value, column, record) => html `<board-thumb .board=${record}></board-thumb>`
|
|
144
|
+
}
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
type: 'string',
|
|
148
|
+
name: 'name',
|
|
149
|
+
record: { editable: false }
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
type: 'string',
|
|
153
|
+
name: 'meta',
|
|
154
|
+
record: { editable: false }
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
type: 'gutter',
|
|
158
|
+
gutterName: 'button',
|
|
159
|
+
name: 'edit',
|
|
160
|
+
/* Edit first, the star last: the star is the one that can stay lit at rest, so it takes
|
|
161
|
+
* the corner where the eye lands. */
|
|
162
|
+
icon: 'edit',
|
|
163
|
+
quiet: true,
|
|
164
|
+
title: i18next.t('label.edit', { defaultValue: 'Edit' }),
|
|
165
|
+
handlers: {
|
|
166
|
+
click: (_columns, _data, _column, record) => this.onInfoBoard(record)
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
type: 'gutter',
|
|
171
|
+
gutterName: 'button',
|
|
172
|
+
name: 'favorite',
|
|
173
|
+
icon: (record) => (record?.favorited ? 'star' : 'star_border'),
|
|
174
|
+
/* A set star is a status and stays; an unset one is an action and waits for hover. */
|
|
175
|
+
quiet: (record) => !record?.favorited,
|
|
176
|
+
title: (record) => i18next.t(record?.favorited ? 'label.unfavorite' : 'label.favorite', {
|
|
177
|
+
defaultValue: record?.favorited ? 'Remove from favorites' : 'Add to favorites'
|
|
178
|
+
}),
|
|
179
|
+
handlers: {
|
|
180
|
+
click: (_columns, _data, _column, record) => this.toggleFavorite(record)
|
|
181
|
+
}
|
|
182
|
+
},
|
|
183
|
+
/*
|
|
184
|
+
* Group and board-type are filter-only facets - they carry no `list.fields`/`list.details`
|
|
185
|
+
* entry, so record-card.ts never draws them on a card. `filters-form`'s delegated `change`
|
|
186
|
+
* listener (filters-form.ts) turns any change into a `fetch-params-change` event that
|
|
187
|
+
* data-provider.ts already listens for, so no extra wiring is needed to refetch.
|
|
188
|
+
*/
|
|
189
|
+
{
|
|
190
|
+
type: 'string',
|
|
191
|
+
name: 'group',
|
|
192
|
+
header: i18next.t('field.group', { defaultValue: 'Group' }),
|
|
193
|
+
record: { editable: false },
|
|
194
|
+
filter: {
|
|
195
|
+
type: 'select',
|
|
196
|
+
operator: 'eq',
|
|
197
|
+
value: '',
|
|
198
|
+
label: i18next.t('field.group', { defaultValue: 'Group' }),
|
|
199
|
+
onchange: (value) => {
|
|
200
|
+
this.currentGroupId = value;
|
|
201
|
+
return true;
|
|
202
|
+
},
|
|
203
|
+
options: () => [
|
|
204
|
+
{ value: '', display: i18next.t('label.all', { defaultValue: 'All' }) },
|
|
205
|
+
{ value: 'favor', display: i18next.t('label.favorite', { defaultValue: 'Favorite' }) },
|
|
206
|
+
{ value: 'mywork', display: i18next.t('label.my-works', { defaultValue: 'My works' }) },
|
|
207
|
+
...this.groups.map(group => ({ value: group.id, display: group.name }))
|
|
208
|
+
]
|
|
209
|
+
}
|
|
210
|
+
},
|
|
211
|
+
/*
|
|
212
|
+
* Type and state are chips (select-buttons), the way figure's draft/released facet is - a
|
|
213
|
+
* facet with two or three values is read faster as a row of toggles than as a checkbox
|
|
214
|
+
* or a dropdown. Both are multi-select ('in'), and "Main" starts pressed so the default
|
|
215
|
+
* view is the same main-boards-only list as before.
|
|
216
|
+
*/
|
|
217
|
+
{
|
|
218
|
+
type: 'select-buttons',
|
|
219
|
+
name: 'type',
|
|
220
|
+
header: i18next.t('field.board-type', { defaultValue: 'Type' }),
|
|
221
|
+
record: { editable: false },
|
|
222
|
+
filter: {
|
|
223
|
+
operator: 'in',
|
|
224
|
+
value: ['main'],
|
|
225
|
+
label: '',
|
|
226
|
+
options: [
|
|
227
|
+
{ display: i18next.t('label.main', { defaultValue: 'Main' }), value: 'main' },
|
|
228
|
+
{ display: i18next.t('label.sub', { defaultValue: 'Sub' }), value: 'sub' },
|
|
229
|
+
{ display: i18next.t('label.popup', { defaultValue: 'Popup' }), value: 'popup' }
|
|
230
|
+
]
|
|
231
|
+
}
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
type: 'select-buttons',
|
|
235
|
+
name: 'state',
|
|
236
|
+
header: i18next.t('field.state', { defaultValue: 'State' }),
|
|
237
|
+
record: { editable: false },
|
|
238
|
+
filter: {
|
|
239
|
+
operator: 'in',
|
|
240
|
+
value: [],
|
|
241
|
+
label: '',
|
|
242
|
+
options: [
|
|
243
|
+
{ display: i18next.t('label.draft', { defaultValue: 'Draft' }), value: 'draft' },
|
|
244
|
+
{ display: i18next.t('label.released', { defaultValue: 'Released' }), value: 'released' }
|
|
245
|
+
]
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
],
|
|
249
|
+
rows: {
|
|
250
|
+
selectable: false,
|
|
251
|
+
appendable: false,
|
|
252
|
+
reorderable: true,
|
|
253
|
+
handlers: {
|
|
254
|
+
click: (_columns, _data, _column, record) => navigate(`board-viewer/${record.id}`)
|
|
255
|
+
}
|
|
256
|
+
},
|
|
257
|
+
sorters: []
|
|
258
|
+
};
|
|
259
|
+
this.refresh();
|
|
260
|
+
}
|
|
261
|
+
async fetchHandler({ page = 1, limit = 24, filters = [] }) {
|
|
262
|
+
const groupId = filters.find(f => f.name === 'group')?.value || '';
|
|
263
|
+
const types = filters.find(f => f.name === 'type')?.value || [];
|
|
264
|
+
const states = filters.find(f => f.name === 'state')?.value || [];
|
|
265
|
+
const { items, total } = await this.getBoards({ page, limit, groupId, types, states });
|
|
266
|
+
const favorites = this.favorites || [];
|
|
267
|
+
const records = (items || []).map(board => ({
|
|
268
|
+
...board,
|
|
269
|
+
favorited: favorites.includes(board.id),
|
|
270
|
+
meta: boardMeta(board)
|
|
271
|
+
}));
|
|
272
|
+
return { total, records };
|
|
273
|
+
}
|
|
125
274
|
async refresh() {
|
|
126
275
|
this.groups = (await fetchGroupList()).groups.items;
|
|
127
276
|
if (this.groups) {
|
|
277
|
+
/* New reference - `options()` on the group filter closes over `this.groups`, but the filter
|
|
278
|
+
* widget only re-reads it if `<ox-grist>`'s own `.config` binding actually changes. */
|
|
279
|
+
this.gristConfig = { ...this.gristConfig };
|
|
128
280
|
await this.refreshBoards();
|
|
129
281
|
}
|
|
130
282
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
}
|
|
135
|
-
async getBoards({ page = 1, limit = this._infiniteScrollOptions.limit } = {}) {
|
|
136
|
-
if (this.groupId && this.groupId == 'favor') {
|
|
137
|
-
return await this.getFavoriteBoards({
|
|
138
|
-
page,
|
|
139
|
-
limit
|
|
140
|
-
});
|
|
283
|
+
async getBoards({ page = 1, limit = 24, groupId = '', types = [], states = [] } = {}) {
|
|
284
|
+
if (groupId === 'favor') {
|
|
285
|
+
return await this.getFavoriteBoards({ page, limit, types, states });
|
|
141
286
|
}
|
|
142
|
-
else if (
|
|
143
|
-
return await this.getMyBoards({
|
|
144
|
-
page,
|
|
145
|
-
limit
|
|
146
|
-
});
|
|
287
|
+
else if (groupId === 'mywork') {
|
|
288
|
+
return await this.getMyBoards({ page, limit, types, states });
|
|
147
289
|
}
|
|
148
290
|
const filters = [];
|
|
149
|
-
if (
|
|
291
|
+
if (groupId) {
|
|
150
292
|
filters.push({
|
|
151
293
|
name: 'groupId',
|
|
152
294
|
operator: 'eq',
|
|
153
|
-
value:
|
|
295
|
+
value: groupId
|
|
154
296
|
});
|
|
155
297
|
}
|
|
156
298
|
if (this.searchText) {
|
|
@@ -160,11 +302,18 @@ let BoardListPage = class BoardListPage extends BoardListPageBase {
|
|
|
160
302
|
value: '%' + this.searchText + '%'
|
|
161
303
|
});
|
|
162
304
|
}
|
|
163
|
-
if (
|
|
305
|
+
if (types.length) {
|
|
164
306
|
filters.push({
|
|
165
307
|
name: 'type',
|
|
166
|
-
operator: '
|
|
167
|
-
value:
|
|
308
|
+
operator: 'in',
|
|
309
|
+
value: types
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
if (states.length) {
|
|
313
|
+
filters.push({
|
|
314
|
+
name: 'state',
|
|
315
|
+
operator: 'in',
|
|
316
|
+
value: states
|
|
168
317
|
});
|
|
169
318
|
}
|
|
170
319
|
const listParam = {
|
|
@@ -190,8 +339,16 @@ let BoardListPage = class BoardListPage extends BoardListPageBase {
|
|
|
190
339
|
* favor/mywork 같이 "search text + pagination"만 받는 간단한 리스트 조회 공용 헬퍼.
|
|
191
340
|
* 그룹/타입 필터 등 복합 조건을 쓰는 일반 목록(getBoards)과 구분해 중복 제거.
|
|
192
341
|
*/
|
|
193
|
-
async _fetchSimpleBoardList(fetcher, resultKey, { page, limit }) {
|
|
342
|
+
async _fetchSimpleBoardList(fetcher, resultKey, { page, limit, types = [], states = [] }) {
|
|
343
|
+
/* The type and state chips apply here too - the server narrows these lists with the same
|
|
344
|
+
* filters as the plain board list, so a pressed chip means the same thing in every view. */
|
|
194
345
|
const filters = [];
|
|
346
|
+
if (types.length) {
|
|
347
|
+
filters.push({ name: 'type', operator: 'in', value: types });
|
|
348
|
+
}
|
|
349
|
+
if (states.length) {
|
|
350
|
+
filters.push({ name: 'state', operator: 'in', value: states });
|
|
351
|
+
}
|
|
195
352
|
if (this.searchText) {
|
|
196
353
|
filters.push({
|
|
197
354
|
name: 'name',
|
|
@@ -202,129 +359,30 @@ let BoardListPage = class BoardListPage extends BoardListPageBase {
|
|
|
202
359
|
const result = await fetcher({ filters, pagination: { page, limit } });
|
|
203
360
|
return result[resultKey];
|
|
204
361
|
}
|
|
205
|
-
async getFavoriteBoards({ page = 1, limit =
|
|
206
|
-
return this._fetchSimpleBoardList(fetchFavoriteBoardList, 'favoriteBoards', { page, limit });
|
|
362
|
+
async getFavoriteBoards({ page = 1, limit = 24, types = [], states = [] } = {}) {
|
|
363
|
+
return this._fetchSimpleBoardList(fetchFavoriteBoardList, 'favoriteBoards', { page, limit, types, states });
|
|
207
364
|
}
|
|
208
|
-
async getMyBoards({ page = 1, limit =
|
|
209
|
-
return this._fetchSimpleBoardList(fetchMyBoardList, 'boardsCreatedByMe', { page, limit });
|
|
365
|
+
async getMyBoards({ page = 1, limit = 24, types = [], states = [] } = {}) {
|
|
366
|
+
return this._fetchSimpleBoardList(fetchMyBoardList, 'boardsCreatedByMe', { page, limit, types, states });
|
|
210
367
|
}
|
|
211
368
|
async refreshBoards() {
|
|
212
369
|
if (!this.groups) {
|
|
213
370
|
await this.refresh();
|
|
214
371
|
return;
|
|
215
372
|
}
|
|
216
|
-
const { items: boards, total } = await this.getBoards();
|
|
217
|
-
this.boards = boards;
|
|
218
|
-
this._page = 1;
|
|
219
|
-
this._total = total;
|
|
220
|
-
this._lastFetchTime = new Date();
|
|
221
373
|
this.updateContext();
|
|
222
|
-
this.
|
|
223
|
-
this.
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
async fillViewport() {
|
|
227
|
-
if (this._filling)
|
|
228
|
-
return;
|
|
229
|
-
this._filling = true;
|
|
230
|
-
try {
|
|
231
|
-
const el = this.scrollTargetEl;
|
|
232
|
-
if (!el)
|
|
233
|
-
return;
|
|
234
|
-
const { limit } = this._infiniteScrollOptions;
|
|
235
|
-
while (this._page < this._total / limit && el.scrollHeight <= el.clientHeight) {
|
|
236
|
-
await this.appendBoards();
|
|
237
|
-
await this.updateComplete;
|
|
238
|
-
}
|
|
374
|
+
this.grist?.fetch();
|
|
375
|
+
if (this.grist) {
|
|
376
|
+
this.grist.style.transition = '';
|
|
377
|
+
this.grist.style.transform = `translate3d(0, 0, 0)`;
|
|
239
378
|
}
|
|
240
|
-
finally {
|
|
241
|
-
this._filling = false;
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
/**
|
|
245
|
-
* 기존 목록을 유지하면서 변경된 보드만 갱신한다.
|
|
246
|
-
* 스크롤 위치가 보존된다.
|
|
247
|
-
*/
|
|
248
|
-
async incrementalRefresh() {
|
|
249
|
-
if (!this._lastFetchTime || this.boards.length === 0) {
|
|
250
|
-
return this.refreshBoards();
|
|
251
|
-
}
|
|
252
|
-
// boardsUpdatedSince 는 *전역* 델타라, 즐겨찾기/내작업/검색 모드는 소속·검색일치를
|
|
253
|
-
// 클라이언트에서 판별할 수 없다 → 정확성 위해 전체 갱신으로 폴백.
|
|
254
|
-
// (일반 그룹/전체 보기에서만 증분 병합으로 스크롤을 유지한다.)
|
|
255
|
-
if (this.groupId === 'favor' || this.groupId === 'mywork' || this.searchText) {
|
|
256
|
-
return this.refreshBoards();
|
|
257
|
-
}
|
|
258
|
-
const updatedBoards = await fetchBoardsUpdatedSince(this._lastFetchTime);
|
|
259
|
-
if (updatedBoards && updatedBoards.length > 0) {
|
|
260
|
-
const updatedMap = new Map(updatedBoards.map((b) => [b.id, b]));
|
|
261
|
-
const existingIds = new Set(this.boards.map(b => b.id));
|
|
262
|
-
const changedIds = [];
|
|
263
|
-
// 현재 보기(그룹/타입 필터)에 속하는 살아있는 보드인지
|
|
264
|
-
const belongsToView = (b) => !b.deletedAt &&
|
|
265
|
-
(!this.groupId || b.group?.id === this.groupId) &&
|
|
266
|
-
(!this.showMainBoardOnly || b.type === 'main');
|
|
267
|
-
// 1) 기존 항목: 변경분 갱신, 삭제·그룹이동·타입이탈은 제거
|
|
268
|
-
const merged = this.boards
|
|
269
|
-
.map(board => {
|
|
270
|
-
const updated = updatedMap.get(board.id);
|
|
271
|
-
if (!updated)
|
|
272
|
-
return board;
|
|
273
|
-
if (!belongsToView(updated))
|
|
274
|
-
return null;
|
|
275
|
-
changedIds.push(updated.id);
|
|
276
|
-
return updated;
|
|
277
|
-
})
|
|
278
|
-
.filter(Boolean);
|
|
279
|
-
const removedCount = this.boards.length - merged.length;
|
|
280
|
-
// 2) 신규: 기존 리스트에 없고 현재 보기에 속하는 보드를 삽입 (새로 만든 보드가 여기 해당)
|
|
281
|
-
const inserted = updatedBoards.filter((b) => !existingIds.has(b.id) && belongsToView(b));
|
|
282
|
-
for (const b of inserted)
|
|
283
|
-
changedIds.push(b.id);
|
|
284
|
-
// 3) 서버 정렬(sortOrder asc, name asc) 유지 — 새 항목이 제 위치에 끼어들고 기존 순서/스크롤 보존
|
|
285
|
-
this.boards = [...merged, ...inserted].sort((a, b) => (a.sortOrder ?? 0) - (b.sortOrder ?? 0) ||
|
|
286
|
-
String(a.name ?? '').localeCompare(String(b.name ?? '')));
|
|
287
|
-
this._total += inserted.length - removedCount;
|
|
288
|
-
this._updatedBoardIds = changedIds;
|
|
289
|
-
}
|
|
290
|
-
this._lastFetchTime = new Date();
|
|
291
|
-
}
|
|
292
|
-
async appendBoards() {
|
|
293
|
-
if (!this.groups) {
|
|
294
|
-
await this.refresh();
|
|
295
|
-
return;
|
|
296
|
-
}
|
|
297
|
-
const { items: appendedBoards, total } = await this.getBoards({ page: this._page + 1 });
|
|
298
|
-
const existingIds = new Set(this.boards.map(b => b.id));
|
|
299
|
-
const newBoards = appendedBoards.filter(b => !existingIds.has(b.id));
|
|
300
|
-
this.boards = [...this.boards, ...newBoards];
|
|
301
|
-
this._page = this._page + 1;
|
|
302
|
-
this._total = total;
|
|
303
|
-
this._lastFetchTime = new Date();
|
|
304
|
-
}
|
|
305
|
-
async scrollAction() {
|
|
306
|
-
return this.appendBoards();
|
|
307
|
-
}
|
|
308
|
-
async pageInitialized() {
|
|
309
|
-
this.refresh();
|
|
310
379
|
}
|
|
311
380
|
async pageUpdated(changes, lifecycle) {
|
|
312
381
|
if (this.active) {
|
|
313
|
-
const
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
this.searchText = lifecycle.params?.search || '';
|
|
318
|
-
await this.updateComplete;
|
|
319
|
-
// 검색어가 변경되었으면 전체 갱신
|
|
320
|
-
// 같은 그룹으로 복귀하고 기존 데이터가 있으면 변경분만 갱신 (스크롤 유지)
|
|
321
|
-
if (prevSearchText !== this.searchText) {
|
|
322
|
-
this.refreshBoards();
|
|
323
|
-
}
|
|
324
|
-
else if (prevGroupId === this.groupId && this.boards.length > 0 && this._lastFetchTime) {
|
|
325
|
-
this.incrementalRefresh();
|
|
326
|
-
}
|
|
327
|
-
else {
|
|
382
|
+
const searchText = lifecycle.params?.search || '';
|
|
383
|
+
if (searchText !== this.searchText) {
|
|
384
|
+
this.searchText = searchText;
|
|
385
|
+
await this.updateComplete;
|
|
328
386
|
this.refreshBoards();
|
|
329
387
|
}
|
|
330
388
|
}
|
|
@@ -340,58 +398,35 @@ let BoardListPage = class BoardListPage extends BoardListPageBase {
|
|
|
340
398
|
}
|
|
341
399
|
_applyState() {
|
|
342
400
|
const __state__ = store.getState();
|
|
343
|
-
|
|
401
|
+
const favorites = __state__.favorite.favorites;
|
|
402
|
+
/*
|
|
403
|
+
* `store.subscribe` fires on every dispatch anywhere in the app, not just favorite changes -
|
|
404
|
+
* `favorited` is folded into each record inside `fetchHandler`, so refreshing it means a
|
|
405
|
+
* refetch, and an unconditional one here span a real loop: fetch() completing dispatches through
|
|
406
|
+
* this same store, which re-enters this callback, which calls fetch() again.
|
|
407
|
+
*/
|
|
408
|
+
const changed = JSON.stringify(favorites) !== JSON.stringify(this.favorites);
|
|
409
|
+
this.favorites = favorites;
|
|
410
|
+
if (changed) {
|
|
411
|
+
this.grist?.fetch();
|
|
412
|
+
}
|
|
344
413
|
}
|
|
345
414
|
firstUpdated() {
|
|
346
|
-
|
|
415
|
+
/* Group used to be a route segment, and swiping moved between routes - now it is one filter
|
|
416
|
+
* among others in the same list, so there is no longer a sequence of screens to swipe between. */
|
|
347
417
|
pulltorefresh({
|
|
348
418
|
container: this.renderRoot,
|
|
349
|
-
scrollable:
|
|
419
|
+
scrollable: this.grist,
|
|
350
420
|
refresh: () => {
|
|
351
421
|
return this.refresh();
|
|
352
422
|
}
|
|
353
423
|
});
|
|
354
|
-
swipe({
|
|
355
|
-
container: list,
|
|
356
|
-
animates: {
|
|
357
|
-
dragging: async (d, opts) => {
|
|
358
|
-
const groups = [{ id: '' }, { id: 'favor' }, ...this.groups];
|
|
359
|
-
const currentIndex = groups.findIndex(group => group.id == this.groupId);
|
|
360
|
-
if ((d > 0 && currentIndex <= 0) || (d < 0 && currentIndex >= groups.length - 1)) {
|
|
361
|
-
/* TODO blocked gesture */
|
|
362
|
-
return false;
|
|
363
|
-
}
|
|
364
|
-
list.style.transform = `translate3d(${d}px, 0, 0)`;
|
|
365
|
-
},
|
|
366
|
-
aborting: async (opts) => {
|
|
367
|
-
list.style.transition = 'transform 0.3s';
|
|
368
|
-
list.style.transform = `translate3d(0, 0, 0)`;
|
|
369
|
-
setTimeout(() => {
|
|
370
|
-
list.style.transition = '';
|
|
371
|
-
});
|
|
372
|
-
},
|
|
373
|
-
swiping: async (d, opts) => {
|
|
374
|
-
const groups = [{ id: '' }, { id: 'favor' }, ...this.groups];
|
|
375
|
-
const currentIndex = groups.findIndex(group => group.id == this.groupId);
|
|
376
|
-
if ((d > 0 && currentIndex <= 0) || (d < 0 && currentIndex >= groups.length - 1)) {
|
|
377
|
-
list.style.transition = '';
|
|
378
|
-
list.style.transform = `translate3d(0, 0, 0)`;
|
|
379
|
-
}
|
|
380
|
-
else {
|
|
381
|
-
list.style.transition = 'transform 0.3s';
|
|
382
|
-
list.style.transform = `translate3d(${d < 0 ? '-100%' : '100%'}, 0, 0)`;
|
|
383
|
-
navigate(`${this.page}/${groups[currentIndex + (d < 0 ? 1 : -1)].id}${this.searchText ? '?search=' + this.searchText : ''}`);
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
});
|
|
388
424
|
}
|
|
389
425
|
async onInfoBoard(board) {
|
|
390
|
-
|
|
391
|
-
template: html `
|
|
426
|
+
openPopup(html `
|
|
392
427
|
<board-info
|
|
393
428
|
.board=${board}
|
|
394
|
-
.groupId=${this.
|
|
429
|
+
.groupId=${this.currentGroupId}
|
|
395
430
|
@clone-board=${e => this.onCloneBoard(e.detail)}
|
|
396
431
|
@release-board=${e => this.onReleaseBoard(e.detail)}
|
|
397
432
|
@revert-board-version=${e => this.onRevertBoardVersion(e.detail)}
|
|
@@ -401,12 +436,11 @@ let BoardListPage = class BoardListPage extends BoardListPageBase {
|
|
|
401
436
|
@leave-playgroup=${e => this.onLeavePlayGroup(e.detail)}
|
|
402
437
|
@register-template=${e => this.onRegisterTemplate(e.detail)}
|
|
403
438
|
></board-info>
|
|
404
|
-
|
|
405
|
-
});
|
|
439
|
+
`, { backdrop: true, size: 'medium', closable: true, title: board.name });
|
|
406
440
|
}
|
|
407
441
|
async onInfoGroup(groupId) {
|
|
408
|
-
|
|
409
|
-
|
|
442
|
+
const group = this.groups.find(group => group.id == groupId);
|
|
443
|
+
openPopup(html `
|
|
410
444
|
<group-info
|
|
411
445
|
.groupId=${groupId}
|
|
412
446
|
@update-group=${e => this.onUpdateGroup(e.detail)}
|
|
@@ -414,7 +448,11 @@ let BoardListPage = class BoardListPage extends BoardListPageBase {
|
|
|
414
448
|
@create-group=${e => this.onCreateGroup(e.detail)}
|
|
415
449
|
@import-boards=${e => this.onImportBoards(e.detail)}
|
|
416
450
|
></group-info>
|
|
417
|
-
|
|
451
|
+
`, {
|
|
452
|
+
backdrop: true,
|
|
453
|
+
size: 'small',
|
|
454
|
+
closable: true,
|
|
455
|
+
title: group?.name || i18next.t('text.new group', { defaultValue: 'New Group' })
|
|
418
456
|
});
|
|
419
457
|
}
|
|
420
458
|
async onCreateGroup(group) {
|
|
@@ -457,13 +495,12 @@ let BoardListPage = class BoardListPage extends BoardListPageBase {
|
|
|
457
495
|
}
|
|
458
496
|
this.refresh();
|
|
459
497
|
}
|
|
460
|
-
async onReorderBoards({
|
|
498
|
+
async onReorderBoards({ ids, movedId }) {
|
|
461
499
|
if (!movedId)
|
|
462
500
|
return;
|
|
463
|
-
const newIndex =
|
|
464
|
-
const prevId = newIndex > 0 ?
|
|
465
|
-
const nextId = newIndex <
|
|
466
|
-
this.boards = boardIds.map(id => this.boards.find(b => b.id === id)).filter(Boolean);
|
|
501
|
+
const newIndex = ids.indexOf(movedId);
|
|
502
|
+
const prevId = newIndex > 0 ? ids[newIndex - 1] : undefined;
|
|
503
|
+
const nextId = newIndex < ids.length - 1 ? ids[newIndex + 1] : undefined;
|
|
467
504
|
try {
|
|
468
505
|
await client.mutate({
|
|
469
506
|
mutation: gql `
|
|
@@ -509,7 +546,7 @@ let BoardListPage = class BoardListPage extends BoardListPageBase {
|
|
|
509
546
|
catch (ex) {
|
|
510
547
|
notify('error', ex, ex);
|
|
511
548
|
}
|
|
512
|
-
this.
|
|
549
|
+
this.refreshBoards();
|
|
513
550
|
}
|
|
514
551
|
async onReleaseBoard(board) {
|
|
515
552
|
try {
|
|
@@ -519,7 +556,7 @@ let BoardListPage = class BoardListPage extends BoardListPageBase {
|
|
|
519
556
|
catch (ex) {
|
|
520
557
|
notify('error', ex, ex);
|
|
521
558
|
}
|
|
522
|
-
this.
|
|
559
|
+
this.refreshBoards();
|
|
523
560
|
}
|
|
524
561
|
async onRevertBoardVersion({ id, version }) {
|
|
525
562
|
try {
|
|
@@ -529,7 +566,7 @@ let BoardListPage = class BoardListPage extends BoardListPageBase {
|
|
|
529
566
|
catch (ex) {
|
|
530
567
|
notify('error', ex, ex);
|
|
531
568
|
}
|
|
532
|
-
this.
|
|
569
|
+
this.refreshBoards();
|
|
533
570
|
}
|
|
534
571
|
async onUpdateBoard(board) {
|
|
535
572
|
try {
|
|
@@ -539,7 +576,7 @@ let BoardListPage = class BoardListPage extends BoardListPageBase {
|
|
|
539
576
|
catch (ex) {
|
|
540
577
|
notify('error', ex, ex);
|
|
541
578
|
}
|
|
542
|
-
this.
|
|
579
|
+
this.refreshBoards();
|
|
543
580
|
}
|
|
544
581
|
async onDeleteBoard(board) {
|
|
545
582
|
try {
|
|
@@ -549,7 +586,7 @@ let BoardListPage = class BoardListPage extends BoardListPageBase {
|
|
|
549
586
|
catch (ex) {
|
|
550
587
|
notify('error', ex, ex);
|
|
551
588
|
}
|
|
552
|
-
this.
|
|
589
|
+
this.refreshBoards();
|
|
553
590
|
}
|
|
554
591
|
async onJoinPlayGroup({ board, playGroup }) {
|
|
555
592
|
try {
|
|
@@ -621,6 +658,78 @@ let BoardListPage = class BoardListPage extends BoardListPageBase {
|
|
|
621
658
|
notify('error', ex, ex);
|
|
622
659
|
}
|
|
623
660
|
}
|
|
661
|
+
async toggleFavorite(board) {
|
|
662
|
+
const wasFavorite = (this.favorites || []).includes(board.id);
|
|
663
|
+
/*
|
|
664
|
+
* The star answers the click at once; the server round trip only confirms it. Before, nothing
|
|
665
|
+
* moved until the mutation, the favorites re-query, the store dispatch and a full refetch had
|
|
666
|
+
* all come back - a click with no reaction reads as a click that did not land.
|
|
667
|
+
*
|
|
668
|
+
* `favorites` is set locally too, so the store update that follows carries the same list and
|
|
669
|
+
* `_applyState` does not trigger the refetch that would repaint every card.
|
|
670
|
+
*/
|
|
671
|
+
this.favorites = wasFavorite
|
|
672
|
+
? (this.favorites || []).filter(id => id !== board.id)
|
|
673
|
+
: [...(this.favorites || []), board.id];
|
|
674
|
+
this.patchRecord(board.id, { favorited: !wasFavorite });
|
|
675
|
+
try {
|
|
676
|
+
if (wasFavorite) {
|
|
677
|
+
await this.removeFavorite(board.id);
|
|
678
|
+
}
|
|
679
|
+
else {
|
|
680
|
+
await this.addFavorite(board.id);
|
|
681
|
+
}
|
|
682
|
+
/* In the Favorite view an unstarred board no longer belongs on the page: refetch so it leaves. */
|
|
683
|
+
if (this.currentGroupId === 'favor') {
|
|
684
|
+
this.grist?.fetch();
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
catch (ex) {
|
|
688
|
+
/* Put it back the way it was and say why. */
|
|
689
|
+
this.favorites = wasFavorite
|
|
690
|
+
? [...(this.favorites || []), board.id]
|
|
691
|
+
: (this.favorites || []).filter(id => id !== board.id);
|
|
692
|
+
this.patchRecord(board.id, { favorited: wasFavorite });
|
|
693
|
+
notify('error', ex?.message || String(ex));
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
/** Replaces one record in the grist in place, so only that card re-renders. */
|
|
697
|
+
patchRecord(id, patch) {
|
|
698
|
+
const grist = this.grist;
|
|
699
|
+
const data = grist?.data;
|
|
700
|
+
if (!data?.records) {
|
|
701
|
+
return;
|
|
702
|
+
}
|
|
703
|
+
grist.data = {
|
|
704
|
+
...data,
|
|
705
|
+
records: data.records.map(record => (record.id === id ? { ...record, ...patch } : record))
|
|
706
|
+
};
|
|
707
|
+
}
|
|
708
|
+
async removeFavorite(boardId) {
|
|
709
|
+
await client.mutate({
|
|
710
|
+
mutation: gql `
|
|
711
|
+
mutation {
|
|
712
|
+
deleteFavorite(routing: "${boardId}")
|
|
713
|
+
}
|
|
714
|
+
`
|
|
715
|
+
});
|
|
716
|
+
this.refreshFavorites();
|
|
717
|
+
}
|
|
718
|
+
async addFavorite(boardId) {
|
|
719
|
+
await client.mutate({
|
|
720
|
+
mutation: gql `
|
|
721
|
+
mutation {
|
|
722
|
+
createFavorite(favorite: {
|
|
723
|
+
routing: "${boardId}"
|
|
724
|
+
}) {
|
|
725
|
+
id
|
|
726
|
+
routing
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
`
|
|
730
|
+
});
|
|
731
|
+
this.refreshFavorites();
|
|
732
|
+
}
|
|
624
733
|
async refreshFavorites() {
|
|
625
734
|
const { data } = await client.query({
|
|
626
735
|
query: gql `
|
|
@@ -639,41 +748,36 @@ let BoardListPage = class BoardListPage extends BoardListPageBase {
|
|
|
639
748
|
});
|
|
640
749
|
}
|
|
641
750
|
};
|
|
642
|
-
__decorate([
|
|
643
|
-
property({ type: String }),
|
|
644
|
-
__metadata("design:type", String)
|
|
645
|
-
], BoardListPage.prototype, "groupId", void 0);
|
|
646
751
|
__decorate([
|
|
647
752
|
property({ type: Array }),
|
|
648
753
|
__metadata("design:type", Array)
|
|
649
754
|
], BoardListPage.prototype, "groups", void 0);
|
|
650
|
-
__decorate([
|
|
651
|
-
property({ type: Array }),
|
|
652
|
-
__metadata("design:type", Array)
|
|
653
|
-
], BoardListPage.prototype, "boards", void 0);
|
|
654
755
|
__decorate([
|
|
655
756
|
property({ type: Array }),
|
|
656
757
|
__metadata("design:type", Array)
|
|
657
758
|
], BoardListPage.prototype, "favorites", void 0);
|
|
658
759
|
__decorate([
|
|
659
|
-
|
|
660
|
-
__metadata("design:type",
|
|
661
|
-
], BoardListPage.prototype, "
|
|
760
|
+
property({ type: Object }),
|
|
761
|
+
__metadata("design:type", Object)
|
|
762
|
+
], BoardListPage.prototype, "gristConfig", void 0);
|
|
662
763
|
__decorate([
|
|
663
764
|
state(),
|
|
664
765
|
__metadata("design:type", String)
|
|
665
766
|
], BoardListPage.prototype, "searchText", void 0);
|
|
666
767
|
__decorate([
|
|
667
|
-
|
|
668
|
-
__metadata("design:type",
|
|
669
|
-
], BoardListPage.prototype, "
|
|
768
|
+
state(),
|
|
769
|
+
__metadata("design:type", Object)
|
|
770
|
+
], BoardListPage.prototype, "creatable", void 0);
|
|
670
771
|
__decorate([
|
|
671
772
|
state(),
|
|
672
|
-
__metadata("design:type",
|
|
673
|
-
], BoardListPage.prototype, "
|
|
773
|
+
__metadata("design:type", String)
|
|
774
|
+
], BoardListPage.prototype, "currentGroupId", void 0);
|
|
775
|
+
__decorate([
|
|
776
|
+
query('ox-grist'),
|
|
777
|
+
__metadata("design:type", DataGrist)
|
|
778
|
+
], BoardListPage.prototype, "grist", void 0);
|
|
674
779
|
BoardListPage = __decorate([
|
|
675
|
-
customElement('board-list-page')
|
|
676
|
-
__metadata("design:paramtypes", [])
|
|
780
|
+
customElement('board-list-page')
|
|
677
781
|
], BoardListPage);
|
|
678
782
|
export { BoardListPage };
|
|
679
783
|
//# sourceMappingURL=board-list-page.js.map
|