@jskit-ai/http-web 0.1.22 → 0.1.24
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/package.json +6 -6
- package/patterns/crud-screen-set/PATTERN.md +1 -0
- package/patterns/crud-screen-set/example/books/BookEditPage.vue +0 -2
- package/patterns/crud-screen-set/example/books/BookListPage.vue +0 -4
- package/patterns/crud-screen-set/example/books/BookNewPage.vue +0 -2
- package/patterns/crud-screen-set/example/books/BookViewPage.vue +0 -1
- package/src/client/components/CrudAddEditScreen.vue +12 -45
- package/src/client/components/CrudListScreen.vue +22 -79
- package/src/client/components/CrudViewScreen.vue +22 -67
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jskit-ai/http-web",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.24",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"test": "node --test"
|
|
@@ -44,14 +44,14 @@
|
|
|
44
44
|
"./client/support/contractGuards": "./src/client/support/contractGuards.js"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@jskit-ai/resource-crud-core": "0.1.
|
|
47
|
+
"@jskit-ai/resource-crud-core": "0.1.121",
|
|
48
48
|
"@mdi/js": "^7.4.47"
|
|
49
49
|
},
|
|
50
50
|
"peerDependencies": {
|
|
51
|
-
"@jskit-ai/http-runtime": "0.1.
|
|
52
|
-
"@jskit-ai/kernel": "0.1.
|
|
53
|
-
"@jskit-ai/realtime": "0.1.
|
|
54
|
-
"@jskit-ai/shell-web": "0.1.
|
|
51
|
+
"@jskit-ai/http-runtime": "0.1.177",
|
|
52
|
+
"@jskit-ai/kernel": "0.1.179",
|
|
53
|
+
"@jskit-ai/realtime": "0.1.176",
|
|
54
|
+
"@jskit-ai/shell-web": "0.1.183",
|
|
55
55
|
"@tanstack/vue-query": "^5.90.5",
|
|
56
56
|
"vue": "^3.5.13",
|
|
57
57
|
"vue-router": "^5.0.4",
|
|
@@ -35,6 +35,7 @@ questionnaire.
|
|
|
35
35
|
|
|
36
36
|
- Route pages stay thin and use `useCrudListScreen()`, `useCrudViewScreen()`,
|
|
37
37
|
or `useCrudAddEditScreen()`.
|
|
38
|
+
- Route pages do not add a page header or standalone heading by default.
|
|
38
39
|
- Transport and validators come from the shared resource contract.
|
|
39
40
|
- Initial visible loading uses structure-matching skeletons; pending buttons use
|
|
40
41
|
stable disabled labels, never spinners.
|
|
@@ -12,8 +12,6 @@ const routeRecordId = computed(() => String(route.params.bookId || "").trim());
|
|
|
12
12
|
|
|
13
13
|
const screen = useCrudAddEditScreen({
|
|
14
14
|
mode: "edit",
|
|
15
|
-
title: "Edit book",
|
|
16
|
-
subtitle: "Update this catalogue entry.",
|
|
17
15
|
saveLabel: "Save changes",
|
|
18
16
|
cancelTo: "/books/:bookId",
|
|
19
17
|
preserveCancelQuery: true,
|
|
@@ -25,11 +25,7 @@ const screen = useCrudListScreen({
|
|
|
25
25
|
<template>
|
|
26
26
|
<CrudListScreen
|
|
27
27
|
:screen="screen"
|
|
28
|
-
title-label="Books"
|
|
29
|
-
heading-title="My books"
|
|
30
|
-
subtitle="Search and maintain your catalogue."
|
|
31
28
|
create-label="Add book"
|
|
32
|
-
empty-title="No books yet"
|
|
33
29
|
empty-body="Add your first book to begin the catalogue."
|
|
34
30
|
>
|
|
35
31
|
<template #card-fields="{ record, formatListCardValue }">
|
|
@@ -30,7 +30,6 @@ const deleteAction = useCrudDeleteAction({
|
|
|
30
30
|
:screen="screen"
|
|
31
31
|
resource-singular-title="Book"
|
|
32
32
|
resource-plural-title="Books"
|
|
33
|
-
description="Review this catalogue entry."
|
|
34
33
|
>
|
|
35
34
|
<template #actions>
|
|
36
35
|
<CrudDeleteAction :action="deleteAction" resource-singular-title="Book" />
|
|
@@ -28,8 +28,6 @@ const formRuntime = computed(() => props.screen?.formRuntime || {});
|
|
|
28
28
|
const addEdit = computed(() => props.screen?.addEdit || formRuntime.value?.addEdit || {});
|
|
29
29
|
const formState = computed(() => props.screen?.formState || formRuntime.value?.form || {});
|
|
30
30
|
const mode = computed(() => String(unref(props.screen?.mode) || "new").trim() || "new");
|
|
31
|
-
const title = computed(() => String(unref(props.screen?.title) || "").trim());
|
|
32
|
-
const subtitle = computed(() => String(unref(props.screen?.subtitle) || "").trim());
|
|
33
31
|
const saveLabel = computed(() => String(unref(props.screen?.saveLabel) || "Save").trim() || "Save");
|
|
34
32
|
const cancelTo = computed(() => unref(props.screen?.cancelTo) || "");
|
|
35
33
|
|
|
@@ -53,27 +51,20 @@ function resolveCancelTo(target = cancelTo.value) {
|
|
|
53
51
|
|
|
54
52
|
<template>
|
|
55
53
|
<section class="crud-screen crud-screen--operator crud-add-edit-form d-flex flex-column ga-4">
|
|
56
|
-
<
|
|
57
|
-
<
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
@click="addEdit.submit"
|
|
68
|
-
>
|
|
69
|
-
{{ addEdit.isSaving ? "Saving…" : saveLabel }}
|
|
70
|
-
</v-btn>
|
|
71
|
-
</div>
|
|
72
|
-
</header>
|
|
54
|
+
<div class="crud-add-edit-form__actions">
|
|
55
|
+
<v-btn v-if="cancelTo" color="primary" variant="outlined" :to="resolveCancelTo(cancelTo)">Cancel</v-btn>
|
|
56
|
+
<v-btn
|
|
57
|
+
color="primary"
|
|
58
|
+
variant="flat"
|
|
59
|
+
:disabled="addEdit.isSubmitDisabled"
|
|
60
|
+
@click="addEdit.submit"
|
|
61
|
+
>
|
|
62
|
+
{{ addEdit.isSaving ? "Saving…" : saveLabel }}
|
|
63
|
+
</v-btn>
|
|
64
|
+
</div>
|
|
73
65
|
|
|
74
66
|
<v-sheet rounded="lg" border class="crud-add-edit-form__panel">
|
|
75
67
|
<div v-if="addEdit.loadError" class="crud-add-edit-form__state">
|
|
76
|
-
<h2 class="text-h6 mb-2">Unable to load form</h2>
|
|
77
68
|
<p class="text-body-2 text-medium-emphasis mb-4">
|
|
78
69
|
{{ addEdit.loadError }}
|
|
79
70
|
</p>
|
|
@@ -89,7 +80,7 @@ function resolveCancelTo(target = cancelTo.value) {
|
|
|
89
80
|
</div>
|
|
90
81
|
<template v-else-if="formRuntime.showFormSkeleton">
|
|
91
82
|
<div class="pa-4">
|
|
92
|
-
<v-skeleton-loader type="
|
|
83
|
+
<v-skeleton-loader type="text@2, article" />
|
|
93
84
|
</div>
|
|
94
85
|
</template>
|
|
95
86
|
<v-form v-else class="pa-4" @submit.prevent="addEdit.submit" novalidate>
|
|
@@ -114,7 +105,6 @@ function resolveCancelTo(target = cancelTo.value) {
|
|
|
114
105
|
|
|
115
106
|
<style scoped>
|
|
116
107
|
.crud-screen {
|
|
117
|
-
--crud-screen-title-size: clamp(1.35rem, 2vw, 1.85rem);
|
|
118
108
|
--crud-screen-state-padding: 2.5rem 1.25rem;
|
|
119
109
|
}
|
|
120
110
|
|
|
@@ -122,25 +112,6 @@ function resolveCancelTo(target = cancelTo.value) {
|
|
|
122
112
|
--crud-screen-state-padding: 2rem 1rem;
|
|
123
113
|
}
|
|
124
114
|
|
|
125
|
-
.crud-add-edit-form__header {
|
|
126
|
-
align-items: flex-start;
|
|
127
|
-
display: flex;
|
|
128
|
-
gap: 1rem;
|
|
129
|
-
justify-content: space-between;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
.crud-add-edit-form__copy {
|
|
133
|
-
min-width: 0;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
.crud-add-edit-form__title {
|
|
137
|
-
font-size: var(--crud-screen-title-size);
|
|
138
|
-
font-weight: 650;
|
|
139
|
-
letter-spacing: -0.02em;
|
|
140
|
-
line-height: 1.15;
|
|
141
|
-
margin: 0 0 0.35rem;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
115
|
.crud-add-edit-form__actions {
|
|
145
116
|
display: flex;
|
|
146
117
|
flex-wrap: wrap;
|
|
@@ -164,10 +135,6 @@ function resolveCancelTo(target = cancelTo.value) {
|
|
|
164
135
|
}
|
|
165
136
|
|
|
166
137
|
@media (max-width: 960px) {
|
|
167
|
-
.crud-add-edit-form__header {
|
|
168
|
-
flex-direction: column;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
138
|
.crud-add-edit-form__actions {
|
|
172
139
|
width: 100%;
|
|
173
140
|
}
|
|
@@ -10,34 +10,14 @@ const props = defineProps({
|
|
|
10
10
|
type: Object,
|
|
11
11
|
required: true
|
|
12
12
|
},
|
|
13
|
-
titleLabel: {
|
|
14
|
-
type: String,
|
|
15
|
-
default: "Records"
|
|
16
|
-
},
|
|
17
|
-
headingTitle: {
|
|
18
|
-
type: String,
|
|
19
|
-
default: ""
|
|
20
|
-
},
|
|
21
|
-
subtitle: {
|
|
22
|
-
type: String,
|
|
23
|
-
default: ""
|
|
24
|
-
},
|
|
25
13
|
createLabel: {
|
|
26
14
|
type: String,
|
|
27
15
|
default: "New record"
|
|
28
16
|
},
|
|
29
|
-
loadErrorTitle: {
|
|
30
|
-
type: String,
|
|
31
|
-
default: "Unable to load records"
|
|
32
|
-
},
|
|
33
17
|
loadErrorBody: {
|
|
34
18
|
type: String,
|
|
35
19
|
default: "Check the connection and try again."
|
|
36
20
|
},
|
|
37
|
-
emptyTitle: {
|
|
38
|
-
type: String,
|
|
39
|
-
default: "No records yet"
|
|
40
|
-
},
|
|
41
21
|
emptyBody: {
|
|
42
22
|
type: String,
|
|
43
23
|
default: "Create the first record to start using this workflow."
|
|
@@ -57,10 +37,6 @@ const hasBulkActions = computed(() => Boolean(unref(bulkActions.value?.hasAction
|
|
|
57
37
|
const hasRowActions = computed(() => Boolean(unref(rowActions.value?.hasActions)));
|
|
58
38
|
const hasViewUrl = computed(() => Boolean(props.screen?.hasViewUrl));
|
|
59
39
|
const hasEditUrl = computed(() => Boolean(props.screen?.hasEditUrl));
|
|
60
|
-
const resolvedHeadingTitle = computed(() => String(props.headingTitle || props.titleLabel || "").trim());
|
|
61
|
-
const resolvedSubtitle = computed(() =>
|
|
62
|
-
String(props.subtitle || `Search, review, and update ${props.titleLabel} from this screen.`).trim()
|
|
63
|
-
);
|
|
64
40
|
|
|
65
41
|
function resolveListRecordTitle(record) {
|
|
66
42
|
if (typeof props.screen?.resolveRecordTitle === "function") {
|
|
@@ -127,32 +103,25 @@ function setSelectableRowsSelected(selected = true) {
|
|
|
127
103
|
|
|
128
104
|
<template>
|
|
129
105
|
<section class="crud-screen crud-screen--operator crud-list-element d-flex flex-column ga-4">
|
|
130
|
-
<
|
|
131
|
-
<
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
variant="flat"
|
|
150
|
-
:to="listPrimaryAction"
|
|
151
|
-
>
|
|
152
|
-
{{ createLabel }}
|
|
153
|
-
</v-btn>
|
|
154
|
-
</div>
|
|
155
|
-
</header>
|
|
106
|
+
<div class="crud-list-actions">
|
|
107
|
+
<v-btn
|
|
108
|
+
color="primary"
|
|
109
|
+
variant="tonal"
|
|
110
|
+
:disabled="records.isFetching"
|
|
111
|
+
@click="records.reload"
|
|
112
|
+
>
|
|
113
|
+
{{ records.isFetching ? "Refreshing…" : "Refresh" }}
|
|
114
|
+
</v-btn>
|
|
115
|
+
<v-btn
|
|
116
|
+
v-if="listPrimaryAction"
|
|
117
|
+
class="crud-list-primary-action"
|
|
118
|
+
color="primary"
|
|
119
|
+
variant="flat"
|
|
120
|
+
:to="listPrimaryAction"
|
|
121
|
+
>
|
|
122
|
+
{{ createLabel }}
|
|
123
|
+
</v-btn>
|
|
124
|
+
</div>
|
|
156
125
|
|
|
157
126
|
<v-sheet rounded="lg" border class="crud-list-panel">
|
|
158
127
|
<div class="crud-list-toolbar">
|
|
@@ -182,7 +151,6 @@ function setSelectableRowsSelected(selected = true) {
|
|
|
182
151
|
</template>
|
|
183
152
|
<template v-else>
|
|
184
153
|
<div v-if="records.loadError" class="crud-list-state">
|
|
185
|
-
<h2 class="text-h6 mb-2">{{ loadErrorTitle }}</h2>
|
|
186
154
|
<p class="text-body-2 text-medium-emphasis mb-4">{{ loadErrorBody }}</p>
|
|
187
155
|
<v-btn
|
|
188
156
|
color="primary"
|
|
@@ -195,7 +163,6 @@ function setSelectableRowsSelected(selected = true) {
|
|
|
195
163
|
</div>
|
|
196
164
|
|
|
197
165
|
<div v-else-if="displayRows.length < 1" class="crud-list-state">
|
|
198
|
-
<h2 class="text-h6 mb-2">{{ emptyTitle }}</h2>
|
|
199
166
|
<p class="text-body-2 text-medium-emphasis mb-4">{{ emptyBody }}</p>
|
|
200
167
|
<v-btn v-if="listPrimaryAction" color="primary" variant="flat" :to="listPrimaryAction">
|
|
201
168
|
{{ createLabel }}
|
|
@@ -354,7 +321,6 @@ function setSelectableRowsSelected(selected = true) {
|
|
|
354
321
|
|
|
355
322
|
<style scoped>
|
|
356
323
|
.crud-screen {
|
|
357
|
-
--crud-screen-title-size: clamp(1.35rem, 2vw, 1.85rem);
|
|
358
324
|
--crud-screen-state-padding: 2.5rem 1.25rem;
|
|
359
325
|
}
|
|
360
326
|
|
|
@@ -362,26 +328,7 @@ function setSelectableRowsSelected(selected = true) {
|
|
|
362
328
|
--crud-screen-state-padding: 2rem 1rem;
|
|
363
329
|
}
|
|
364
330
|
|
|
365
|
-
.crud-list-
|
|
366
|
-
align-items: flex-start;
|
|
367
|
-
display: flex;
|
|
368
|
-
gap: 1rem;
|
|
369
|
-
justify-content: space-between;
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
.crud-list-header__copy {
|
|
373
|
-
min-width: 0;
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
.crud-list-header__title {
|
|
377
|
-
font-size: var(--crud-screen-title-size);
|
|
378
|
-
font-weight: 650;
|
|
379
|
-
letter-spacing: -0.02em;
|
|
380
|
-
line-height: 1.15;
|
|
381
|
-
margin: 0 0 0.35rem;
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
.crud-list-header__actions {
|
|
331
|
+
.crud-list-actions {
|
|
385
332
|
display: flex;
|
|
386
333
|
flex-wrap: wrap;
|
|
387
334
|
gap: 0.5rem;
|
|
@@ -495,11 +442,7 @@ function setSelectableRowsSelected(selected = true) {
|
|
|
495
442
|
}
|
|
496
443
|
|
|
497
444
|
@media (max-width: 960px) {
|
|
498
|
-
.crud-list-
|
|
499
|
-
flex-direction: column;
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
.crud-list-header__actions {
|
|
445
|
+
.crud-list-actions {
|
|
503
446
|
width: 100%;
|
|
504
447
|
}
|
|
505
448
|
|
|
@@ -507,7 +450,7 @@ function setSelectableRowsSelected(selected = true) {
|
|
|
507
450
|
min-height: 48px;
|
|
508
451
|
}
|
|
509
452
|
|
|
510
|
-
.crud-list-
|
|
453
|
+
.crud-list-primary-action {
|
|
511
454
|
display: none;
|
|
512
455
|
}
|
|
513
456
|
|
|
@@ -13,58 +13,38 @@ const props = defineProps({
|
|
|
13
13
|
resourcePluralTitle: {
|
|
14
14
|
type: String,
|
|
15
15
|
default: "Records"
|
|
16
|
-
},
|
|
17
|
-
description: {
|
|
18
|
-
type: String,
|
|
19
|
-
default: ""
|
|
20
|
-
},
|
|
21
|
-
unavailableTitle: {
|
|
22
|
-
type: String,
|
|
23
|
-
default: "Record unavailable"
|
|
24
16
|
}
|
|
25
17
|
});
|
|
26
18
|
|
|
27
19
|
const view = computed(() => props.screen?.view || {});
|
|
28
|
-
const recordTitle = computed(() => unref(props.screen?.recordTitle) || props.resourceSingularTitle);
|
|
29
20
|
const listLocation = computed(() => unref(props.screen?.listLocation) || null);
|
|
30
21
|
const editLocation = computed(() => unref(props.screen?.editLocation) || null);
|
|
31
|
-
const resolvedDescription = computed(() =>
|
|
32
|
-
String(props.description || `Review this ${props.resourceSingularTitle} record.`).trim()
|
|
33
|
-
);
|
|
34
22
|
</script>
|
|
35
23
|
|
|
36
24
|
<template>
|
|
37
25
|
<section class="crud-screen crud-screen--operator crud-view-element d-flex flex-column ga-4">
|
|
38
|
-
<
|
|
39
|
-
<
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
variant="flat"
|
|
58
|
-
:to="editLocation"
|
|
59
|
-
>
|
|
60
|
-
Edit
|
|
61
|
-
</v-btn>
|
|
62
|
-
</div>
|
|
63
|
-
</header>
|
|
26
|
+
<div class="crud-view-actions">
|
|
27
|
+
<v-btn
|
|
28
|
+
v-if="listLocation"
|
|
29
|
+
color="primary"
|
|
30
|
+
variant="outlined"
|
|
31
|
+
:to="listLocation"
|
|
32
|
+
>
|
|
33
|
+
Back to {{ resourcePluralTitle }}
|
|
34
|
+
</v-btn>
|
|
35
|
+
<slot name="actions" :screen="screen" :view="view" />
|
|
36
|
+
<v-btn
|
|
37
|
+
v-if="editLocation"
|
|
38
|
+
color="primary"
|
|
39
|
+
variant="flat"
|
|
40
|
+
:to="editLocation"
|
|
41
|
+
>
|
|
42
|
+
Edit
|
|
43
|
+
</v-btn>
|
|
44
|
+
</div>
|
|
64
45
|
|
|
65
46
|
<v-sheet rounded="lg" border class="crud-view-panel">
|
|
66
47
|
<div v-if="view.loadError || view.isNotFound" class="crud-view-state">
|
|
67
|
-
<h2 class="text-h6 mb-2">{{ unavailableTitle }}</h2>
|
|
68
48
|
<p class="text-body-2 text-medium-emphasis mb-4">
|
|
69
49
|
{{ view.loadError || `This ${resourceSingularTitle} could not be found.` }}
|
|
70
50
|
</p>
|
|
@@ -114,7 +94,6 @@ const resolvedDescription = computed(() =>
|
|
|
114
94
|
|
|
115
95
|
<style scoped>
|
|
116
96
|
.crud-screen {
|
|
117
|
-
--crud-screen-title-size: clamp(1.35rem, 2vw, 1.85rem);
|
|
118
97
|
--crud-screen-state-padding: 2.5rem 1.25rem;
|
|
119
98
|
}
|
|
120
99
|
|
|
@@ -122,27 +101,7 @@ const resolvedDescription = computed(() =>
|
|
|
122
101
|
--crud-screen-state-padding: 2rem 1rem;
|
|
123
102
|
}
|
|
124
103
|
|
|
125
|
-
.crud-view-
|
|
126
|
-
align-items: flex-start;
|
|
127
|
-
display: flex;
|
|
128
|
-
gap: 1rem;
|
|
129
|
-
justify-content: space-between;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
.crud-view-header__copy {
|
|
133
|
-
min-width: 0;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
.crud-view-header__title {
|
|
137
|
-
font-size: var(--crud-screen-title-size);
|
|
138
|
-
font-weight: 650;
|
|
139
|
-
letter-spacing: -0.02em;
|
|
140
|
-
line-height: 1.15;
|
|
141
|
-
margin: 0 0 0.35rem;
|
|
142
|
-
overflow-wrap: anywhere;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
.crud-view-header__actions {
|
|
104
|
+
.crud-view-actions {
|
|
146
105
|
display: flex;
|
|
147
106
|
flex-wrap: wrap;
|
|
148
107
|
gap: 0.5rem;
|
|
@@ -176,15 +135,11 @@ const resolvedDescription = computed(() =>
|
|
|
176
135
|
}
|
|
177
136
|
|
|
178
137
|
@media (max-width: 960px) {
|
|
179
|
-
.crud-view-
|
|
180
|
-
flex-direction: column;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
.crud-view-header__actions {
|
|
138
|
+
.crud-view-actions {
|
|
184
139
|
width: 100%;
|
|
185
140
|
}
|
|
186
141
|
|
|
187
|
-
.crud-view-
|
|
142
|
+
.crud-view-actions :deep(.v-btn) {
|
|
188
143
|
min-height: 48px;
|
|
189
144
|
flex: 1 1 10rem;
|
|
190
145
|
}
|