lupine.api 1.1.57 → 1.1.59
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/README.md +3 -3
- package/admin/admin-about.tsx +12 -16
- package/admin/admin-config.tsx +47 -44
- package/admin/admin-css.tsx +3 -3
- package/admin/admin-db.tsx +75 -75
- package/admin/admin-frame-helper.tsx +364 -364
- package/admin/admin-frame.tsx +164 -164
- package/admin/admin-index.tsx +65 -65
- package/admin/admin-login.tsx +111 -111
- package/admin/admin-menu-edit.tsx +637 -637
- package/admin/admin-menu-list.tsx +87 -87
- package/admin/admin-page-edit.tsx +564 -564
- package/admin/admin-page-list.tsx +83 -83
- package/admin/admin-performance.tsx +28 -28
- package/admin/admin-release.tsx +427 -404
- package/admin/admin-resources.tsx +382 -382
- package/admin/admin-shell.tsx +89 -89
- package/admin/admin-table-data.tsx +146 -146
- package/admin/admin-table-list.tsx +230 -230
- package/admin/admin-test-animations.tsx +395 -395
- package/admin/admin-test-component.tsx +823 -808
- package/admin/admin-test-edit.tsx +319 -319
- package/admin/admin-test-themes.tsx +56 -56
- package/admin/admin-tokens.tsx +338 -338
- package/admin/design/admin-design.tsx +174 -174
- package/admin/design/block-grid.tsx +36 -36
- package/admin/design/block-grid1.tsx +21 -21
- package/admin/design/block-paragraph.tsx +19 -19
- package/admin/design/block-title.tsx +19 -19
- package/admin/design/design-block-box.tsx +140 -140
- package/admin/design/drag-data.tsx +24 -24
- package/admin/index.ts +9 -9
- package/admin/package.json +15 -15
- package/admin/tsconfig.json +127 -127
- package/dev/copy-folder.js +32 -32
- package/dev/cp-index-html.js +69 -69
- package/dev/file-utils.js +12 -12
- package/dev/index.js +18 -19
- package/dev/package.json +12 -12
- package/dev/plugin-ifelse.js +168 -168
- package/dev/plugin-ifelse.test.js +37 -37
- package/dev/run-cmd.js +14 -14
- package/dev/send-request.js +12 -12
- package/package.json +55 -55
- package/src/admin-api/admin-api-helper.ts +210 -205
- package/src/admin-api/admin-api.ts +65 -65
- package/src/admin-api/admin-auth.ts +152 -146
- package/src/admin-api/admin-config.ts +94 -84
- package/src/admin-api/admin-csv.ts +94 -94
- package/src/admin-api/admin-db.ts +269 -269
- package/src/admin-api/admin-menu.ts +135 -135
- package/src/admin-api/admin-page.ts +135 -135
- package/src/admin-api/admin-performance.ts +128 -128
- package/src/admin-api/admin-release.ts +703 -700
- package/src/admin-api/admin-resources.ts +318 -318
- package/src/admin-api/admin-token-helper.ts +82 -79
- package/src/admin-api/admin-tokens.ts +90 -90
- package/src/admin-api/index.ts +2 -2
- package/src/admin-api/web-config-api.ts +19 -19
- package/src/api/api-cache.ts +103 -103
- package/src/api/api-helper.ts +44 -44
- package/src/api/api-module.ts +67 -60
- package/src/api/api-router.ts +177 -177
- package/src/api/api-shared-storage.ts +64 -64
- package/src/api/async-storage.ts +5 -5
- package/src/api/debug-service.ts +56 -56
- package/src/api/encode-html.ts +27 -27
- package/src/api/handle-status.ts +75 -75
- package/src/api/index.ts +15 -16
- package/src/api/mini-web-socket.ts +270 -270
- package/src/api/server-content-type.ts +82 -82
- package/src/api/server-render.ts +235 -215
- package/src/api/shell-service.ts +74 -74
- package/src/api/simple-storage.ts +80 -80
- package/src/api/static-server.ts +128 -125
- package/src/api/to-client-delivery.ts +26 -26
- package/src/app/app-cache.ts +55 -55
- package/src/app/app-helper.ts +62 -62
- package/src/app/app-message.ts +109 -109
- package/src/app/app-shared-storage.ts +363 -363
- package/src/app/app-start.ts +136 -136
- package/src/app/cleanup-exit.ts +16 -16
- package/src/app/host-to-path.ts +38 -38
- package/src/app/index.ts +11 -11
- package/src/app/process-dev-requests.ts +130 -130
- package/src/app/web-listener.ts +294 -294
- package/src/app/web-processor.ts +47 -42
- package/src/app/web-server.ts +100 -100
- package/src/common-js/web-env.js +104 -104
- package/src/index.ts +7 -7
- package/src/lang/api-lang-en.ts +26 -26
- package/src/lang/api-lang-zh-cn.ts +27 -27
- package/src/lang/index.ts +2 -2
- package/src/lang/lang-helper.ts +76 -76
- package/src/lang/lang-props.ts +6 -6
- package/src/lib/db/db-helper.ts +23 -23
- package/src/lib/db/db-mysql.ts +249 -250
- package/src/lib/db/db-sqlite.ts +101 -101
- package/src/lib/db/db.spec.ts +28 -28
- package/src/lib/db/db.ts +325 -325
- package/src/lib/db/index.ts +5 -5
- package/src/lib/index.ts +3 -3
- package/src/lib/logger.spec.ts +214 -214
- package/src/lib/logger.ts +281 -281
- package/src/lib/runtime-require.ts +37 -37
- package/src/lib/utils/cookie-util.ts +34 -34
- package/src/lib/utils/crypto.ts +58 -58
- package/src/lib/utils/date-utils.ts +317 -317
- package/src/lib/utils/deep-merge.ts +37 -37
- package/src/lib/utils/delay.ts +12 -12
- package/src/lib/utils/file-setting.ts +55 -55
- package/src/lib/utils/format-bytes.ts +11 -11
- package/src/lib/utils/fs-utils.ts +158 -158
- package/src/lib/utils/get-env.ts +27 -27
- package/src/lib/utils/index.ts +12 -12
- package/src/lib/utils/is-type.ts +48 -48
- package/src/lib/utils/load-env.ts +14 -14
- package/src/lib/utils/pad.ts +6 -6
- package/src/models/api-base.ts +5 -5
- package/src/models/api-module-props.ts +10 -11
- package/src/models/api-router-props.ts +26 -26
- package/src/models/app-cache-props.ts +33 -33
- package/src/models/app-data-props.ts +10 -10
- package/src/models/app-helper-props.ts +6 -6
- package/src/models/app-shared-storage-props.ts +38 -38
- package/src/models/app-start-props.ts +18 -18
- package/src/models/async-storage-props.ts +13 -13
- package/src/models/db-config.ts +30 -30
- package/src/models/host-to-path-props.ts +12 -12
- package/src/models/index.ts +16 -16
- package/src/models/json-object.ts +8 -8
- package/src/models/locals-props.ts +36 -36
- package/src/models/logger-props.ts +84 -84
- package/src/models/simple-storage-props.ts +13 -14
- package/src/models/to-client-delivery-props.ts +6 -6
- package/tsconfig.json +115 -115
- package/dev/plugin-gen-versions.js +0 -20
|
@@ -1,319 +1,319 @@
|
|
|
1
|
-
import {
|
|
2
|
-
CssProps,
|
|
3
|
-
RefProps,
|
|
4
|
-
EditableLabel,
|
|
5
|
-
HtmlVar,
|
|
6
|
-
ModalWindow,
|
|
7
|
-
NotificationColor,
|
|
8
|
-
NotificationMessage,
|
|
9
|
-
PagingLink,
|
|
10
|
-
ToggleBaseHookProps,
|
|
11
|
-
ToggleSwitch,
|
|
12
|
-
ToggleSwitchSize,
|
|
13
|
-
} from 'lupine.components';
|
|
14
|
-
|
|
15
|
-
type SampleDataProps = {
|
|
16
|
-
id: number;
|
|
17
|
-
name: string;
|
|
18
|
-
info: string;
|
|
19
|
-
checked: boolean;
|
|
20
|
-
};
|
|
21
|
-
const _DEFAULT_PAGE_LIMIT = 10;
|
|
22
|
-
const _DEFAULT_ITEM_COUNT = 101;
|
|
23
|
-
const sampleData: SampleDataProps[] = Array.from({ length: _DEFAULT_ITEM_COUNT }, (_, i) => ({
|
|
24
|
-
id: i + 1,
|
|
25
|
-
name: `Book Name ${i + 1}`,
|
|
26
|
-
info: `This is the info field ${i + 1}`,
|
|
27
|
-
checked: true,
|
|
28
|
-
}));
|
|
29
|
-
const getSampleData = (pageIndex = 0, searchTexts: string[] = []) => {
|
|
30
|
-
const filterItems = sampleData.filter((item) =>
|
|
31
|
-
searchTexts.every(
|
|
32
|
-
(text) =>
|
|
33
|
-
item.name.toLowerCase().includes(text.toLowerCase()) || item.info.toLowerCase().includes(text.toLowerCase())
|
|
34
|
-
)
|
|
35
|
-
);
|
|
36
|
-
const pageLimit = _DEFAULT_PAGE_LIMIT;
|
|
37
|
-
const itemsCount = filterItems.length;
|
|
38
|
-
let maxPages = Math.floor(itemsCount / pageLimit);
|
|
39
|
-
if (itemsCount % pageLimit !== 0) {
|
|
40
|
-
maxPages++;
|
|
41
|
-
}
|
|
42
|
-
if (pageIndex > maxPages) {
|
|
43
|
-
pageIndex = maxPages - 1;
|
|
44
|
-
}
|
|
45
|
-
const offset = pageIndex * pageLimit;
|
|
46
|
-
return {
|
|
47
|
-
status: 'ok',
|
|
48
|
-
itemsCount,
|
|
49
|
-
pageIndex,
|
|
50
|
-
result: filterItems.slice(offset, offset + pageLimit),
|
|
51
|
-
};
|
|
52
|
-
};
|
|
53
|
-
const removeSampleData = (itemId: number) => {
|
|
54
|
-
const index = sampleData.findIndex((item) => item.id === itemId);
|
|
55
|
-
if (index !== -1) {
|
|
56
|
-
sampleData.splice(index, 1);
|
|
57
|
-
}
|
|
58
|
-
};
|
|
59
|
-
const updateSampleData = (item: SampleDataProps) => {
|
|
60
|
-
const index = sampleData.findIndex((i) => i.id === item.id);
|
|
61
|
-
if (index !== -1) {
|
|
62
|
-
sampleData[index] = { ...item };
|
|
63
|
-
} else {
|
|
64
|
-
// it's a new record
|
|
65
|
-
let newId = sampleData.length + 1;
|
|
66
|
-
while (sampleData.some((item) => item.id === newId)) {
|
|
67
|
-
newId++;
|
|
68
|
-
}
|
|
69
|
-
item.id = newId;
|
|
70
|
-
sampleData.push({ ...item });
|
|
71
|
-
}
|
|
72
|
-
return item;
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
type SampleDataUpdateProps = {
|
|
76
|
-
save?: () => SampleDataProps | null;
|
|
77
|
-
cancel?: () => void;
|
|
78
|
-
};
|
|
79
|
-
// show dialog to edit one item, and call update when save the result
|
|
80
|
-
const showBookEditItem = async (item: SampleDataProps, update: (item: SampleDataProps) => void) => {
|
|
81
|
-
const handleClicked = async (index: number) => {
|
|
82
|
-
if (index === 1) {
|
|
83
|
-
updateFn.cancel?.();
|
|
84
|
-
modalClose();
|
|
85
|
-
}
|
|
86
|
-
if (index === 0) {
|
|
87
|
-
const newItem = updateFn.save?.();
|
|
88
|
-
if (newItem) {
|
|
89
|
-
update(newItem);
|
|
90
|
-
modalClose();
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
};
|
|
94
|
-
const updateFn: SampleDataUpdateProps = {};
|
|
95
|
-
const modalClose = await ModalWindow.show({
|
|
96
|
-
title: 'Edit Sample Data',
|
|
97
|
-
buttons: ['Save', 'Cancel'],
|
|
98
|
-
// contentMaxHeight: '400px',
|
|
99
|
-
handleClicked,
|
|
100
|
-
children: <BookEditItem item={item} update={updateFn}></BookEditItem>,
|
|
101
|
-
closeWhenClickOutside: false,
|
|
102
|
-
});
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
// edit one item
|
|
106
|
-
export const BookEditItem = (props: { item: SampleDataProps; update: SampleDataUpdateProps }) => {
|
|
107
|
-
const ref: RefProps = { id: '' };
|
|
108
|
-
const css: CssProps = {
|
|
109
|
-
padding: '10px',
|
|
110
|
-
border: 'solid 1px gray',
|
|
111
|
-
margin: '1px',
|
|
112
|
-
position: 'relative',
|
|
113
|
-
'.lable': {
|
|
114
|
-
width: '70px',
|
|
115
|
-
},
|
|
116
|
-
};
|
|
117
|
-
props.update.save = () => {
|
|
118
|
-
const name = ref.$('input.name').value;
|
|
119
|
-
const info = ref.$('input.info').value;
|
|
120
|
-
if (name && info) {
|
|
121
|
-
const newItem = updateSampleData({ id: props.item.id, name, info, checked: switchUpdate.getChecked!() });
|
|
122
|
-
props.item.name = newItem.name;
|
|
123
|
-
props.item.info = newItem.info;
|
|
124
|
-
props.item.checked = newItem.checked;
|
|
125
|
-
props.item.id = newItem.id;
|
|
126
|
-
return newItem;
|
|
127
|
-
}
|
|
128
|
-
NotificationMessage.sendMessage('Please input name and info', NotificationColor.Error);
|
|
129
|
-
return null;
|
|
130
|
-
};
|
|
131
|
-
const switchUpdate: ToggleBaseHookProps = {};
|
|
132
|
-
return (
|
|
133
|
-
<div ref={ref} css={css} class='sample-data'>
|
|
134
|
-
<div class='row-box'>
|
|
135
|
-
<div class='lable'>Name: </div>
|
|
136
|
-
<div>
|
|
137
|
-
<input type='text' class='input-base name' value={props.item.name} />
|
|
138
|
-
</div>
|
|
139
|
-
</div>
|
|
140
|
-
<div class='row-box mt-m'>
|
|
141
|
-
<div class='lable'>Info: </div>
|
|
142
|
-
<div>
|
|
143
|
-
<input type='text' class='input-base info' value={props.item.info} />
|
|
144
|
-
</div>
|
|
145
|
-
</div>
|
|
146
|
-
<div class='row-box mt-m'>
|
|
147
|
-
<div class='lable'>Checked: </div>
|
|
148
|
-
<ToggleSwitch size={ToggleSwitchSize.Small} hook={switchUpdate} checked={props.item.checked} />
|
|
149
|
-
</div>
|
|
150
|
-
</div>
|
|
151
|
-
);
|
|
152
|
-
};
|
|
153
|
-
|
|
154
|
-
// show one item
|
|
155
|
-
export const BookShowItem = (props: { item: SampleDataProps }) => {
|
|
156
|
-
const ref: RefProps = { id: '' };
|
|
157
|
-
const css: CssProps = {
|
|
158
|
-
padding: '10px',
|
|
159
|
-
border: 'solid 1px gray',
|
|
160
|
-
margin: '1px',
|
|
161
|
-
position: 'relative',
|
|
162
|
-
'.control-box': {
|
|
163
|
-
display: 'none',
|
|
164
|
-
position: 'absolute',
|
|
165
|
-
right: '10px',
|
|
166
|
-
top: '10px',
|
|
167
|
-
},
|
|
168
|
-
'&:hover .control-box': {
|
|
169
|
-
display: 'block',
|
|
170
|
-
},
|
|
171
|
-
'.lable': {
|
|
172
|
-
width: '70px',
|
|
173
|
-
},
|
|
174
|
-
};
|
|
175
|
-
const onEdit = (ev: any) => {
|
|
176
|
-
const update = (item: SampleDataProps) => {
|
|
177
|
-
dom.value = makeDom(item);
|
|
178
|
-
};
|
|
179
|
-
showBookEditItem(props.item, update);
|
|
180
|
-
};
|
|
181
|
-
const onRemove = (ev: any) => {
|
|
182
|
-
removeSampleData(props.item.id);
|
|
183
|
-
ref.current.remove();
|
|
184
|
-
};
|
|
185
|
-
|
|
186
|
-
const makeDom = (item: SampleDataProps) => {
|
|
187
|
-
const saveText = (text: string) => {
|
|
188
|
-
item.name = text;
|
|
189
|
-
updateSampleData(item);
|
|
190
|
-
dom.value = makeDom(item);
|
|
191
|
-
};
|
|
192
|
-
return (
|
|
193
|
-
<>
|
|
194
|
-
<div class='control-box'>
|
|
195
|
-
<button class='button-base button-ss' onClick={onEdit}>
|
|
196
|
-
Edit
|
|
197
|
-
</button>
|
|
198
|
-
<button class='button-base button-ss' onClick={onRemove}>
|
|
199
|
-
Delete
|
|
200
|
-
</button>
|
|
201
|
-
</div>
|
|
202
|
-
<div class='row-box'>
|
|
203
|
-
<div class='lable'>Name: </div>
|
|
204
|
-
<div>{item.name}</div>
|
|
205
|
-
<div class='px-m'>Double Click to edit: </div>
|
|
206
|
-
<EditableLabel text={item.name} save={saveText} type='text' />
|
|
207
|
-
</div>
|
|
208
|
-
<div class='row-box'>
|
|
209
|
-
<div class='lable'>Info: </div>
|
|
210
|
-
<div>{item.info}</div>
|
|
211
|
-
</div>
|
|
212
|
-
<div class='row-box'>
|
|
213
|
-
<div class='lable'>Checked: </div>
|
|
214
|
-
<div>{item.checked ? 'Yes' : 'No'}</div>
|
|
215
|
-
</div>
|
|
216
|
-
</>
|
|
217
|
-
);
|
|
218
|
-
};
|
|
219
|
-
|
|
220
|
-
const dom = new HtmlVar(makeDom(props.item));
|
|
221
|
-
return (
|
|
222
|
-
<div ref={ref} css={css} class='sample-data'>
|
|
223
|
-
{dom.node}
|
|
224
|
-
</div>
|
|
225
|
-
);
|
|
226
|
-
};
|
|
227
|
-
|
|
228
|
-
// show the list
|
|
229
|
-
export const BookList = () => {
|
|
230
|
-
let currentIndex = 0;
|
|
231
|
-
let searchTexts: string[] = [];
|
|
232
|
-
let items = getSampleData(currentIndex, searchTexts);
|
|
233
|
-
const ref: RefProps = { onLoad: async (self: Element) => {} };
|
|
234
|
-
const onAdd = async () => {
|
|
235
|
-
const update = (item: SampleDataProps) => {
|
|
236
|
-
// new item is added at the list end, so update the last page
|
|
237
|
-
currentIndex = Math.floor((items.itemsCount + 1) / _DEFAULT_PAGE_LIMIT);
|
|
238
|
-
listDom.value = makeList(currentIndex);
|
|
239
|
-
};
|
|
240
|
-
showBookEditItem(
|
|
241
|
-
{
|
|
242
|
-
id: -1,
|
|
243
|
-
name: '',
|
|
244
|
-
info: '',
|
|
245
|
-
checked: false,
|
|
246
|
-
},
|
|
247
|
-
update
|
|
248
|
-
);
|
|
249
|
-
};
|
|
250
|
-
const onSearch = () => {
|
|
251
|
-
searchTexts = ref.$('input.search').value.trim().split(' ');
|
|
252
|
-
items = getSampleData(currentIndex, searchTexts);
|
|
253
|
-
listDom.value = makeList(currentIndex);
|
|
254
|
-
};
|
|
255
|
-
const makeList = (pageIndex: number) => {
|
|
256
|
-
const onLinkClick = (index: number) => {
|
|
257
|
-
currentIndex = index;
|
|
258
|
-
listDom.value = makeList(currentIndex);
|
|
259
|
-
};
|
|
260
|
-
const items = getSampleData(pageIndex, searchTexts);
|
|
261
|
-
return (
|
|
262
|
-
<div>
|
|
263
|
-
<PagingLink
|
|
264
|
-
itemsCount={items.itemsCount}
|
|
265
|
-
pageIndex={pageIndex}
|
|
266
|
-
pageLimit={_DEFAULT_PAGE_LIMIT}
|
|
267
|
-
onClick={onLinkClick}
|
|
268
|
-
baseLink=''
|
|
269
|
-
></PagingLink>
|
|
270
|
-
{items.result.map((item) => (
|
|
271
|
-
<BookShowItem item={item}></BookShowItem>
|
|
272
|
-
))}
|
|
273
|
-
<PagingLink
|
|
274
|
-
itemsCount={items.itemsCount}
|
|
275
|
-
pageIndex={pageIndex}
|
|
276
|
-
pageLimit={_DEFAULT_PAGE_LIMIT}
|
|
277
|
-
onClick={onLinkClick}
|
|
278
|
-
baseLink=''
|
|
279
|
-
></PagingLink>
|
|
280
|
-
</div>
|
|
281
|
-
);
|
|
282
|
-
};
|
|
283
|
-
const listDom = new HtmlVar(makeList(currentIndex));
|
|
284
|
-
const css: CssProps = {
|
|
285
|
-
display: 'flex',
|
|
286
|
-
flexDirection: 'column',
|
|
287
|
-
'.label': {
|
|
288
|
-
width: '70px',
|
|
289
|
-
},
|
|
290
|
-
};
|
|
291
|
-
return (
|
|
292
|
-
<div ref={ref} css={css}>
|
|
293
|
-
<div>
|
|
294
|
-
<div class='row-box'>
|
|
295
|
-
<div class='label'>Search: </div>
|
|
296
|
-
<input type='text' class='input-base search' />
|
|
297
|
-
<button class='button-base mr-s' onClick={onSearch}>
|
|
298
|
-
Search
|
|
299
|
-
</button>
|
|
300
|
-
<button class='button-base' onClick={onAdd}>
|
|
301
|
-
Add
|
|
302
|
-
</button>
|
|
303
|
-
</div>
|
|
304
|
-
</div>
|
|
305
|
-
<div class='list'>{listDom.node}</div>
|
|
306
|
-
</div>
|
|
307
|
-
);
|
|
308
|
-
};
|
|
309
|
-
|
|
310
|
-
export const AdminTestEditPage = () => {
|
|
311
|
-
return (
|
|
312
|
-
<div>
|
|
313
|
-
<div>
|
|
314
|
-
<div>Test editing.</div>
|
|
315
|
-
<BookList />
|
|
316
|
-
</div>
|
|
317
|
-
</div>
|
|
318
|
-
);
|
|
319
|
-
};
|
|
1
|
+
import {
|
|
2
|
+
CssProps,
|
|
3
|
+
RefProps,
|
|
4
|
+
EditableLabel,
|
|
5
|
+
HtmlVar,
|
|
6
|
+
ModalWindow,
|
|
7
|
+
NotificationColor,
|
|
8
|
+
NotificationMessage,
|
|
9
|
+
PagingLink,
|
|
10
|
+
ToggleBaseHookProps,
|
|
11
|
+
ToggleSwitch,
|
|
12
|
+
ToggleSwitchSize,
|
|
13
|
+
} from 'lupine.components';
|
|
14
|
+
|
|
15
|
+
type SampleDataProps = {
|
|
16
|
+
id: number;
|
|
17
|
+
name: string;
|
|
18
|
+
info: string;
|
|
19
|
+
checked: boolean;
|
|
20
|
+
};
|
|
21
|
+
const _DEFAULT_PAGE_LIMIT = 10;
|
|
22
|
+
const _DEFAULT_ITEM_COUNT = 101;
|
|
23
|
+
const sampleData: SampleDataProps[] = Array.from({ length: _DEFAULT_ITEM_COUNT }, (_, i) => ({
|
|
24
|
+
id: i + 1,
|
|
25
|
+
name: `Book Name ${i + 1}`,
|
|
26
|
+
info: `This is the info field ${i + 1}`,
|
|
27
|
+
checked: true,
|
|
28
|
+
}));
|
|
29
|
+
const getSampleData = (pageIndex = 0, searchTexts: string[] = []) => {
|
|
30
|
+
const filterItems = sampleData.filter((item) =>
|
|
31
|
+
searchTexts.every(
|
|
32
|
+
(text) =>
|
|
33
|
+
item.name.toLowerCase().includes(text.toLowerCase()) || item.info.toLowerCase().includes(text.toLowerCase())
|
|
34
|
+
)
|
|
35
|
+
);
|
|
36
|
+
const pageLimit = _DEFAULT_PAGE_LIMIT;
|
|
37
|
+
const itemsCount = filterItems.length;
|
|
38
|
+
let maxPages = Math.floor(itemsCount / pageLimit);
|
|
39
|
+
if (itemsCount % pageLimit !== 0) {
|
|
40
|
+
maxPages++;
|
|
41
|
+
}
|
|
42
|
+
if (pageIndex > maxPages) {
|
|
43
|
+
pageIndex = maxPages - 1;
|
|
44
|
+
}
|
|
45
|
+
const offset = pageIndex * pageLimit;
|
|
46
|
+
return {
|
|
47
|
+
status: 'ok',
|
|
48
|
+
itemsCount,
|
|
49
|
+
pageIndex,
|
|
50
|
+
result: filterItems.slice(offset, offset + pageLimit),
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
const removeSampleData = (itemId: number) => {
|
|
54
|
+
const index = sampleData.findIndex((item) => item.id === itemId);
|
|
55
|
+
if (index !== -1) {
|
|
56
|
+
sampleData.splice(index, 1);
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
const updateSampleData = (item: SampleDataProps) => {
|
|
60
|
+
const index = sampleData.findIndex((i) => i.id === item.id);
|
|
61
|
+
if (index !== -1) {
|
|
62
|
+
sampleData[index] = { ...item };
|
|
63
|
+
} else {
|
|
64
|
+
// it's a new record
|
|
65
|
+
let newId = sampleData.length + 1;
|
|
66
|
+
while (sampleData.some((item) => item.id === newId)) {
|
|
67
|
+
newId++;
|
|
68
|
+
}
|
|
69
|
+
item.id = newId;
|
|
70
|
+
sampleData.push({ ...item });
|
|
71
|
+
}
|
|
72
|
+
return item;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
type SampleDataUpdateProps = {
|
|
76
|
+
save?: () => SampleDataProps | null;
|
|
77
|
+
cancel?: () => void;
|
|
78
|
+
};
|
|
79
|
+
// show dialog to edit one item, and call update when save the result
|
|
80
|
+
const showBookEditItem = async (item: SampleDataProps, update: (item: SampleDataProps) => void) => {
|
|
81
|
+
const handleClicked = async (index: number) => {
|
|
82
|
+
if (index === 1) {
|
|
83
|
+
updateFn.cancel?.();
|
|
84
|
+
modalClose();
|
|
85
|
+
}
|
|
86
|
+
if (index === 0) {
|
|
87
|
+
const newItem = updateFn.save?.();
|
|
88
|
+
if (newItem) {
|
|
89
|
+
update(newItem);
|
|
90
|
+
modalClose();
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
const updateFn: SampleDataUpdateProps = {};
|
|
95
|
+
const modalClose = await ModalWindow.show({
|
|
96
|
+
title: 'Edit Sample Data',
|
|
97
|
+
buttons: ['Save', 'Cancel'],
|
|
98
|
+
// contentMaxHeight: '400px',
|
|
99
|
+
handleClicked,
|
|
100
|
+
children: <BookEditItem item={item} update={updateFn}></BookEditItem>,
|
|
101
|
+
closeWhenClickOutside: false,
|
|
102
|
+
});
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
// edit one item
|
|
106
|
+
export const BookEditItem = (props: { item: SampleDataProps; update: SampleDataUpdateProps }) => {
|
|
107
|
+
const ref: RefProps = { id: '' };
|
|
108
|
+
const css: CssProps = {
|
|
109
|
+
padding: '10px',
|
|
110
|
+
border: 'solid 1px gray',
|
|
111
|
+
margin: '1px',
|
|
112
|
+
position: 'relative',
|
|
113
|
+
'.lable': {
|
|
114
|
+
width: '70px',
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
props.update.save = () => {
|
|
118
|
+
const name = ref.$('input.name').value;
|
|
119
|
+
const info = ref.$('input.info').value;
|
|
120
|
+
if (name && info) {
|
|
121
|
+
const newItem = updateSampleData({ id: props.item.id, name, info, checked: switchUpdate.getChecked!() });
|
|
122
|
+
props.item.name = newItem.name;
|
|
123
|
+
props.item.info = newItem.info;
|
|
124
|
+
props.item.checked = newItem.checked;
|
|
125
|
+
props.item.id = newItem.id;
|
|
126
|
+
return newItem;
|
|
127
|
+
}
|
|
128
|
+
NotificationMessage.sendMessage('Please input name and info', NotificationColor.Error);
|
|
129
|
+
return null;
|
|
130
|
+
};
|
|
131
|
+
const switchUpdate: ToggleBaseHookProps = {};
|
|
132
|
+
return (
|
|
133
|
+
<div ref={ref} css={css} class='sample-data'>
|
|
134
|
+
<div class='row-box'>
|
|
135
|
+
<div class='lable'>Name: </div>
|
|
136
|
+
<div>
|
|
137
|
+
<input type='text' class='input-base name' value={props.item.name} />
|
|
138
|
+
</div>
|
|
139
|
+
</div>
|
|
140
|
+
<div class='row-box mt-m'>
|
|
141
|
+
<div class='lable'>Info: </div>
|
|
142
|
+
<div>
|
|
143
|
+
<input type='text' class='input-base info' value={props.item.info} />
|
|
144
|
+
</div>
|
|
145
|
+
</div>
|
|
146
|
+
<div class='row-box mt-m'>
|
|
147
|
+
<div class='lable'>Checked: </div>
|
|
148
|
+
<ToggleSwitch size={ToggleSwitchSize.Small} hook={switchUpdate} checked={props.item.checked} />
|
|
149
|
+
</div>
|
|
150
|
+
</div>
|
|
151
|
+
);
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
// show one item
|
|
155
|
+
export const BookShowItem = (props: { item: SampleDataProps }) => {
|
|
156
|
+
const ref: RefProps = { id: '' };
|
|
157
|
+
const css: CssProps = {
|
|
158
|
+
padding: '10px',
|
|
159
|
+
border: 'solid 1px gray',
|
|
160
|
+
margin: '1px',
|
|
161
|
+
position: 'relative',
|
|
162
|
+
'.control-box': {
|
|
163
|
+
display: 'none',
|
|
164
|
+
position: 'absolute',
|
|
165
|
+
right: '10px',
|
|
166
|
+
top: '10px',
|
|
167
|
+
},
|
|
168
|
+
'&:hover .control-box': {
|
|
169
|
+
display: 'block',
|
|
170
|
+
},
|
|
171
|
+
'.lable': {
|
|
172
|
+
width: '70px',
|
|
173
|
+
},
|
|
174
|
+
};
|
|
175
|
+
const onEdit = (ev: any) => {
|
|
176
|
+
const update = (item: SampleDataProps) => {
|
|
177
|
+
dom.value = makeDom(item);
|
|
178
|
+
};
|
|
179
|
+
showBookEditItem(props.item, update);
|
|
180
|
+
};
|
|
181
|
+
const onRemove = (ev: any) => {
|
|
182
|
+
removeSampleData(props.item.id);
|
|
183
|
+
ref.current.remove();
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
const makeDom = (item: SampleDataProps) => {
|
|
187
|
+
const saveText = (text: string) => {
|
|
188
|
+
item.name = text;
|
|
189
|
+
updateSampleData(item);
|
|
190
|
+
dom.value = makeDom(item);
|
|
191
|
+
};
|
|
192
|
+
return (
|
|
193
|
+
<>
|
|
194
|
+
<div class='control-box'>
|
|
195
|
+
<button class='button-base button-ss' onClick={onEdit}>
|
|
196
|
+
Edit
|
|
197
|
+
</button>
|
|
198
|
+
<button class='button-base button-ss' onClick={onRemove}>
|
|
199
|
+
Delete
|
|
200
|
+
</button>
|
|
201
|
+
</div>
|
|
202
|
+
<div class='row-box'>
|
|
203
|
+
<div class='lable'>Name: </div>
|
|
204
|
+
<div>{item.name}</div>
|
|
205
|
+
<div class='px-m'>Double Click to edit: </div>
|
|
206
|
+
<EditableLabel text={item.name} save={saveText} type='text' />
|
|
207
|
+
</div>
|
|
208
|
+
<div class='row-box'>
|
|
209
|
+
<div class='lable'>Info: </div>
|
|
210
|
+
<div>{item.info}</div>
|
|
211
|
+
</div>
|
|
212
|
+
<div class='row-box'>
|
|
213
|
+
<div class='lable'>Checked: </div>
|
|
214
|
+
<div>{item.checked ? 'Yes' : 'No'}</div>
|
|
215
|
+
</div>
|
|
216
|
+
</>
|
|
217
|
+
);
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
const dom = new HtmlVar(makeDom(props.item));
|
|
221
|
+
return (
|
|
222
|
+
<div ref={ref} css={css} class='sample-data'>
|
|
223
|
+
{dom.node}
|
|
224
|
+
</div>
|
|
225
|
+
);
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
// show the list
|
|
229
|
+
export const BookList = () => {
|
|
230
|
+
let currentIndex = 0;
|
|
231
|
+
let searchTexts: string[] = [];
|
|
232
|
+
let items = getSampleData(currentIndex, searchTexts);
|
|
233
|
+
const ref: RefProps = { onLoad: async (self: Element) => {} };
|
|
234
|
+
const onAdd = async () => {
|
|
235
|
+
const update = (item: SampleDataProps) => {
|
|
236
|
+
// new item is added at the list end, so update the last page
|
|
237
|
+
currentIndex = Math.floor((items.itemsCount + 1) / _DEFAULT_PAGE_LIMIT);
|
|
238
|
+
listDom.value = makeList(currentIndex);
|
|
239
|
+
};
|
|
240
|
+
showBookEditItem(
|
|
241
|
+
{
|
|
242
|
+
id: -1,
|
|
243
|
+
name: '',
|
|
244
|
+
info: '',
|
|
245
|
+
checked: false,
|
|
246
|
+
},
|
|
247
|
+
update
|
|
248
|
+
);
|
|
249
|
+
};
|
|
250
|
+
const onSearch = () => {
|
|
251
|
+
searchTexts = ref.$('input.search').value.trim().split(' ');
|
|
252
|
+
items = getSampleData(currentIndex, searchTexts);
|
|
253
|
+
listDom.value = makeList(currentIndex);
|
|
254
|
+
};
|
|
255
|
+
const makeList = (pageIndex: number) => {
|
|
256
|
+
const onLinkClick = (index: number) => {
|
|
257
|
+
currentIndex = index;
|
|
258
|
+
listDom.value = makeList(currentIndex);
|
|
259
|
+
};
|
|
260
|
+
const items = getSampleData(pageIndex, searchTexts);
|
|
261
|
+
return (
|
|
262
|
+
<div>
|
|
263
|
+
<PagingLink
|
|
264
|
+
itemsCount={items.itemsCount}
|
|
265
|
+
pageIndex={pageIndex}
|
|
266
|
+
pageLimit={_DEFAULT_PAGE_LIMIT}
|
|
267
|
+
onClick={onLinkClick}
|
|
268
|
+
baseLink=''
|
|
269
|
+
></PagingLink>
|
|
270
|
+
{items.result.map((item) => (
|
|
271
|
+
<BookShowItem item={item}></BookShowItem>
|
|
272
|
+
))}
|
|
273
|
+
<PagingLink
|
|
274
|
+
itemsCount={items.itemsCount}
|
|
275
|
+
pageIndex={pageIndex}
|
|
276
|
+
pageLimit={_DEFAULT_PAGE_LIMIT}
|
|
277
|
+
onClick={onLinkClick}
|
|
278
|
+
baseLink=''
|
|
279
|
+
></PagingLink>
|
|
280
|
+
</div>
|
|
281
|
+
);
|
|
282
|
+
};
|
|
283
|
+
const listDom = new HtmlVar(makeList(currentIndex));
|
|
284
|
+
const css: CssProps = {
|
|
285
|
+
display: 'flex',
|
|
286
|
+
flexDirection: 'column',
|
|
287
|
+
'.label': {
|
|
288
|
+
width: '70px',
|
|
289
|
+
},
|
|
290
|
+
};
|
|
291
|
+
return (
|
|
292
|
+
<div ref={ref} css={css}>
|
|
293
|
+
<div>
|
|
294
|
+
<div class='row-box'>
|
|
295
|
+
<div class='label'>Search: </div>
|
|
296
|
+
<input type='text' class='input-base search' />
|
|
297
|
+
<button class='button-base mr-s' onClick={onSearch}>
|
|
298
|
+
Search
|
|
299
|
+
</button>
|
|
300
|
+
<button class='button-base' onClick={onAdd}>
|
|
301
|
+
Add
|
|
302
|
+
</button>
|
|
303
|
+
</div>
|
|
304
|
+
</div>
|
|
305
|
+
<div class='list'>{listDom.node}</div>
|
|
306
|
+
</div>
|
|
307
|
+
);
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
export const AdminTestEditPage = () => {
|
|
311
|
+
return (
|
|
312
|
+
<div>
|
|
313
|
+
<div>
|
|
314
|
+
<div>Test editing.</div>
|
|
315
|
+
<BookList />
|
|
316
|
+
</div>
|
|
317
|
+
</div>
|
|
318
|
+
);
|
|
319
|
+
};
|