spoko-design-system 1.1.17 → 1.2.1

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.
Files changed (59) hide show
  1. package/.github/workflows/code-quality.yml +9 -18
  2. package/.github/workflows/deploy.yml +4 -5
  3. package/.github/workflows/release.yml +7 -19
  4. package/.github/workflows/sonarcloud.yml +7 -19
  5. package/.vscode/settings.json +20 -0
  6. package/CHANGELOG.md +31 -0
  7. package/icon.config.ts +3 -1
  8. package/package.json +16 -16
  9. package/sonar-project.properties +4 -1
  10. package/src/components/Badge.vue +4 -1
  11. package/src/components/Badges.vue +40 -10
  12. package/src/components/Breadcrumbs.vue +41 -12
  13. package/src/components/ButtonCopy.vue +10 -4
  14. package/src/components/Category/CategoryLink.vue +5 -1
  15. package/src/components/Category/CategorySidebarToggler.vue +5 -1
  16. package/src/components/FeaturesList.vue +11 -4
  17. package/src/components/FuckRussia.vue +36 -9
  18. package/src/components/Input.vue +18 -5
  19. package/src/components/MainColors.vue +13 -3
  20. package/src/components/MainInput.vue +2 -3
  21. package/src/components/PartNumber.vue +4 -1
  22. package/src/components/Product/ProductButton.vue +4 -1
  23. package/src/components/Product/ProductDetailName.vue +4 -1
  24. package/src/components/Product/ProductDetails.vue +57 -15
  25. package/src/components/Product/ProductDoc.vue +4 -1
  26. package/src/components/Product/ProductEngineType.vue +4 -1
  27. package/src/components/Product/ProductLink.vue +27 -8
  28. package/src/components/Product/ProductLinkInfo.astro +1 -1
  29. package/src/components/Product/ProductModel.vue +4 -1
  30. package/src/components/Product/ProductModels.vue +10 -2
  31. package/src/components/Product/ProductPositions.vue +5 -1
  32. package/src/components/ProductCodes.vue +9 -2
  33. package/src/components/ProductDetailName.vue +4 -1
  34. package/src/components/ProductDetailsList.vue +46 -15
  35. package/src/components/SlimBanner.vue +9 -7
  36. package/src/components/Table.vue +16 -4
  37. package/src/components/Translations.vue +4 -1
  38. package/src/pages/index.astro +12 -12
  39. package/src/styles/base/base.css +7 -8
  40. package/src/styles/base/typography.css +2 -2
  41. package/src/styles/main.css +0 -1
  42. package/src/types/Product.ts +77 -22
  43. package/src/types/catalog.ts +33 -0
  44. package/src/types/category.ts +54 -0
  45. package/src/types/common.ts +38 -0
  46. package/src/types/index.ts +5 -232
  47. package/src/utils/api/getCategories.ts +0 -4
  48. package/src/utils/product/getProductChecklist.ts +1 -1
  49. package/src/utils/seo/getShorterDescription.ts +1 -1
  50. package/uno-config/theme/index.ts +7 -9
  51. package/uno-config/theme/shortcuts/buttons.ts +1 -1
  52. package/.astro/content-assets.mjs +0 -1
  53. package/.astro/content-modules.mjs +0 -1
  54. package/.astro/content.d.ts +0 -210
  55. package/.astro/data-store.json +0 -1
  56. package/.astro/settings.json +0 -5
  57. package/.astro/types.d.ts +0 -2
  58. package/.claude/settings.local.json +0 -25
  59. /package/{src/components/Jumbotron/styles.css → .nojekyll} +0 -0
