@redneckz/wildless-cms-uni-blocks 0.2.46 → 0.2.47

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 (224) hide show
  1. package/dist/BenefitsBlock.d.ts +2 -2
  2. package/dist/Blocks.d.ts +2 -0
  3. package/dist/Blocks.js +11 -7
  4. package/dist/Blocks.js.map +1 -1
  5. package/dist/ContentPage.d.ts +2 -2
  6. package/dist/ContentPage.js +3 -3
  7. package/dist/ContentPage.js.map +1 -1
  8. package/dist/ContentPageContext.d.ts +6 -3
  9. package/dist/ExchangeRateTile.d.ts +7 -0
  10. package/dist/ExchangeRateTile.js +32 -0
  11. package/dist/ExchangeRateTile.js.map +1 -0
  12. package/dist/Footer/Contacts.d.ts +2 -2
  13. package/dist/Footer/Footer.d.ts +2 -2
  14. package/dist/Footer/FooterLink.d.ts +2 -2
  15. package/dist/Footer/Sitemap.d.ts +2 -2
  16. package/dist/Footer/Sitemap.js +2 -1
  17. package/dist/Footer/Sitemap.js.map +1 -1
  18. package/dist/Footer/SocialMedia.d.ts +2 -2
  19. package/dist/Footer/SocialMedia.js +5 -5
  20. package/dist/Footer/SocialMedia.js.map +1 -1
  21. package/dist/Header/Header.d.ts +2 -2
  22. package/dist/Header/Header.js +3 -2
  23. package/dist/Header/Header.js.map +1 -1
  24. package/dist/Icons/DollarIcon.d.ts +1 -0
  25. package/dist/Icons/DollarIcon.js +6 -0
  26. package/dist/Icons/DollarIcon.js.map +1 -0
  27. package/dist/Icons/EuroIcon.d.ts +1 -0
  28. package/dist/Icons/EuroIcon.js +6 -0
  29. package/dist/Icons/EuroIcon.js.map +1 -0
  30. package/dist/Icons/index.d.ts +17 -16
  31. package/dist/Icons/index.js +26 -22
  32. package/dist/Icons/index.js.map +1 -1
  33. package/dist/Img.js +1 -1
  34. package/dist/Img.js.map +1 -1
  35. package/dist/LikeControl.d.ts +2 -2
  36. package/dist/LikeControl.js.map +1 -1
  37. package/dist/MobileAppTile.d.ts +8 -0
  38. package/dist/MobileAppTile.js +9 -0
  39. package/dist/MobileAppTile.js.map +1 -0
  40. package/dist/Placeholder.d.ts +3 -1
  41. package/dist/Placeholder.js +9 -2
  42. package/dist/Placeholder.js.map +1 -1
  43. package/dist/ProductBlock.d.ts +2 -2
  44. package/dist/ProductBlockInner.d.ts +2 -2
  45. package/dist/ProductGallery.d.ts +2 -2
  46. package/dist/ProductGallery.js +2 -2
  47. package/dist/ProductGallery.js.map +1 -1
  48. package/dist/ProjectSettings.d.ts +18 -0
  49. package/dist/ProjectSettings.js +30 -0
  50. package/dist/ProjectSettings.js.map +1 -0
  51. package/dist/StepsBlock.d.ts +2 -2
  52. package/dist/Tile.d.ts +2 -2
  53. package/dist/Tile.js +15 -7
  54. package/dist/Tile.js.map +1 -1
  55. package/dist/index.d.ts +5 -3
  56. package/dist/index.js +7 -5
  57. package/dist/index.js.map +1 -1
  58. package/dist/types.d.ts +1 -1
  59. package/dist/ui-kit/SearchBar.d.ts +2 -2
  60. package/dist/ui-kit/SearchBar.js.map +1 -1
  61. package/dist/useSitemap.d.ts +3 -0
  62. package/dist/useSitemap.js +16 -0
  63. package/dist/useSitemap.js.map +1 -0
  64. package/lib/BenefitsBlock.d.ts +2 -2
  65. package/lib/Blocks.d.ts +2 -0
  66. package/lib/Blocks.js +8 -4
  67. package/lib/Blocks.js.map +1 -1
  68. package/lib/ContentPage.d.ts +2 -2
  69. package/lib/ContentPage.js +3 -3
  70. package/lib/ContentPage.js.map +1 -1
  71. package/lib/ContentPageContext.d.ts +6 -3
  72. package/lib/ExchangeRateTile.d.ts +7 -0
  73. package/lib/ExchangeRateTile.js +30 -0
  74. package/lib/ExchangeRateTile.js.map +1 -0
  75. package/lib/Footer/Contacts.d.ts +2 -2
  76. package/lib/Footer/Footer.d.ts +2 -2
  77. package/lib/Footer/FooterLink.d.ts +2 -2
  78. package/lib/Footer/Sitemap.d.ts +2 -2
  79. package/lib/Footer/Sitemap.js +2 -1
  80. package/lib/Footer/Sitemap.js.map +1 -1
  81. package/lib/Footer/SocialMedia.d.ts +2 -2
  82. package/lib/Footer/SocialMedia.js +5 -5
  83. package/lib/Footer/SocialMedia.js.map +1 -1
  84. package/lib/Header/Header.d.ts +2 -2
  85. package/lib/Header/Header.js +3 -2
  86. package/lib/Header/Header.js.map +1 -1
  87. package/lib/Icons/DollarIcon.d.ts +1 -0
  88. package/lib/Icons/DollarIcon.js +3 -0
  89. package/lib/Icons/DollarIcon.js.map +1 -0
  90. package/lib/Icons/EuroIcon.d.ts +1 -0
  91. package/lib/Icons/EuroIcon.js +3 -0
  92. package/lib/Icons/EuroIcon.js.map +1 -0
  93. package/lib/Icons/index.d.ts +17 -16
  94. package/lib/Icons/index.js +17 -16
  95. package/lib/Icons/index.js.map +1 -1
  96. package/lib/Img.js +1 -1
  97. package/lib/Img.js.map +1 -1
  98. package/lib/LikeControl.d.ts +2 -2
  99. package/lib/LikeControl.js.map +1 -1
  100. package/lib/MobileAppTile.d.ts +8 -0
  101. package/lib/MobileAppTile.js +7 -0
  102. package/lib/MobileAppTile.js.map +1 -0
  103. package/lib/Placeholder.d.ts +3 -1
  104. package/lib/Placeholder.js +9 -2
  105. package/lib/Placeholder.js.map +1 -1
  106. package/lib/ProductBlock.d.ts +2 -2
  107. package/lib/ProductBlockInner.d.ts +2 -2
  108. package/lib/ProductGallery.d.ts +2 -2
  109. package/lib/ProductGallery.js +2 -2
  110. package/lib/ProductGallery.js.map +1 -1
  111. package/lib/ProjectSettings.d.ts +18 -0
  112. package/lib/ProjectSettings.js +28 -0
  113. package/lib/ProjectSettings.js.map +1 -0
  114. package/lib/StepsBlock.d.ts +2 -2
  115. package/lib/Tile.d.ts +2 -2
  116. package/lib/Tile.js +15 -7
  117. package/lib/Tile.js.map +1 -1
  118. package/lib/common.css +1 -1
  119. package/lib/index.d.ts +5 -3
  120. package/lib/index.js +3 -2
  121. package/lib/index.js.map +1 -1
  122. package/lib/types.d.ts +1 -1
  123. package/lib/ui-kit/SearchBar.d.ts +2 -2
  124. package/lib/ui-kit/SearchBar.js.map +1 -1
  125. package/lib/useSitemap.d.ts +3 -0
  126. package/lib/useSitemap.js +13 -0
  127. package/lib/useSitemap.js.map +1 -0
  128. package/package.json +3 -2
  129. package/src/BenefitsBlock.fixture.tsx +34 -0
  130. package/src/BenefitsBlock.tsx +45 -0
  131. package/src/Blocks.ts +23 -0
  132. package/src/ContentPage.fixture.tsx +38 -0
  133. package/src/ContentPage.page.json +540 -0
  134. package/src/ContentPage.tsx +83 -0
  135. package/src/ContentPageContext.tsx +34 -0
  136. package/src/ContentPageHead.tsx +26 -0
  137. package/src/ExchangeRateTile.fixture.tsx +11 -0
  138. package/src/ExchangeRateTile.tsx +80 -0
  139. package/src/Footer/Contacts.tsx +71 -0
  140. package/src/Footer/Footer.fixture.tsx +265 -0
  141. package/src/Footer/Footer.tsx +46 -0
  142. package/src/Footer/FooterLink.ts +6 -0
  143. package/src/Footer/HorizontalNavigation.tsx +46 -0
  144. package/src/Footer/Sitemap.tsx +55 -0
  145. package/src/Footer/SocialMedia.tsx +46 -0
  146. package/src/Footer/TextInformation.tsx +50 -0
  147. package/src/Footer/index.ts +2 -0
  148. package/src/Header/Header.fixture.tsx +21 -0
  149. package/src/Header/Header.tsx +55 -0
  150. package/src/Header/HeaderContent.ts +6 -0
  151. package/src/Header/HeaderSecondaryMenu.tsx +42 -0
  152. package/src/Header/HeaderSecondaryMenuButton.tsx +21 -0
  153. package/src/Header/index.ts +3 -0
  154. package/src/Header/isSubItemActive.ts +9 -0
  155. package/src/Header/isTopItemActive.ts +13 -0
  156. package/src/Icons/ActualBalanceIcon.tsx +202 -0
  157. package/src/Icons/CalendarIcon.tsx +19 -0
  158. package/src/Icons/ClockIcon.tsx +212 -0
  159. package/src/Icons/ComfortableCompIcon.tsx +379 -0
  160. package/src/Icons/DollarIcon.tsx +12 -0
  161. package/src/Icons/EmptyWalletIcon.tsx +25 -0
  162. package/src/Icons/EuroIcon.tsx +12 -0
  163. package/src/Icons/GridIcon.tsx +9 -0
  164. package/src/Icons/LoupeIcon.tsx +11 -0
  165. package/src/Icons/NewDocIcon.tsx +262 -0
  166. package/src/Icons/OkIcon.tsx +8 -0
  167. package/src/Icons/PassSendIcon.tsx +250 -0
  168. package/src/Icons/PercentageSquareIcon.tsx +18 -0
  169. package/src/Icons/ProfileIcon.tsx +18 -0
  170. package/src/Icons/SignDocsIcon.tsx +295 -0
  171. package/src/Icons/TelegramIcon.tsx +8 -0
  172. package/src/Icons/VKIcon.tsx +10 -0
  173. package/src/Icons/index.ts +17 -0
  174. package/src/Img.tsx +22 -0
  175. package/src/LikeControl.fixture.tsx +5 -0
  176. package/src/LikeControl.tsx +23 -0
  177. package/src/MobileAppTile.fixture.tsx +15 -0
  178. package/src/MobileAppTile.tsx +34 -0
  179. package/src/Placeholder.fixture.tsx +5 -0
  180. package/src/Placeholder.tsx +33 -0
  181. package/src/ProductBlock.fixture.tsx +95 -0
  182. package/src/ProductBlock.tsx +15 -0
  183. package/src/ProductBlockInner.tsx +110 -0
  184. package/src/ProductGallery.fixture.tsx +138 -0
  185. package/src/ProductGallery.tsx +90 -0
  186. package/src/ProjectSettings.ts +38 -0
  187. package/src/StepsBlock.fixture.tsx +23 -0
  188. package/src/StepsBlock.tsx +51 -0
  189. package/src/Tile.fixture.tsx +124 -0
  190. package/src/Tile.tsx +89 -0
  191. package/src/api/LikeAPI.ts +141 -0
  192. package/src/common.css +39 -0
  193. package/src/content-page-repository/ContentPageRepository.ts +38 -0
  194. package/src/content-page-repository/TransformationOptions.ts +4 -0
  195. package/src/content-page-repository/index.ts +1 -0
  196. package/src/content-page-repository/mapJSON.spec.ts +28 -0
  197. package/src/content-page-repository/mapJSON.ts +23 -0
  198. package/src/content-page-repository/transformContentPage.ts +26 -0
  199. package/src/content-page-repository/transformMarkdown.ts +10 -0
  200. package/src/content-page-repository/transformPicture.ts +61 -0
  201. package/src/cosmos.decorator.tsx +19 -0
  202. package/src/index.ts +19 -0
  203. package/src/mergeTopItems.spec.ts +20 -0
  204. package/src/mergeTopItems.ts +13 -0
  205. package/src/setup-fixture.ts +49 -0
  206. package/src/types.ts +109 -0
  207. package/src/ui-kit/BlockItem.fixture.tsx +5 -0
  208. package/src/ui-kit/BlockItem.tsx +36 -0
  209. package/src/ui-kit/Breadcrumb.tsx +26 -0
  210. package/src/ui-kit/Button.fixture.tsx +113 -0
  211. package/src/ui-kit/Button.tsx +66 -0
  212. package/src/ui-kit/HeaderItem.fixture.tsx +8 -0
  213. package/src/ui-kit/HeaderItem.tsx +25 -0
  214. package/src/ui-kit/LikeButton.fixture.tsx +6 -0
  215. package/src/ui-kit/LikeButton.tsx +41 -0
  216. package/src/ui-kit/Logo.fixture.tsx +5 -0
  217. package/src/ui-kit/Logo.tsx +35 -0
  218. package/src/ui-kit/SVG.tsx +32 -0
  219. package/src/ui-kit/SearchBar.tsx +33 -0
  220. package/src/ui-kit/TopItem.fixture.tsx +8 -0
  221. package/src/ui-kit/TopItem.tsx +37 -0
  222. package/src/useLink.ts +28 -0
  223. package/src/useSitemap.ts +17 -0
  224. package/src/utils/url.ts +22 -0
