itube-specs 0.0.892 → 0.0.893
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/assets/scss/themes/dark/_theme.scss +14 -1
- package/assets/scss/themes/light/_theme.scss +14 -1
- package/components/card/dropdown/trigger.vue +9 -5
- package/components/card/history.vue +4 -3
- package/components/card/video/index.vue +21 -8
- package/components/library/model.vue +307 -0
- package/components/ui/video-preview.vue +62 -0
- package/package.json +1 -1
- package/pages/library/index.vue +366 -0
- package/pages/library/models.vue +168 -0
- package/pages/library.vue +1 -361
- package/stubs/history.ts +17 -14
- package/stubs/library-models.ts +75 -0
- package/stubs/videos.ts +29 -0
- package/components/card/preview.vue +0 -139
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
<!-- Библиотека: «продолжить просмотр», коллекции и подписки. Данные пока заглушка -->
|
|
2
|
+
<template>
|
|
3
|
+
<div class="library _page _container">
|
|
4
|
+
<UiBreadcrumbs
|
|
5
|
+
:items="breadcrumbs"
|
|
6
|
+
class="_breadcrumbs"
|
|
7
|
+
/>
|
|
8
|
+
|
|
9
|
+
<div class="library__header">
|
|
10
|
+
<h1 class="library__title _title">{{ t('navigation.library') }}</h1>
|
|
11
|
+
|
|
12
|
+
<UiToggle
|
|
13
|
+
v-model="saveHistory"
|
|
14
|
+
class="library__save"
|
|
15
|
+
left
|
|
16
|
+
>{{ t('history_text') }}</UiToggle>
|
|
17
|
+
</div>
|
|
18
|
+
|
|
19
|
+
<section
|
|
20
|
+
v-if="continueWatching.length > 0"
|
|
21
|
+
class="library__section"
|
|
22
|
+
>
|
|
23
|
+
<header class="library__section-header">
|
|
24
|
+
<h2 class="library__heading">
|
|
25
|
+
<span class="library__heading-icon">
|
|
26
|
+
<UiIcon
|
|
27
|
+
name="history"
|
|
28
|
+
size="16"
|
|
29
|
+
/>
|
|
30
|
+
</span>
|
|
31
|
+
{{ t('library.continue_watching') }}
|
|
32
|
+
</h2>
|
|
33
|
+
|
|
34
|
+
<UiLink
|
|
35
|
+
:to="generateLink('/history')"
|
|
36
|
+
theme="secondary"
|
|
37
|
+
>
|
|
38
|
+
{{ t('library.view_all_history') }}
|
|
39
|
+
<UiIcon
|
|
40
|
+
name="arrow-right"
|
|
41
|
+
size="16"
|
|
42
|
+
/>
|
|
43
|
+
</UiLink>
|
|
44
|
+
</header>
|
|
45
|
+
|
|
46
|
+
<div class="library__continue">
|
|
47
|
+
<CardHistory
|
|
48
|
+
v-for="item in continueWatching"
|
|
49
|
+
:key="`continue-${item.guid}`"
|
|
50
|
+
:card="item"
|
|
51
|
+
loading="eager"
|
|
52
|
+
priority
|
|
53
|
+
remaining
|
|
54
|
+
@share="onShare"
|
|
55
|
+
@remove="onRemove"
|
|
56
|
+
/>
|
|
57
|
+
</div>
|
|
58
|
+
</section>
|
|
59
|
+
|
|
60
|
+
<section class="library__section">
|
|
61
|
+
<header class="library__section-header">
|
|
62
|
+
<h2 class="library__heading">
|
|
63
|
+
<span class="library__heading-icon">
|
|
64
|
+
<UiIcon
|
|
65
|
+
name="layers"
|
|
66
|
+
size="16"
|
|
67
|
+
/>
|
|
68
|
+
</span>
|
|
69
|
+
{{ t('library.collections') }}
|
|
70
|
+
</h2>
|
|
71
|
+
</header>
|
|
72
|
+
|
|
73
|
+
<ul class="library__grid">
|
|
74
|
+
<li
|
|
75
|
+
v-for="item in collections"
|
|
76
|
+
:key="item.title"
|
|
77
|
+
>
|
|
78
|
+
<LibraryItem
|
|
79
|
+
:to="item.to"
|
|
80
|
+
:icon="item.icon"
|
|
81
|
+
:title="item.title"
|
|
82
|
+
:subtitle="item.subtitle"
|
|
83
|
+
/>
|
|
84
|
+
</li>
|
|
85
|
+
</ul>
|
|
86
|
+
</section>
|
|
87
|
+
|
|
88
|
+
<section class="library__section">
|
|
89
|
+
<header class="library__section-header">
|
|
90
|
+
<h2 class="library__heading">
|
|
91
|
+
<span class="library__heading-icon">
|
|
92
|
+
<UiIcon
|
|
93
|
+
name="plus"
|
|
94
|
+
size="16"
|
|
95
|
+
/>
|
|
96
|
+
</span>
|
|
97
|
+
{{ t('library.following') }}
|
|
98
|
+
</h2>
|
|
99
|
+
|
|
100
|
+
<UiBtn theme="secondary">
|
|
101
|
+
{{ t('library.whats_new', { count: newTotal }) }}
|
|
102
|
+
<UiIcon
|
|
103
|
+
name="arrow-right"
|
|
104
|
+
size="16"
|
|
105
|
+
/>
|
|
106
|
+
</UiBtn>
|
|
107
|
+
</header>
|
|
108
|
+
|
|
109
|
+
<ul class="library__grid">
|
|
110
|
+
<li
|
|
111
|
+
v-for="item in following"
|
|
112
|
+
:key="item.title"
|
|
113
|
+
>
|
|
114
|
+
<LibraryItem
|
|
115
|
+
:to="item.to"
|
|
116
|
+
:icon="item.icon"
|
|
117
|
+
:title="item.title"
|
|
118
|
+
:subtitle="item.subtitle"
|
|
119
|
+
:badge="item.badge"
|
|
120
|
+
:badge-note="item.badgeNote"
|
|
121
|
+
:badge-theme="item.badgeTheme"
|
|
122
|
+
/>
|
|
123
|
+
</li>
|
|
124
|
+
</ul>
|
|
125
|
+
</section>
|
|
126
|
+
|
|
127
|
+
<ShareMain
|
|
128
|
+
v-model="isShareOpen"
|
|
129
|
+
:url="shareUrl"
|
|
130
|
+
>
|
|
131
|
+
<template #card>
|
|
132
|
+
<CardVideoMini
|
|
133
|
+
v-if="shareCard"
|
|
134
|
+
:card="shareCard"
|
|
135
|
+
/>
|
|
136
|
+
</template>
|
|
137
|
+
</ShareMain>
|
|
138
|
+
</div>
|
|
139
|
+
</template>
|
|
140
|
+
|
|
141
|
+
<script setup lang="ts">
|
|
142
|
+
import { getHistoryStub } from '../../stubs/history';
|
|
143
|
+
import type { HistoryStubCard } from '../../stubs/history';
|
|
144
|
+
import { libraryStub } from '../../stubs/library';
|
|
145
|
+
import type { IBreadcrumbItem, IVideoCard } from '../../types';
|
|
146
|
+
|
|
147
|
+
/** Карточка-ссылка библиотеки в разметке страницы. */
|
|
148
|
+
type LibraryLink = {
|
|
149
|
+
to: string
|
|
150
|
+
icon: string
|
|
151
|
+
title: string
|
|
152
|
+
subtitle?: string
|
|
153
|
+
badge?: string
|
|
154
|
+
badgeNote?: string
|
|
155
|
+
badgeTheme?: 'accent' | 'bordered'
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
const CONTINUE_LIMIT = 4;
|
|
159
|
+
|
|
160
|
+
const { t } = useI18n();
|
|
161
|
+
|
|
162
|
+
const { generateLink } = useGenerateLink();
|
|
163
|
+
|
|
164
|
+
const saveHistory = ref(true);
|
|
165
|
+
|
|
166
|
+
const removedGuids = ref<string[]>([]);
|
|
167
|
+
|
|
168
|
+
/** Недосмотренные ролики: прогресс есть, но до конца не дошли. */
|
|
169
|
+
const continueWatching = computed(() => getHistoryStub()
|
|
170
|
+
.filter(item => item.progress > 0 && item.progress < 100)
|
|
171
|
+
.filter(item => !removedGuids.value.includes(item.guid))
|
|
172
|
+
.slice(0, CONTINUE_LIMIT));
|
|
173
|
+
|
|
174
|
+
const newTotal = computed(() => libraryStub.newModels + libraryStub.newChannels + libraryStub.newPlaylists);
|
|
175
|
+
|
|
176
|
+
const collections = computed<LibraryLink[]>(() => [
|
|
177
|
+
{
|
|
178
|
+
to: generateLink('/favorites'),
|
|
179
|
+
icon: 'heart-stroke',
|
|
180
|
+
title: t('favorites'),
|
|
181
|
+
subtitle: t('plural.videos', libraryStub.favorites),
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
to: `${generateLink('/favorites')}?tab=watch-later`,
|
|
185
|
+
icon: 'clock',
|
|
186
|
+
title: t('watch_later'),
|
|
187
|
+
subtitle: t('plural.videos', libraryStub.watchLater),
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
to: generateLink('/playlists'),
|
|
191
|
+
icon: 'playlist',
|
|
192
|
+
title: t('my_playlists'),
|
|
193
|
+
subtitle: t('library.playlists_visibility', {
|
|
194
|
+
privateCount: libraryStub.privatePlaylists,
|
|
195
|
+
publicCount: libraryStub.publicPlaylists,
|
|
196
|
+
}),
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
to: generateLink('/history'),
|
|
200
|
+
icon: 'history',
|
|
201
|
+
title: t('history'),
|
|
202
|
+
subtitle: t('library.watched_today', { count: libraryStub.watchedToday }),
|
|
203
|
+
},
|
|
204
|
+
]);
|
|
205
|
+
|
|
206
|
+
const following = computed<LibraryLink[]>(() => [
|
|
207
|
+
{
|
|
208
|
+
to: generateLink('/library/models'),
|
|
209
|
+
icon: 'user',
|
|
210
|
+
title: t('models'),
|
|
211
|
+
subtitle: t('plural.models', libraryStub.models),
|
|
212
|
+
badge: t('library.new_count', { count: libraryStub.newModels }),
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
to: generateLink('/channels'),
|
|
216
|
+
icon: 'play-circle',
|
|
217
|
+
title: t('channels'),
|
|
218
|
+
subtitle: t('plural.channels', libraryStub.channels),
|
|
219
|
+
badge: t('library.new_count', { count: libraryStub.newChannels }),
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
to: generateLink('/categories'),
|
|
223
|
+
icon: 'grid',
|
|
224
|
+
title: t('categories'),
|
|
225
|
+
subtitle: t('plural.categories', libraryStub.categories),
|
|
226
|
+
badge: `+${libraryStub.categoryVideosPerWeek}`,
|
|
227
|
+
badgeNote: t('library.this_week'),
|
|
228
|
+
badgeTheme: 'bordered',
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
to: generateLink('/playlists'),
|
|
232
|
+
icon: 'playlist',
|
|
233
|
+
title: t('playlists'),
|
|
234
|
+
subtitle: t('plural.playlists', libraryStub.playlists),
|
|
235
|
+
badge: t('library.new_count', { count: libraryStub.newPlaylists }),
|
|
236
|
+
},
|
|
237
|
+
]);
|
|
238
|
+
|
|
239
|
+
function onRemove(card: HistoryStubCard) {
|
|
240
|
+
removedGuids.value = [...removedGuids.value, card.guid];
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const isShareOpen = ref(false);
|
|
244
|
+
const shareCard = ref<IVideoCard | null>(null);
|
|
245
|
+
|
|
246
|
+
const shareUrl = computed(() => shareCard.value ? generateLink(`/videos/${shareCard.value.id}`) : '');
|
|
247
|
+
|
|
248
|
+
function onShare(card: IVideoCard) {
|
|
249
|
+
shareCard.value = card;
|
|
250
|
+
isShareOpen.value = true;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
useNoindex();
|
|
254
|
+
|
|
255
|
+
const { brandName } = useAppConfig();
|
|
256
|
+
|
|
257
|
+
useHead(() => ({
|
|
258
|
+
title: `${t('navigation.library')} — ${brandName}`,
|
|
259
|
+
}));
|
|
260
|
+
|
|
261
|
+
const breadcrumbs = [
|
|
262
|
+
{
|
|
263
|
+
name: `${t('navigation.main')}`,
|
|
264
|
+
to: '/',
|
|
265
|
+
},
|
|
266
|
+
{
|
|
267
|
+
name: `${t('navigation.library')}`,
|
|
268
|
+
},
|
|
269
|
+
] as IBreadcrumbItem[];
|
|
270
|
+
</script>
|
|
271
|
+
|
|
272
|
+
<style lang="scss">
|
|
273
|
+
.library__header {
|
|
274
|
+
display: flex;
|
|
275
|
+
flex-wrap: wrap;
|
|
276
|
+
align-items: center;
|
|
277
|
+
justify-content: space-between;
|
|
278
|
+
column-gap: 16px;
|
|
279
|
+
row-gap: 12px;
|
|
280
|
+
margin-bottom: 24px;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
.library__title._title {
|
|
284
|
+
margin-bottom: 0;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
.library__save .ui-toggle__text {
|
|
288
|
+
@include font-sm;
|
|
289
|
+
|
|
290
|
+
color: $page-library-toggle-text;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
.library__section {
|
|
294
|
+
margin-bottom: 48px;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
.library__section-header {
|
|
298
|
+
display: flex;
|
|
299
|
+
flex-wrap: wrap;
|
|
300
|
+
align-items: center;
|
|
301
|
+
justify-content: space-between;
|
|
302
|
+
column-gap: 16px;
|
|
303
|
+
row-gap: 12px;
|
|
304
|
+
margin-bottom: 16px;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
.library__heading {
|
|
308
|
+
@include font-xl;
|
|
309
|
+
|
|
310
|
+
display: flex;
|
|
311
|
+
align-items: center;
|
|
312
|
+
column-gap: 8px;
|
|
313
|
+
margin: 0;
|
|
314
|
+
font-weight: 600;
|
|
315
|
+
color: $page-library-heading;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
.library__heading-icon {
|
|
319
|
+
display: grid;
|
|
320
|
+
place-content: center;
|
|
321
|
+
flex-shrink: 0;
|
|
322
|
+
width: 24px;
|
|
323
|
+
height: 24px;
|
|
324
|
+
color: $page-library-heading-icon;
|
|
325
|
+
background-color: $page-library-heading-icon-bg;
|
|
326
|
+
border-radius: $radius-md;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
.library__continue {
|
|
330
|
+
display: grid;
|
|
331
|
+
gap: 16px;
|
|
332
|
+
grid-template-columns: 1fr;
|
|
333
|
+
|
|
334
|
+
@include from-br(xm) {
|
|
335
|
+
grid-template-columns: repeat(2, 1fr);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
@include from-br(sm) {
|
|
339
|
+
gap: 24px;
|
|
340
|
+
grid-template-columns: repeat(3, 1fr);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
@include from-br(md) {
|
|
344
|
+
grid-template-columns: repeat(4, 1fr);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
.library__grid {
|
|
349
|
+
display: grid;
|
|
350
|
+
gap: 16px;
|
|
351
|
+
margin: 0;
|
|
352
|
+
padding: 0;
|
|
353
|
+
list-style: none;
|
|
354
|
+
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
|
|
355
|
+
|
|
356
|
+
@include from-br(sm) {
|
|
357
|
+
gap: 24px;
|
|
358
|
+
grid-template-columns: repeat(4, 1fr);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/* Карточка тянется на всю высоту ряда — иначе соседи с двухстрочным тайтлом разной высоты. */
|
|
363
|
+
.library__grid > li {
|
|
364
|
+
display: grid;
|
|
365
|
+
}
|
|
366
|
+
</style>
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
<!-- Подписки на моделей: сортировка и лента последних роликов по каждой модели. Данные пока заглушка -->
|
|
2
|
+
<template>
|
|
3
|
+
<div class="library-models _page _container">
|
|
4
|
+
<UiBreadcrumbs
|
|
5
|
+
:items="breadcrumbs"
|
|
6
|
+
class="_breadcrumbs"
|
|
7
|
+
/>
|
|
8
|
+
|
|
9
|
+
<UiSectionTitle
|
|
10
|
+
:title="t('library.my_models')"
|
|
11
|
+
:count="models.length"
|
|
12
|
+
class="library-models__title"
|
|
13
|
+
icon="user"
|
|
14
|
+
/>
|
|
15
|
+
|
|
16
|
+
<div class="library-models__sort">
|
|
17
|
+
<UiBtn
|
|
18
|
+
v-for="item in sortItems"
|
|
19
|
+
:key="item.name"
|
|
20
|
+
:theme="sort === item.name ? 'primary' : 'secondary'"
|
|
21
|
+
@click="sort = item.name"
|
|
22
|
+
>{{ item.title }}</UiBtn>
|
|
23
|
+
</div>
|
|
24
|
+
|
|
25
|
+
<div
|
|
26
|
+
v-if="models.length > 0"
|
|
27
|
+
class="library-models__list"
|
|
28
|
+
>
|
|
29
|
+
<LibraryModel
|
|
30
|
+
v-for="(model, index) in models"
|
|
31
|
+
:key="model.guid"
|
|
32
|
+
:model="model"
|
|
33
|
+
:following="isFollowing(model)"
|
|
34
|
+
:priority="index === 0"
|
|
35
|
+
@toggle-follow="onToggleFollow"
|
|
36
|
+
/>
|
|
37
|
+
</div>
|
|
38
|
+
|
|
39
|
+
<UiEmptyState
|
|
40
|
+
v-else
|
|
41
|
+
without-reset
|
|
42
|
+
/>
|
|
43
|
+
|
|
44
|
+
<UiConfirm
|
|
45
|
+
v-model="isUnfollowOpen"
|
|
46
|
+
:title="t('library.unfollow_title', { name: pendingUnfollow?.title })"
|
|
47
|
+
:confirm-text="t('library.unfollow_confirm')"
|
|
48
|
+
@confirm="onUnfollowConfirm"
|
|
49
|
+
>
|
|
50
|
+
<p class="library-models__confirm">{{ t('library.unfollow_text') }}</p>
|
|
51
|
+
</UiConfirm>
|
|
52
|
+
</div>
|
|
53
|
+
</template>
|
|
54
|
+
|
|
55
|
+
<script setup lang="ts">
|
|
56
|
+
import { getLibraryModelsStub } from '../../stubs/library-models';
|
|
57
|
+
import type { LibraryModelStub } from '../../stubs/library-models';
|
|
58
|
+
import type { IBreadcrumbItem } from '../../types';
|
|
59
|
+
|
|
60
|
+
const LibrarySort = {
|
|
61
|
+
New: 'new',
|
|
62
|
+
Alphabet: 'alphabet',
|
|
63
|
+
} as const;
|
|
64
|
+
|
|
65
|
+
type LibrarySortValue = typeof LibrarySort[keyof typeof LibrarySort];
|
|
66
|
+
|
|
67
|
+
const { t } = useI18n();
|
|
68
|
+
|
|
69
|
+
const sort = ref<LibrarySortValue>(LibrarySort.New);
|
|
70
|
+
|
|
71
|
+
const sortItems = computed(() => [
|
|
72
|
+
{ name: LibrarySort.New, title: t('library.sort_new_first') },
|
|
73
|
+
{ name: LibrarySort.Alphabet, title: t('library.sort_alphabet') },
|
|
74
|
+
]);
|
|
75
|
+
|
|
76
|
+
const unfollowed = ref<string[]>([]);
|
|
77
|
+
|
|
78
|
+
const allModels = getLibraryModelsStub();
|
|
79
|
+
|
|
80
|
+
const models = computed(() => {
|
|
81
|
+
const items = [...allModels];
|
|
82
|
+
|
|
83
|
+
return sort.value === LibrarySort.Alphabet
|
|
84
|
+
? items.sort((a, b) => a.title.localeCompare(b.title))
|
|
85
|
+
: items.sort((a, b) => b.lastVideoAddedAt - a.lastVideoAddedAt);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
function isFollowing(model: LibraryModelStub) {
|
|
89
|
+
return !unfollowed.value.includes(model.guid);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** Модель, для которой открыт попап отписки. */
|
|
93
|
+
const pendingUnfollow = ref<LibraryModelStub | null>(null);
|
|
94
|
+
|
|
95
|
+
const isUnfollowOpen = computed({
|
|
96
|
+
get: () => pendingUnfollow.value !== null,
|
|
97
|
+
set: value => {
|
|
98
|
+
if (!value) pendingUnfollow.value = null;
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
/** Подписка — сразу, отписка — только через подтверждение: она сносит ленту обновлений. */
|
|
103
|
+
function onToggleFollow(model: LibraryModelStub) {
|
|
104
|
+
if (isFollowing(model)) {
|
|
105
|
+
pendingUnfollow.value = model;
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
unfollowed.value = unfollowed.value.filter(guid => guid !== model.guid);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function onUnfollowConfirm() {
|
|
113
|
+
if (!pendingUnfollow.value) return;
|
|
114
|
+
|
|
115
|
+
unfollowed.value = [...unfollowed.value, pendingUnfollow.value.guid];
|
|
116
|
+
pendingUnfollow.value = null;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
useNoindex();
|
|
120
|
+
|
|
121
|
+
const { brandName } = useAppConfig();
|
|
122
|
+
|
|
123
|
+
useHead(() => ({
|
|
124
|
+
title: `${t('library.my_models')} — ${brandName}`,
|
|
125
|
+
}));
|
|
126
|
+
|
|
127
|
+
const breadcrumbs = [
|
|
128
|
+
{
|
|
129
|
+
name: `${t('navigation.main')}`,
|
|
130
|
+
to: '/',
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
name: `${t('navigation.library')}`,
|
|
134
|
+
to: '/library',
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
name: `${t('library.my_models')}`,
|
|
138
|
+
},
|
|
139
|
+
] as IBreadcrumbItem[];
|
|
140
|
+
</script>
|
|
141
|
+
|
|
142
|
+
<style lang="scss">
|
|
143
|
+
.library-models__title {
|
|
144
|
+
margin-bottom: 16px;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
.library-models__sort {
|
|
148
|
+
display: flex;
|
|
149
|
+
flex-wrap: wrap;
|
|
150
|
+
align-items: center;
|
|
151
|
+
gap: 8px;
|
|
152
|
+
margin-bottom: 24px;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.library-models__list {
|
|
156
|
+
display: grid;
|
|
157
|
+
row-gap: 24px;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
.library-models__confirm {
|
|
161
|
+
@include font-sm;
|
|
162
|
+
|
|
163
|
+
max-width: 400px;
|
|
164
|
+
margin: 0 auto;
|
|
165
|
+
text-align: center;
|
|
166
|
+
color: $page-library-confirm-text;
|
|
167
|
+
}
|
|
168
|
+
</style>
|