@@ -5,7 +5,7 @@ const props = defineProps<{
5
5
 
6
6
  const theads = Object.keys(Object.values(props.data)[0]);
7
7
 
8
- const capitalizeFirstLetter = (text: String) => {
8
+ const capitalizeFirstLetter = (text: string) => {
9
9
  return text[0].toUpperCase() + text.slice(1);
10
10
  };
11
11
  </script>
@@ -14,14 +14,26 @@ const capitalizeFirstLetter = (text: String) => {
14
14
  <table class="table-auto text-left border bg-white shadow-md">
15
15
  <thead class="bg-gray-500 text-white">
16
16
  <tr class="border">
17
- <th v-for="(thead, index) in theads" :key="index" class="px-4 py-2 font-semibold">
17
+ <th
18
+ v-for="(thead, index) in theads"
19
+ :key="index"
20
+ class="px-4 py-2 font-semibold"
21
+ >
18
22
  {{ capitalizeFirstLetter(thead) }}
19
23
  </th>
20
24
  </tr>
21
25
  </thead>
22
26
  <tbody>
23
- <tr v-for="row in props.data" :key="row" class="border">
24
- <td v-for="key in Object.keys(row)" :key="key" class="px-4 py-2">
27
+ <tr
28
+ v-for="(row, index) in props.data"
29
+ :key="index"
30
+ class="border"
31
+ >
32
+ <td
33
+ v-for="key in Object.keys(row)"
34
+ :key="key"
35
+ class="px-4 py-2"
36
+ >
25
37
  {{ row[key] }}
26
38
  </td>
27
39
  </tr>
@@ -18,7 +18,10 @@ const props = defineProps({
18
18
  </script>
19
19
 
20
20
  <template>
21
- <div v-if="props.translations !== null && props.translations.uri" data-pagefind-ignore>
21
+ <div
22
+ v-if="props.translations !== null && props.translations.uri"
23
+ data-pagefind-ignore
24
+ >
22
25
  <a
23
26
  aria-label="Change language"
24
27
  type="button"
@@ -60,7 +60,7 @@ const navItems = [
60
60
  class="flex w-full flex-wrap bg-white rounded-md hover:-translate-y-1 hover:shadow-lg transition-all flex-1 items-center py-10 px-4 md:(flex-col w-auto flex-nowrap text-center py-16)"
61
61
  >
62
62
  <Headline as="h2" textSize="2xl" underline={false}>
63
- <Icon name={icon} aria-hidden="true" class="text-blue-400 mr-2" />
63
+ <Icon name={icon} aria-hidden="true" class="text-blue-400 mr-2 text-4xl" />
64
64
  {title}
65
65
  </Headline>
66
66
  <p class="text-slate-500 w-full">{description}</p>
@@ -91,9 +91,9 @@ const navItems = [
91
91
  class="mb-3 text-gray-900 flex items-center justify-center"
92
92
  >
93
93
  <Icon
94
- name="simple-icons:astro"
94
+ name="vscode-icons:file-type-light-astro"
95
95
  aria-hidden="true"
96
- class="text-3xl text-blue-400 mr-3"
96
+ class="text-4xl text-blue-400 mr-3"
97
97
  />
98
98
  Astro-Powered
99
99
  </Headline>
@@ -117,7 +117,7 @@ const navItems = [
117
117
  <Icon
118
118
  name="streamline-freehand-color:data-transfer-document-module"
119
119
  aria-hidden="true"
120
- class="text-3xl text-blue-400 mr-3"
120
+ class="text-4xl text-blue-400 mr-3"
121
121
  />
122
122
  Rich Components
123
123
  </Headline>
@@ -137,7 +137,7 @@ const navItems = [
137
137
  <Icon
138
138
  name="vscode-icons:file-type-unocss"
139
139
  aria-hidden="true"
140
- class="text-3xl text-blue-400 mr-3"
140
+ class="text-4xl text-blue-400 mr-3"
141
141
  />
142
142
  UnoCSS Styling
143
143
  </Headline>
@@ -161,7 +161,7 @@ const navItems = [
161
161
  <Icon
162
162
  name="vscode-icons:file-type-vue"
163
163
  aria-hidden="true"
164
- class="text-3xl text-blue-400 mr-3"
164
+ class="text-4xl text-blue-400 mr-3"
165
165
  />
166
166
  Vue Integration
167
167
  </Headline>
@@ -185,7 +185,7 @@ const navItems = [
185
185
  <Icon
186
186
  name="streamline-freehand-color:design-process-drawing-board"
187
187
  aria-hidden="true"
188
- class="text-3xl text-blue-400 mr-3"
188
+ class="text-4xl text-blue-400 mr-3"
189
189
  />
190
190
  Design Patterns
191
191
  </Headline>
@@ -205,7 +205,7 @@ const navItems = [
205
205
  <Icon
206
206
  name="streamline-freehand-color:app-window-source-code"
207
207
  aria-hidden="true"
208
- class="text-3xl text-blue-400 mr-3"
208
+ class="text-4xl text-blue-400 mr-3"
209
209
  />
210
210
  Developer Ready
211
211
  </Headline>
@@ -237,7 +237,7 @@ const navItems = [
237
237
  <Icon
238
238
  name="streamline-freehand-color:archive-box"
239
239
  aria-hidden="true"
240
- class="text-3xl text-blue-400 mr-3"
240
+ class="text-4xl text-blue-400 mr-3"
241
241
  />
242
242
  Get Started
243
243
  </Headline>
@@ -262,7 +262,7 @@ const navItems = [
262
262
  rel="noopener noreferrer"
263
263
  class="flex items-center gap-3 bg-red-600 hover:bg-red-700 text-white px-6 py-3 rounded-lg transition-colors"
264
264
  >
265
- <Icon name="mdi:npm" class="text-3xl" />
265
+ <Icon name="vscode-icons:file-type-npm" class="text-3xl" />
266
266
  <span class="font-semibold">npm Package</span>
267
267
  </a>
268
268
  <a
@@ -285,9 +285,9 @@ const navItems = [
285
285
  <div class="text-center">
286
286
  <Headline underline as="h2" class="text-gray-900 mb-8" textSize="3xl">
287
287
  <Icon
288
- name="streamline-freehand-color:app-window-source-code"
288
+ name="streamline-freehand-color:coding-files-network-folder"
289
289
  aria-hidden="true"
290
- class="text-3xl text-blue-400 mr-3"
290
+ class="text-4xl text-blue-400 mr-3"
291
291
  />
292
292
  Live Examples
293
293
  </Headline>
@@ -29,6 +29,13 @@
29
29
  --clr-accent-300: hsl(358, 72%, 65%);
30
30
  --clr-accent-400: hsl(358, 72%, 50%);
31
31
  --clr-accent-500: hsl(358, 72%, 35%);
32
+
33
+ /* Swiper styles */
34
+ /* // --swiper-navigation-color: #0099da; */
35
+ --swiper-navigation-size: 22px;
36
+ --swiper-scrollbar-drag-bg-color: var(--clr-primary-400);
37
+ --swiper-scrollbar-size: 1px;
38
+ --swiper-scrollbar-bottom: 0px;
32
39
  }
33
40
 
34
41
  * {
@@ -103,14 +110,6 @@ html {
103
110
  scroll-behavior: smooth;
104
111
  }
105
112
 
106
- /* Swiper styles */
107
- :root {
108
- /* // --swiper-navigation-color: #0099da; */
109
- --swiper-navigation-size: 22px;
110
- --swiper-scrollbar-drag-bg-color: var(--clr-primary-400);
111
- --swiper-scrollbar-size: 1px;
112
- --swiper-scrollbar-bottom: 0px;
113
- }
114
113
 
115
114
  .swiper-horizontal {
116
115
  & > .swiper-scrollbar {
@@ -15,7 +15,7 @@
15
15
  }
16
16
 
17
17
  .font-vw-textbold {
18
- font-family: 'vw_textbold';
18
+ font-family: 'vw_textbold', sans-serif;
19
19
  font-display: fallback;
20
20
  }
21
21
 
@@ -28,7 +28,7 @@
28
28
  }
29
29
 
30
30
  .font-vw-headbold {
31
- font-family: 'vw_headbold' !important;
31
+ font-family: 'vw_headbold', sans-serif !important;
32
32
  font-display: fallback;
33
33
  }
34
34
 
@@ -1,4 +1,3 @@
1
- /* @import "variables"; */
2
1
  @import 'base/typography';
3
2
  @import 'base/base';
4
3
  @import 'base/grid';
@@ -1,32 +1,87 @@
1
- export interface ProductImage {
2
- path: string;
3
- }
1
+ import type { Color, EngineType, MaterialsObject } from './common';
4
2
 
5
- export interface BaseProduct {
6
- id: string | number;
3
+ export interface Replacement {
4
+ date: string | null;
5
+ info: string | null;
7
6
  number: string;
8
- photo: string | null;
9
- price_pln?: number;
7
+ prcodes: string[] | null;
8
+ manufacturer: string | null;
9
+ }
10
+
11
+ export interface DetailList {
12
+ name: string;
13
+ id: string;
14
+ value: string | number[] | Color[];
15
+ translated: boolean;
16
+ icon: boolean | null;
17
+ isArrayValue: boolean | null;
18
+ }
19
+
20
+ export interface ImportantDetail {
21
+ name: string;
22
+ id: string;
23
+ value: string | number[];
24
+ translated: boolean;
25
+ icon: boolean | null;
26
+ isArrayValue: boolean | null;
27
+ }
28
+
29
+ export interface Detail {
30
+ name: string;
31
+ value: string | number[];
32
+ translated: boolean;
33
+ icon: boolean | null;
34
+ isArrayValue: boolean | null;
10
35
  }
11
36
 
12
- export interface ShopProduct extends BaseProduct {
13
- images: ProductImage[];
14
- slug: string;
15
- name_pl: string;
16
- name_en: string;
37
+ export interface DetailObject {
38
+ importantDetails: ImportantDetail[];
39
+ list: DetailList[];
40
+ checklist: DetailList[];
17
41
  }
18
42
 
19
- export interface CatalogProduct extends BaseProduct {
20
- photo: string | null;
43
+ export interface Image {
44
+ id: number;
45
+ path: string;
21
46
  }
22
47
 
23
- export type Product = ShopProduct | CatalogProduct;
48
+ export interface ImageObject {
49
+ id: number;
50
+ path: string;
51
+ }
24
52
 
25
- export interface ProductLinkProps {
26
- productId: string;
27
- bigTile?: boolean;
28
- locale: string;
29
- index?: number | null;
30
- loading?: 'lazy' | 'eager';
31
- isShopProduct?: boolean;
53
+ export interface Product {
54
+ id: number;
55
+ number: string;
56
+ info: string | null;
57
+ date: string | null;
58
+ prcodes: string[] | null;
59
+ engines: string[] | null;
60
+ engine_type: EngineType[] | null;
61
+ manufacturer: string[] | null;
62
+ manufacturer_number: string | null;
63
+ related: string[] | null;
64
+ color_ids: string[] | null;
65
+ position: string[] | null;
66
+ model: string[] | null;
67
+ replacement: Replacement[] | null;
68
+ details: DetailList[] | null;
69
+ photo: string;
70
+ files: number[] | null;
71
+ images: Image[] | null;
72
+ category_id: number;
73
+ hand_drive: number | null;
74
+ materials: MaterialsObject[] | null;
75
+ sort: number;
76
+ created_at: string;
77
+ updated_at: string;
32
78
  }
79
+
80
+
81
+ export interface ProductImage {
82
+ title: string;
83
+ alt: string;
84
+ link: string;
85
+ style?: string;
86
+ __typename: string;
87
+ }
@@ -0,0 +1,33 @@
1
+ // Catalog/Shop-specific product types
2
+ export interface ProductImage {
3
+ path: string;
4
+ }
5
+
6
+ export interface BaseProduct {
7
+ id: string | number;
8
+ number: string;
9
+ photo: string | null;
10
+ price_pln?: number;
11
+ }
12
+
13
+ export interface ShopProduct extends BaseProduct {
14
+ images: ProductImage[];
15
+ slug: string;
16
+ name_pl: string;
17
+ name_en: string;
18
+ }
19
+
20
+ export interface CatalogProduct extends BaseProduct {
21
+ photo: string | null;
22
+ }
23
+
24
+ export type Product = ShopProduct | CatalogProduct;
25
+
26
+ export interface ProductLinkProps {
27
+ productId: string;
28
+ bigTile?: boolean;
29
+ locale: string;
30
+ index?: number | null;
31
+ loading?: 'lazy' | 'eager';
32
+ isShopProduct?: boolean;
33
+ }
@@ -0,0 +1,54 @@
1
+ export interface Breadcrumb {
2
+ name: string;
3
+ path: string;
4
+ }
5
+
6
+
7
+ export interface SubCategory {
8
+ id: number;
9
+ name: string;
10
+ parent_id: number;
11
+ title?: string;
12
+ desc?: string;
13
+ slug: string;
14
+ sort: number | null;
15
+ description?: string;
16
+ hasProducts: boolean;
17
+ photo?: string | null;
18
+ children?: SubCategory[];
19
+ seoTitle?: string;
20
+ }
21
+
22
+ export interface Category {
23
+ id: number;
24
+ parent_id: number | null;
25
+ title?: string;
26
+ slug: string;
27
+ sort: number | null;
28
+ description?: string;
29
+ hasProducts: boolean;
30
+ photo?: string | null;
31
+ children: SubCategory[];
32
+ seoTitle?: string;
33
+ }
34
+
35
+ export interface CatObject {
36
+ id: number;
37
+ name: string;
38
+ desc: string;
39
+ parent_id: number | null;
40
+ title?: string;
41
+ slug: string;
42
+ sort?: number | null;
43
+ description?: string;
44
+ hasProducts: boolean;
45
+ photo?: string | null;
46
+ children: SubCategory[];
47
+ seoTitle?: string;
48
+ }
49
+
50
+
51
+ export interface Link {
52
+ path: string;
53
+ name: string;
54
+ }
@@ -0,0 +1,38 @@
1
+ export interface Color {
2
+ id: number;
3
+ slug: string;
4
+ name?: string;
5
+ }
6
+
7
+ export interface EngineType {
8
+ id: number;
9
+ type: string;
10
+ }
11
+
12
+ export interface Model {
13
+ id: number;
14
+ slug: string;
15
+ sort: number;
16
+ name: string;
17
+ }
18
+
19
+ export interface Material {
20
+ id: number;
21
+ name: string;
22
+ slug: string;
23
+ sort: number | null;
24
+ value?: string | null;
25
+ }
26
+
27
+ export interface MaterialsObject {
28
+ name: string;
29
+ list: Material[];
30
+ }
31
+
32
+ export interface Position {
33
+ id: number;
34
+ slug: string;
35
+ sort: number;
36
+ name: string;
37
+ }
38
+
@@ -1,234 +1,7 @@
1
1
  /// <reference types="vite/client" />
2
2
 
3
- export interface Breadcrumb {
4
- name: string;
5
- path: string;
6
- }
7
-
8
- export interface Navigation {
9
- currentIndex: number | null;
10
- prev: Product | null;
11
- next: Product | null;
12
- }
13
-
14
- export interface ResponsiveImage {
15
- media: string;
16
- src: string;
17
- }
18
-
19
- export interface SubCategory {
20
- id: number;
21
- name: string;
22
- parent_id: number;
23
- title?: string;
24
- desc?: string;
25
- slug: string;
26
- sort: number | null;
27
- description?: string;
28
- hasProducts: boolean;
29
- photo?: string | null;
30
- children?: SubCategory[];
31
- seoTitle?: string;
32
- }
33
-
34
- export interface Category {
35
- id: number;
36
- parent_id: number | null;
37
- title?: string;
38
- slug: string;
39
- sort: number | null;
40
- description?: string;
41
- hasProducts: boolean;
42
- photo?: string | null;
43
- children: SubCategory[];
44
- seoTitle?: string;
45
- }
46
-
47
- export interface CatObject {
48
- id: number;
49
- name: string;
50
- desc: string;
51
- parent_id: number | null;
52
- title?: string;
53
- slug: string;
54
- sort?: number | null;
55
- description?: string;
56
- hasProducts: boolean;
57
- photo?: string | null;
58
- children: SubCategory[];
59
- seoTitle?: string;
60
- }
61
-
62
- export interface CatLevels {
63
- lvl1: Category | CatObject | null;
64
- lvl2: CatObject | null;
65
- lvl3: CatObject | null;
66
- }
67
- export interface CatInfo {
68
- category: CatObject | null;
69
- subcategory: CatObject | null;
70
- subsubcategory: CatObject | null;
71
- }
72
-
73
- export interface Replacement {
74
- date: string | null;
75
- info: string | null;
76
- number: string;
77
- prcodes: string[] | null;
78
- manufacturer: string | null;
79
- }
80
-
81
- export interface Color {
82
- id: number;
83
- slug: string;
84
- name?: string;
85
- }
86
-
87
- export interface DetailList {
88
- name: string;
89
- id: string;
90
- value: string | number[] | Color[];
91
- translated: boolean;
92
- icon: boolean | null;
93
- isArrayValue: boolean | null;
94
- }
95
-
96
- export interface importantDetail {
97
- name: string;
98
- id: string;
99
- value: string | number[];
100
- translated: boolean;
101
- icon: boolean | null;
102
- isArrayValue: boolean | null;
103
- }
104
-
105
- export interface Detail {
106
- name: string;
107
- value: string | number[];
108
- translated: boolean;
109
- icon: boolean | null;
110
- isArrayValue: boolean | null;
111
- }
112
-
113
- export interface DetailObject {
114
- importantDetails: importantDetail[];
115
- list: DetailList[];
116
- checklist: DetailList[];
117
- }
118
-
119
- export interface Image {
120
- id: number;
121
- path: string;
122
- }
123
-
124
- export interface ImageType {
125
- id: number;
126
- path: string;
127
- }
128
-
129
- export interface ImageApi {
130
- id: number;
131
- slug: string;
132
- path: string;
133
- }
134
-
135
- export interface ImageObject {
136
- id: number;
137
- path: string;
138
- }
139
-
140
- export interface ProductFile {
141
- id: number;
142
- name: string;
143
- slug: string;
144
- path: string;
145
- type: number;
146
- }
147
-
148
- export interface Product {
149
- id: number;
150
- number: string;
151
- info: string | null;
152
- date: string | null;
153
- prcodes: string[] | null;
154
- engines: string[] | null;
155
- engine_type: EngineType[] | null;
156
- manufacturer: string[] | null;
157
- manufacturer_number: string | null;
158
- related: string[] | null;
159
- color_ids: string[] | null;
160
- position: string[] | null;
161
- model: string[] | null;
162
- replacement: Replacement[] | null;
163
- details: DetailList[] | null;
164
- photo: string;
165
- files: number[] | null;
166
- images: Image[] | null;
167
- category_id: number;
168
- hand_drive: number | null;
169
- materials: MaterialsObject[] | null;
170
- sort: number;
171
- created_at: string;
172
- updated_at: string;
173
- }
174
-
175
- export interface MaterialsObject {
176
- name: string;
177
- list: Material[];
178
- }
179
-
180
- export interface Product2FullSearch {
181
- number: string;
182
- category_id: number;
183
- photo: string;
184
- hand_drive: number | null;
185
- manufacturer: string[] | null;
186
- date: string | null;
187
- text: string;
188
- details: DetailList[] | null;
189
- }
190
-
191
- export interface Link {
192
- path: string;
193
- name: string;
194
- }
195
- export interface EngineType {
196
- id: number;
197
- type: string;
198
- }
199
-
200
- export interface Model {
201
- id: number;
202
- slug: string;
203
- sort: number;
204
- name: string;
205
- }
206
-
207
- export interface Material {
208
- id: number;
209
- name: string;
210
- slug: string;
211
- sort: number | null;
212
- value?: string | null;
213
- }
214
-
215
- export interface ProductImage {
216
- title: string;
217
- alt: string;
218
- link: string;
219
- style?: string;
220
- __typename: string;
221
- }
222
-
223
- export interface Position {
224
- id: number;
225
- slug: string;
226
- sort: number;
227
- name: string;
228
- }
229
-
230
- export interface Hreflang {
231
- rel: 'alternate' | 'canonical';
232
- href: string;
233
- hreflang: 'en' | 'pl' | 'x-default';
234
- }
3
+ // Re-export all types from modular files
4
+ export * from './product';
5
+ export * from './category';
6
+ export * from './common';
7
+ export * from './catalog';
@@ -2,8 +2,4 @@ import { getData } from '../getData';
2
2
 
3
3
  export const getApiCategories = getData('categories');
4
4
 
5
- // const getApiCategories = async () => {
6
- // return await getData('categories');
7
- // }
8
-
9
5
  export default getApiCategories;
@@ -1,5 +1,5 @@
1
1
  export const getProductCheckList = productDetails => {
2
- if (!productDetails || !productDetails.length) {
2
+ if (!productDetails?.length) {
3
3
  return null;
4
4
  }
5
5
 
@@ -5,7 +5,7 @@ export const getShorterDescription = (
5
5
  limit = MAX_DESCRIPTION_LENGTH
6
6
  ) => {
7
7
  function cutString(s: string, n: number) {
8
- const text = s.replace(/(\n)/g, ' ');
8
+ const text = s.replaceAll('\n', ' ');
9
9
  const cut = text.indexOf('. ', n);
10
10
  return cut === -1 ? text : `${text.substring(0, cut)}.`;
11
11
  }