@@ -0,0 +1,141 @@
1
+ interface UserData {
2
+ token: string;
3
+ name: string;
4
+ }
5
+ export interface LikeData {
6
+ node_id: string;
7
+ value: number;
8
+ }
9
+
10
+ const AUTH_PREFIX = 'guest';
11
+ const STORAGE_KEY = 'likesToken';
12
+
13
+ export function LikeAPI(projectId: string, baseURL: string) {
14
+ async function getLikeCount(...nodeIds: string[]): Promise<LikeData[]> {
15
+ if (!nodeIds.length) {
16
+ return [];
17
+ }
18
+
19
+ const { token: Authorization } = await auth();
20
+
21
+ try {
22
+ const response = await fetch(`/assist/v1/public/projects/${projectId}/nodes/likes`, {
23
+ method: 'POST',
24
+ mode: 'cors',
25
+ headers: { 'Content-Type': 'application/json', Authorization },
26
+ body: JSON.stringify({
27
+ node_ids: nodeIds,
28
+ }),
29
+ });
30
+
31
+ const { likes } = await response.json();
32
+
33
+ return likes || [];
34
+ } catch (e) {
35
+ console.error(e);
36
+ return [];
37
+ }
38
+ }
39
+
40
+ async function like(nodeId: string): Promise<number> {
41
+ if (!nodeId) {
42
+ return 0;
43
+ }
44
+
45
+ const { token: Authorization, name } = await auth();
46
+
47
+ try {
48
+ const response = await fetch(`${baseURL}/assist/v1/public/projects/${projectId}/nodes/like`, {
49
+ method: 'POST',
50
+ mode: 'cors',
51
+ headers: {
52
+ 'Content-Type': 'application/json',
53
+ Authorization,
54
+ UserID: name,
55
+ },
56
+ body: JSON.stringify({
57
+ node_id: nodeId,
58
+ user_name: name,
59
+ }),
60
+ });
61
+ const { likes } = await response.json();
62
+
63
+ return likes || 0;
64
+ } catch (e) {
65
+ console.error(e);
66
+ return 0;
67
+ }
68
+ }
69
+
70
+ async function dislike(nodeId: string): Promise<number> {
71
+ if (!nodeId) {
72
+ return 0;
73
+ }
74
+
75
+ const { token: Authorization, name } = await auth();
76
+
77
+ try {
78
+ const response = await fetch(
79
+ `${baseURL}/assist/v1/public/projects/${projectId}/nodes/unlike`,
80
+ {
81
+ method: 'POST',
82
+ mode: 'cors',
83
+ headers: {
84
+ 'Content-Type': 'application/json',
85
+ Authorization,
86
+ UserID: name,
87
+ },
88
+ body: JSON.stringify({
89
+ node_id: nodeId,
90
+ user_name: name,
91
+ }),
92
+ },
93
+ );
94
+ const { likes } = await response.json();
95
+
96
+ return likes || 0;
97
+ } catch (e) {
98
+ console.error(e);
99
+ return 0;
100
+ }
101
+ }
102
+
103
+ async function auth(): Promise<UserData> {
104
+ try {
105
+ const userData: UserData = JSON.parse(globalThis.localStorage.getItem(STORAGE_KEY) || '');
106
+ if (userData?.token) {
107
+ return userData;
108
+ }
109
+ } catch (e) {
110
+ // do nothing
111
+ }
112
+
113
+ try {
114
+ const response = await fetch(`${baseURL}/assist/v1/public/auth`, {
115
+ method: 'POST',
116
+ mode: 'cors',
117
+ headers: {
118
+ 'Content-Type': 'application/json',
119
+ },
120
+ body: JSON.stringify({
121
+ prefix: AUTH_PREFIX,
122
+ }),
123
+ });
124
+
125
+ const { token, name } = await response.json();
126
+
127
+ globalThis.localStorage.setItem(STORAGE_KEY, JSON.stringify({ token, name }));
128
+
129
+ return { token, name } as UserData;
130
+ } catch (e) {
131
+ console.error(e);
132
+ return {} as UserData;
133
+ }
134
+ }
135
+
136
+ return {
137
+ getLikeCount,
138
+ like,
139
+ dislike,
140
+ };
141
+ }
package/src/common.css ADDED
@@ -0,0 +1,39 @@
1
+ @import '../fonts/ubuntu/ubuntu.css';
2
+
3
+ /* Private clients | Green */
4
+ [data-theme='pc'] {
5
+ --color-primary-main: #42ab44;
6
+ --color-primary-hover: #248341;
7
+ --color-primary-active: #245f34;
8
+ --color-secondary-hover: #c5e2c6;
9
+ --color-secondary-active: #d8f2d9;
10
+ }
11
+ /* Business clients | Light-Green */
12
+ [data-theme='bc'] {
13
+ --color-primary-main: #83b83e;
14
+ --color-primary-hover: #5c921d;
15
+ --color-primary-active: #3c5e10;
16
+ --color-secondary-hover: #e8f4d9;
17
+ --color-secondary-active: #d8e3ca;
18
+ }
19
+ /* Ecosystem own | Yellow */
20
+ [data-theme='eo'] {
21
+ --color-primary-main: #ddb63d;
22
+ --color-primary-hover: #bb9727;
23
+ --color-primary-active: #7e630c;
24
+ --color-secondary-hover: #f1ecdd;
25
+ --color-secondary-active: #e5dec7;
26
+ }
27
+
28
+ @tailwind components;
29
+ @tailwind utilities;
30
+
31
+ @layer utilities {
32
+ .rotate-180 {
33
+ transform: rotate(180deg);
34
+ }
35
+
36
+ .-translate-x-full {
37
+ transform: translateX(-100%);
38
+ }
39
+ }
@@ -0,0 +1,38 @@
1
+ import glob from 'glob';
2
+ import util from 'util';
3
+ import type { ContentPageDef } from '../types';
4
+ import type { TransformationOptions } from './TransformationOptions';
5
+ import { transformContentPage } from './transformContentPage';
6
+
7
+ const find = util.promisify(glob);
8
+
9
+ export function ContentPageRepository({
10
+ contentDir = 'content',
11
+ publicDir = 'public',
12
+ }: Partial<TransformationOptions> = {}) {
13
+ async function getAllContentPages(): Promise<ContentPageDef[]> {
14
+ const pagePaths = await find(`${contentDir}/**/*.page.json`);
15
+ return await Promise.all(pagePaths.map(readPage));
16
+ }
17
+
18
+ async function getContentPageBySlug(slug: string): Promise<ContentPageDef | undefined> {
19
+ const pages = await getAllContentPages();
20
+ return pages.find((content) => content.slug === slug);
21
+ }
22
+
23
+ const cache: Record<string, ContentPageDef> = {};
24
+ async function readPage(filePath: string): Promise<ContentPageDef> {
25
+ if (!(filePath in cache)) {
26
+ cache[filePath] = await transformContentPage(filePath, {
27
+ contentDir,
28
+ publicDir,
29
+ });
30
+ }
31
+ return cache[filePath];
32
+ }
33
+
34
+ return {
35
+ getAllContentPages,
36
+ getContentPageBySlug,
37
+ };
38
+ }
@@ -0,0 +1,4 @@
1
+ export interface TransformationOptions {
2
+ contentDir: string;
3
+ publicDir: string;
4
+ }
@@ -0,0 +1 @@
1
+ export { ContentPageRepository } from './ContentPageRepository';
@@ -0,0 +1,28 @@
1
+ import { mapJSON } from './mapJSON';
2
+
3
+ describe('mapJSON', () => {
4
+ it('should map scalars', async () => {
5
+ const mapper = (_: any) => (typeof _ === 'string' ? '<MAPPED>' : _);
6
+ expect(await mapJSON('foo', mapper)).toBe('<MAPPED>');
7
+ });
8
+
9
+ it('should map arrays', async () => {
10
+ const mapper = (_: any) => (Array.isArray(_) ? _.map((item) => item.toUpperCase()) : _);
11
+ expect(await mapJSON(['foo', 'bar', 'baz'], mapper)).toEqual(['FOO', 'BAR', 'BAZ']);
12
+ });
13
+
14
+ it('should map objects', async () => {
15
+ const mapper = (_: any) => ('bar' in _ ? { ..._, bar: '<MAPPED>' } : _);
16
+ expect(await mapJSON({ foo: { bar: 'baz' } }, mapper)).toEqual({ foo: { bar: '<MAPPED>' } });
17
+ });
18
+
19
+ it('should support async mappers', async () => {
20
+ const mapper = (_: any) => ('bar' in _ ? Promise.resolve({ ..._, bar: '<MAPPED>' }) : _);
21
+ expect(await mapJSON({ foo: { bar: 'baz' } }, mapper)).toEqual({ foo: { bar: '<MAPPED>' } });
22
+ });
23
+
24
+ it('should map nil values as is', async () => {
25
+ const mapper = (_: any) => (!_ ? '<EMPTY>' : _);
26
+ expect(await mapJSON([null, undefined, ''], mapper)).toEqual([null, undefined, '<EMPTY>']);
27
+ });
28
+ });
@@ -0,0 +1,23 @@
1
+ export async function mapJSON<T>(
2
+ data: T,
3
+ mapper: <In, Out = In>(value: In, key: string[]) => Promise<Out>,
4
+ key: string[] = [],
5
+ ): Promise<T> {
6
+ if (data === null || data === undefined) return data;
7
+
8
+ const mappedData = await mapper(data, key);
9
+ if (mappedData !== data) return mappedData as T;
10
+
11
+ if (Array.isArray(data)) {
12
+ const values = data.map((_, i) => mapJSON(_, mapper, [String(i)].concat(key)));
13
+ return Promise.all(values) as any;
14
+ } else if (typeof data === 'object' && (data as any).toString() === '[object Object]') {
15
+ const subKeys = Object.keys(data as any);
16
+ const values = subKeys.map((k) => mapJSON(data[k], mapper, [k].concat(key)));
17
+ const pairs = (await Promise.all(values)).map((_, i) => ({
18
+ [subKeys[i]]: _,
19
+ }));
20
+ return Object.assign({}, ...pairs);
21
+ }
22
+ return mapper<T>(data, key);
23
+ }
@@ -0,0 +1,26 @@
1
+ import fs from 'fs';
2
+ import util from 'util';
3
+ import type { ContentPageDef, Picture } from '../types';
4
+ import { mapJSON } from './mapJSON';
5
+ import type { TransformationOptions } from './TransformationOptions';
6
+ import { transformMarkdown } from './transformMarkdown';
7
+ import { transformPicture } from './transformPicture';
8
+
9
+ const readFile = util.promisify(fs.readFile);
10
+
11
+ export async function transformContentPage(
12
+ pagePath: string,
13
+ options: TransformationOptions,
14
+ ): Promise<ContentPageDef> {
15
+ const content = await readFile(pagePath, 'utf8');
16
+ return mapJSON<ContentPageDef>(JSON.parse(content), async (value: any, [key]) => {
17
+ const isMarkdown = typeof value === 'string' && key?.endsWith('__md');
18
+ const isPicture = value && value.src && typeof value.src === 'string';
19
+ if (isMarkdown) {
20
+ return await transformMarkdown(value);
21
+ } else if (isPicture) {
22
+ return await transformPicture(pagePath, value as Picture, options);
23
+ }
24
+ return value;
25
+ });
26
+ }
@@ -0,0 +1,10 @@
1
+ import { unified } from 'unified';
2
+ import remarkParse from 'remark-parse';
3
+ import remarkHtml from 'remark-html';
4
+
5
+ export async function transformMarkdown(markdown: string | null | undefined): Promise<string> {
6
+ if (!markdown) return '';
7
+
8
+ const result = await unified().use(remarkParse).use(remarkHtml).process(markdown);
9
+ return result.toString('utf-8');
10
+ }
@@ -0,0 +1,61 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import sharp from 'sharp';
4
+ import util from 'util';
5
+ import type { ImgSource, Picture } from '../types';
6
+ import type { TransformationOptions } from './TransformationOptions';
7
+
8
+ const mkdir = util.promisify(fs.mkdir);
9
+
10
+ export async function transformPicture(
11
+ pagePath: string,
12
+ picture: Picture,
13
+ options: TransformationOptions,
14
+ ): Promise<Picture> {
15
+ const sources = picture.sources || [];
16
+ const [transformedPicture, ...transformedSources] = await Promise.all(
17
+ [picture].concat(sources).map((_) => transformImg(pagePath, picture.src, { ...options, ..._ })),
18
+ );
19
+ return {
20
+ ...picture,
21
+ src: transformedPicture,
22
+ sources: transformedSources.map((_, i) => ({
23
+ ...sources[i],
24
+ src: _,
25
+ })),
26
+ };
27
+ }
28
+
29
+ async function transformImg(
30
+ pagePath: string,
31
+ src: string,
32
+ transformationOptions: TransformationOptions & ImgSource,
33
+ ): Promise<string> {
34
+ const { contentDir, publicDir, format, options, size } = transformationOptions;
35
+
36
+ const imgPath = `${path.dirname(pagePath)}/${src}`;
37
+ let chain = sharp(imgPath);
38
+ if (size) chain = chain.resize(size);
39
+ if (format) chain = chain.toFormat(format, options);
40
+
41
+ const transformedSrc = transformSrc(src, transformationOptions);
42
+ const transformedImgPath = `${publicDir}/${path.relative(
43
+ contentDir,
44
+ `${path.dirname(pagePath)}/${transformedSrc}`,
45
+ )}`;
46
+
47
+ await mkdir(path.dirname(transformedImgPath), { recursive: true });
48
+ await chain.toFile(transformedImgPath);
49
+
50
+ return transformedSrc;
51
+ }
52
+
53
+ function transformSrc(src: string, { format, size }: TransformationOptions & ImgSource): string {
54
+ const noTransform = !format && !size;
55
+ if (noTransform) return src;
56
+
57
+ const suffix = [size?.width, size?.height].filter(Boolean).join('-');
58
+ return `${path.basename(src, path.extname(src))}${suffix ? `-${suffix}` : ''}.${
59
+ String(format) || path.extname(src)
60
+ }`;
61
+ }
@@ -0,0 +1,19 @@
1
+ export default ({ children }) => {
2
+ document.querySelector(':root')?.setAttribute('lang', 'ru'); // for lighthouse accessibility metrics
3
+
4
+ return (
5
+ <div
6
+ style={{
7
+ backgroundColor: 'rgba(51, 51, 51, 0.07)',
8
+ position: 'absolute',
9
+ left: 0,
10
+ right: 0,
11
+ bottom: 0,
12
+ top: 0,
13
+ }}
14
+ data-theme="pc"
15
+ >
16
+ {children}
17
+ </div>
18
+ );
19
+ };
package/src/index.ts ADDED
@@ -0,0 +1,19 @@
1
+ export type { ProjectSettings } from './ProjectSettings';
2
+ export { projectSettings } from './ProjectSettings';
3
+
4
+ export type { BlockDef, ContentPageMeta, ContentPageDef, Img, ImgSource, Picture } from './types';
5
+ export type {
6
+ ContentPageContext,
7
+ Router,
8
+ LikeService,
9
+ Search,
10
+ HandlerDecorator,
11
+ } from './ContentPageContext';
12
+
13
+ export { ContentPageHead } from './ContentPageHead';
14
+ export { ContentPage } from './ContentPage';
15
+
16
+ export { Blocks } from './Blocks';
17
+ export { LikeAPI } from './api/LikeAPI';
18
+
19
+ export * as url from './utils/url';
@@ -0,0 +1,20 @@
1
+ import { mergeTopItems } from './mergeTopItems';
2
+
3
+ describe('mergeTopItems', () => {
4
+ it('should return original items if no extra items provided', () => {
5
+ const items = [{ href: '/' }];
6
+ expect(mergeTopItems(items)).toEqual(items);
7
+ });
8
+
9
+ it('should append extra items to original ones', () => {
10
+ const items = [{ href: '/' }];
11
+ const extraItems = [{ href: '/foo' }, { href: '/bar' }];
12
+ expect(mergeTopItems(items, extraItems)).toEqual(items.concat(extraItems));
13
+ });
14
+
15
+ it('should replace original items with duplicating extra items (by href)', () => {
16
+ const items = [{ href: '/foo' }];
17
+ const extraItems = [{ href: '/foo', text: 'foo' }, { href: '/bar' }];
18
+ expect(mergeTopItems(items, extraItems)).toEqual(extraItems);
19
+ });
20
+ });
@@ -0,0 +1,13 @@
1
+ import type { TopMenuItem } from './types';
2
+
3
+ const cmp = (a?: TopMenuItem) => (b?: TopMenuItem) => a?.href === b?.href;
4
+
5
+ const substitute = (items?: TopMenuItem[], substitution?: TopMenuItem[]) =>
6
+ (items || []).map((_) => substitution?.find(cmp(_)) || _);
7
+
8
+ const subtract = (minuend?: TopMenuItem[], subtrahend?: TopMenuItem[]) =>
9
+ (minuend || []).filter((_) => !subtrahend?.find(cmp(_)));
10
+
11
+ export function mergeTopItems(left?: TopMenuItem[], right?: TopMenuItem[]): TopMenuItem[] {
12
+ return substitute(left, right).concat(subtract(right, left));
13
+ }
@@ -0,0 +1,49 @@
1
+ import { setup } from '@redneckz/uni-jsx';
2
+ import runtime from 'react/jsx-runtime';
3
+ import { useState, useEffect } from 'react';
4
+ import type { ContentPageContext } from './ContentPageContext';
5
+
6
+ const { jsx, jsxs } = runtime as any;
7
+ setup(jsx, jsxs);
8
+
9
+ const TEST_ORIGIN = 'http://localhost:5001';
10
+
11
+ export const context: ContentPageContext = {
12
+ useState,
13
+ useRouter: () => ({
14
+ href: `${TEST_ORIGIN}/credits`,
15
+ pathname: '/credits',
16
+ query: {},
17
+ push: (url: string) => {
18
+ console.log(url);
19
+ },
20
+ }),
21
+ useAsyncData: (key, fetcher) => {
22
+ const [data, setData] = useState();
23
+ useEffect(() => {
24
+ fetcher().then((_) => {
25
+ setData(_);
26
+ });
27
+ }, [key, fetcher]);
28
+ return { data };
29
+ },
30
+ useLikeService: () => ({
31
+ likeCount: 0,
32
+ like: () => {
33
+ console.log('like');
34
+ },
35
+ dislike: () => {
36
+ console.log('dislike');
37
+ },
38
+ }),
39
+ handlerDecorator:
40
+ (handler, targetContent): any =>
41
+ (ev) => {
42
+ ev.preventDefault();
43
+ console.log(ev.target, ev, targetContent);
44
+ },
45
+ useSearch: () => ({
46
+ term: 'кредит',
47
+ setTerm: (text: string) => console.log(text),
48
+ }),
49
+ };
package/src/types.ts ADDED
@@ -0,0 +1,109 @@
1
+ import type {
2
+ AvifOptions,
3
+ FormatEnum,
4
+ GifOptions,
5
+ HeifOptions,
6
+ JpegOptions,
7
+ PngOptions,
8
+ ResizeOptions,
9
+ TiffOptions,
10
+ WebpOptions,
11
+ } from 'sharp';
12
+ import type { BenefitsBlockContent } from './BenefitsBlock';
13
+ import type { ContentPageContext } from './ContentPageContext';
14
+ import type { HeaderContent } from './Header/index';
15
+ import type { PlaceholderContent } from './Placeholder';
16
+ import type { ProductBlockInnerContent } from './ProductBlockInner';
17
+ import type { ProductGalleryContent } from './ProductGallery';
18
+ import type { StepsBlockContent } from './StepsBlock';
19
+ import type { TileContent } from './Tile';
20
+
21
+ export type BlockVersion = 'primary' | 'secondary';
22
+
23
+ export type ColorPalette = 'pc' | 'bc' | 'eo';
24
+
25
+ export type BlockContent =
26
+ | HeaderContent
27
+ | PlaceholderContent
28
+ | ProductBlockInnerContent
29
+ | TileContent
30
+ | StepsBlockContent
31
+ | BenefitsBlockContent
32
+ | ProductGalleryContent;
33
+
34
+ export interface BlockDef {
35
+ type: string;
36
+ style?: string[];
37
+ version?: BlockVersion;
38
+ content?: BlockContent;
39
+ blocks?: BlockDef[];
40
+ }
41
+
42
+ export interface ContentPageMeta {
43
+ title: string;
44
+ slug: string;
45
+ description?: string;
46
+ keywords?: string[];
47
+ robots?: string[];
48
+ og?: Record<string, string>;
49
+ }
50
+
51
+ export interface ContentPageDef extends ContentPageMeta {
52
+ style?: string[];
53
+ blocks?: BlockDef[];
54
+ likeControl?: boolean;
55
+ colorPalette?: ColorPalette;
56
+ }
57
+
58
+ export interface Img {
59
+ src: string;
60
+ alt?: string;
61
+ title?: string;
62
+ }
63
+
64
+ export interface ImgSource {
65
+ src: string;
66
+ format?: keyof FormatEnum;
67
+ options?:
68
+ | JpegOptions
69
+ | PngOptions
70
+ | WebpOptions
71
+ | AvifOptions
72
+ | HeifOptions
73
+ | GifOptions
74
+ | TiffOptions;
75
+ size?: ResizeOptions;
76
+ }
77
+
78
+ export interface Picture extends Img, ImgSource {
79
+ sources?: ImgSource[];
80
+ }
81
+
82
+ export interface UniBlockProps {
83
+ className?: string;
84
+ context: ContentPageContext;
85
+ }
86
+
87
+ export interface ContactInfo {
88
+ text: string;
89
+ type: 'tel' | 'email';
90
+ description: string;
91
+ }
92
+
93
+ export interface LinkContent {
94
+ text?: string;
95
+ href?: string;
96
+ target?: '_blank' | '_self' | '_parent' | '_top' | string;
97
+ }
98
+
99
+ export type NavMenuItem = LinkContent;
100
+
101
+ export interface TopMenuItem extends NavMenuItem {
102
+ items?: NavMenuItem[];
103
+ }
104
+
105
+ export interface Benefit {
106
+ label: string;
107
+ description?: string;
108
+ icon?: string;
109
+ }
@@ -0,0 +1,5 @@
1
+ import '../setup-fixture';
2
+
3
+ import { BlockItem } from './BlockItem';
4
+
5
+ export default <BlockItem text="Совершайте любые личные покупки" />;
@@ -0,0 +1,36 @@
1
+ import { JSX } from '@redneckz/uni-jsx';
2
+
3
+ type BlockItemVersion = 'primary' | 'secondary';
4
+ export interface BlockItemContent {
5
+ text?: string;
6
+ }
7
+
8
+ export interface BlockItemProps extends BlockItemContent {
9
+ className?: string;
10
+ version?: BlockItemVersion;
11
+ }
12
+
13
+ const listStyleMap: Record<BlockItemVersion, string> = {
14
+ primary: 'bg-primary-main',
15
+ secondary: 'bg-white',
16
+ };
17
+
18
+ const textStyleMap: Record<BlockItemVersion, string> = {
19
+ primary: 'text-primary-text',
20
+ secondary: 'text-white',
21
+ };
22
+
23
+ export const BlockItem = JSX<BlockItemProps>(
24
+ ({ className, text, children, version = 'primary' }) => {
25
+ return (
26
+ <div className={`inline-block flex ${className || ''}`} role="listitem">
27
+ <div
28
+ className={`inline-block w-[8px] h-[8px] min-w-[8px] min-h-[8px] rounded-full mt-2 ${listStyleMap[version]}`}
29
+ ></div>
30
+ <span className={`text-base font-normal font-sans mx-3 ${textStyleMap[version]}`}>
31
+ {text || children}
32
+ </span>
33
+ </div>
34
+ );
35
+ },
36
+ );
@@ -0,0 +1,26 @@
1
+ import { JSX } from '@redneckz/uni-jsx';
2
+ import type { LinkContent } from '../types';
3
+
4
+ export interface BreadcrumbProps extends LinkContent {
5
+ className?: string;
6
+ onClick?: (ev: MouseEvent) => any;
7
+ }
8
+
9
+ export const Breadcrumb = JSX<BreadcrumbProps>(
10
+ ({ text, href, target, onClick, className, children }) => {
11
+ if (href) {
12
+ return (
13
+ <a
14
+ className={`no-underline ${className || ''}`}
15
+ href={href}
16
+ target={target}
17
+ onClick={onClick}
18
+ >
19
+ <span className={`${className || ''}`}>{text || children}</span>
20
+ </a>
21
+ );
22
+ } else {
23
+ return <span className={`${className || ''}`}>{text || children}</span>;
24
+ }
25
+ },
26
+ );