km-card-layout-component-miniprogram 0.1.7 → 0.1.8

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 (24) hide show
  1. package/miniprogram_dist/components/card-layout/index.js +25 -193
  2. package/miniprogram_dist/components/card-layout/index.wxss +3 -1
  3. package/miniprogram_dist/vendor/km-card-layout-core/bindings.js +74 -0
  4. package/miniprogram_dist/vendor/km-card-layout-core/data.js +38 -0
  5. package/miniprogram_dist/vendor/km-card-layout-core/helpers.js +72 -0
  6. package/miniprogram_dist/vendor/km-card-layout-core/index.js +15 -367
  7. package/miniprogram_dist/vendor/km-card-layout-core/layout.js +117 -0
  8. package/miniprogram_dist/vendor/km-card-layout-core/ops/changeBackground.js +135 -0
  9. package/miniprogram_dist/vendor/km-card-layout-core/render/builder.js +210 -0
  10. package/miniprogram_dist/vendor/km-card-layout-core/utils.js +23 -130
  11. package/package.json +1 -1
  12. package/script/sync-core.js +10 -2
  13. package/src/components/card-layout/index.ts +30 -287
  14. package/src/components/card-layout/index.wxss +3 -1
  15. package/src/vendor/km-card-layout-core/bindings.ts +84 -0
  16. package/src/vendor/km-card-layout-core/data.ts +38 -0
  17. package/src/vendor/km-card-layout-core/helpers.ts +76 -0
  18. package/src/vendor/km-card-layout-core/index.ts +21 -460
  19. package/src/vendor/km-card-layout-core/interface/render.ts +1 -0
  20. package/src/vendor/km-card-layout-core/layout.ts +129 -0
  21. package/src/vendor/km-card-layout-core/ops/changeBackground.ts +169 -0
  22. package/src/vendor/km-card-layout-core/render/builder.ts +288 -0
  23. package/src/vendor/km-card-layout-core/types.d.ts +97 -5
  24. package/src/vendor/km-card-layout-core/utils.ts +9 -141
@@ -1,17 +1,11 @@
1
1
  import type {
2
- CardElement,
3
2
  CardLayoutInput,
4
3
  CardLayoutSchema,
5
- IconElement,
6
- ImageElement,
7
- LayoutPanelElement,
8
- TextElement,
4
+ RenderNode,
9
5
  } from '../../vendor/km-card-layout-core/index';
10
6
  import {
11
- addUnit,
7
+ buildRenderResult,
12
8
  normalizeLayout,
13
- resolveBindingValue,
14
- styleObjectToString,
15
9
  } from '../../vendor/km-card-layout-core/index';
16
10
  import { ICON_CODE_MAP } from './icon-map';
17
11
 
@@ -19,35 +13,12 @@ type LayoutData = {
19
13
  [key: string]: any;
20
14
  };
21
15
 
22
- interface RenderIcon {
23
- name?: string;
24
- text?: string;
25
- size?: string;
26
- color?: string;
27
- gap?: string;
28
- align?: 'left' | 'right';
29
- wrapperStyle: string;
30
- }
31
-
32
- interface RenderElement {
33
- id: string;
34
- type: CardElement['type'];
35
- wrapperStyle: string;
36
- contentStyle: string;
37
- name?: string;
38
- text?: string;
39
- src?: string;
40
- mode?: string;
41
- icon?: RenderIcon;
42
- children?: RenderElement[];
43
- }
44
-
45
16
  interface RenderCard {
46
17
  id: string;
47
18
  cardStyle: string;
48
19
  backgroundImage?: string;
49
20
  backgroundStyle: string;
50
- nodes: RenderElement[];
21
+ nodes: RenderNode[];
51
22
  }
52
23
 
53
24
  const ensureArray = (input: CardLayoutInput | any): CardLayoutInput => {
@@ -60,106 +31,6 @@ const pickCardId = (layout: any, idx: number) => {
60
31
  return `card-${idx}`;
61
32
  };
62
33
 
63
- const withUnit = (value: any, unit: 'px' | 'rpx') => addUnit(value, unit);
64
-
65
- const buildCardStyle = (layout: CardLayoutSchema, unit: 'px' | 'rpx') =>
66
- styleObjectToString(
67
- {
68
- width: withUnit(layout.width, unit),
69
- height: withUnit(layout.height, unit),
70
- color: layout.fontColor,
71
- borderRadius:
72
- layout.borderRadius !== undefined
73
- ? withUnit(layout.borderRadius, unit)
74
- : undefined,
75
- padding:
76
- layout.padding !== undefined ? withUnit(layout.padding, unit) : undefined,
77
- position: 'relative',
78
- overflow: 'hidden',
79
- boxSizing: 'border-box',
80
- backgroundColor: 'transparent',
81
- },
82
- unit,
83
- );
84
-
85
- const buildBackgroundStyle = (layout: CardLayoutSchema, unit: 'px' | 'rpx') =>
86
- styleObjectToString(
87
- {
88
- zIndex: layout.backgroundZIndex,
89
- borderRadius:
90
- layout.borderRadius !== undefined
91
- ? withUnit(layout.borderRadius, unit)
92
- : undefined,
93
- width: '100%',
94
- height: '100%',
95
- position: 'absolute',
96
- left: 0,
97
- top: 0,
98
- },
99
- unit,
100
- );
101
-
102
- const buildWrapperStyle = (el: CardElement, unit: 'px' | 'rpx') => {
103
- if (!el.layout || el.layout.mode !== 'absolute') return '';
104
- return styleObjectToString(
105
- {
106
- position: 'absolute',
107
- left: withUnit(el.layout.x, unit),
108
- top: withUnit(el.layout.y, unit),
109
- width: withUnit(el.layout.width, unit),
110
- height: withUnit(el.layout.height, unit),
111
- zIndex: el.layout.zIndex,
112
- boxSizing: 'border-box',
113
- display: 'flex',
114
- alignItems: 'center',
115
- },
116
- unit,
117
- );
118
- };
119
-
120
- const buildPanelContentStyle = (
121
- el: LayoutPanelElement,
122
- unit: 'px' | 'rpx',
123
- ) =>
124
- styleObjectToString(
125
- {
126
- position: 'relative',
127
- width: '100%',
128
- height: '100%',
129
- display: 'block',
130
- boxSizing: 'border-box',
131
- ...(el.style || {}),
132
- },
133
- unit,
134
- );
135
-
136
- const buildTextContentStyle = (el: TextElement, unit: 'px' | 'rpx') => {
137
- const textAlign =
138
- (el.style?.textAlign as string | undefined) || el.align || undefined;
139
- const style: Record<string, any> = {
140
- ...el.style,
141
- whiteSpace: 'pre-wrap',
142
- wordBreak: 'break-word',
143
- lineHeight:
144
- el.style?.lineHeight !== undefined && el.style?.lineHeight !== null
145
- ? el.style.lineHeight
146
- : '1.2',
147
- display: 'inline-flex',
148
- alignItems: 'center',
149
- };
150
- if (textAlign) style.textAlign = textAlign;
151
- return styleObjectToString(style, unit);
152
- };
153
-
154
- const buildBaseContentStyle = (el: CardElement, unit: 'px' | 'rpx') =>
155
- styleObjectToString(
156
- {
157
- ...(el.style || {}),
158
- boxSizing: 'border-box',
159
- },
160
- unit,
161
- );
162
-
163
34
  const mapIconGlyph = (name?: string, fallback?: string) => {
164
35
  if (!name) return fallback;
165
36
  const glyph = ICON_CODE_MAP[name];
@@ -167,161 +38,38 @@ const mapIconGlyph = (name?: string, fallback?: string) => {
167
38
  return fallback || name;
168
39
  };
169
40
 
170
- const buildTextIcon = (
171
- el: TextElement,
172
- unit: 'px' | 'rpx',
173
- ): RenderIcon | undefined => {
174
- const icon = el.icon;
175
- if (!icon || icon.enable === false) return undefined;
176
-
177
- const style = icon.style || 'fill';
178
- const baseName = el.key || el.binding || el.id;
179
- let name: string | undefined;
180
- if (style === 'dot') name = 'round';
181
- else if (style === 'line') name = baseName ? `${baseName}-line` : undefined;
182
- else name = baseName || undefined;
183
- if (!name) return undefined;
184
-
185
- const size =
186
- icon.size !== undefined && icon.size !== null
187
- ? icon.size
188
- : (el.style?.fontSize as number | string | undefined);
189
- const gap = icon.gap !== undefined && icon.gap !== null ? icon.gap : 4;
190
- const color =
191
- icon.color ?? (el.style?.color as string | undefined) ?? undefined;
41
+ const mapRenderNode = (node: RenderNode): RenderNode => {
42
+ const icon = node.icon
43
+ ? {
44
+ ...node.icon,
45
+ text: mapIconGlyph(node.icon.name, node.icon.text),
46
+ }
47
+ : undefined;
48
+ const children = node.children?.map(mapRenderNode);
49
+ const mappedText =
50
+ node.type === 'icon' ? mapIconGlyph(node.name, node.text) : node.text;
192
51
 
193
- const text = mapIconGlyph(name, name);
194
52
  return {
195
- name,
196
- text,
197
- size: size !== undefined ? withUnit(size, unit) : undefined,
198
- gap: gap !== undefined ? withUnit(gap, unit) : undefined,
199
- wrapperStyle: styleObjectToString(
200
- {
201
- display: 'inline-flex',
202
- alignItems: 'center',
203
- height: el.style?.lineHeight || 'auto',
204
- },
205
- unit,
206
- ),
207
- color,
208
- align: icon.align || 'left',
53
+ ...node,
54
+ text: mappedText,
55
+ icon,
56
+ children,
209
57
  };
210
58
  };
211
59
 
212
- const buildRenderNode = (
213
- el: CardElement,
214
- data: LayoutData,
215
- unit: 'px' | 'rpx',
216
- ): RenderElement | null => {
217
- if (!el || el.visible === false) return null;
218
- const wrapperStyle = buildWrapperStyle(el, unit);
219
-
220
- if (el.type === 'layout-panel') {
221
- const panel = el as LayoutPanelElement;
222
- return {
223
- id: el.id,
224
- type: el.type,
225
- wrapperStyle,
226
- contentStyle: buildPanelContentStyle(panel, unit),
227
- children: buildRenderNodes(panel.children || [], data, unit),
228
- };
229
- }
230
-
231
- if (el.type === 'text') {
232
- const textValue =
233
- resolveBindingValue(el.binding, data) ?? el.defaultValue ?? '';
234
- return {
235
- id: el.id,
236
- type: el.type,
237
- wrapperStyle,
238
- contentStyle: buildTextContentStyle(el as TextElement, unit),
239
- text: `${textValue}`,
240
- icon: buildTextIcon(el as TextElement, unit),
241
- };
242
- }
243
-
244
- if (el.type === 'image') {
245
- const style: Record<string, any> = { ...(el.style || {}) };
246
- const borderWidth = Number(style.borderWidth);
247
- if (Number.isFinite(borderWidth) && borderWidth > 0) {
248
- if (!style.borderStyle) style.borderStyle = 'solid';
249
- if (!style.borderColor) style.borderColor = '#000000';
250
- }
251
-
252
- const src =
253
- resolveBindingValue(el.binding, data) ??
254
- (el as ImageElement).defaultUrl ??
255
- el.defaultValue ??
256
- '';
257
- const mode =
258
- (el as ImageElement).fit === 'contain' ? 'aspectFit' : 'aspectFill';
259
- return {
260
- id: el.id,
261
- type: el.type,
262
- wrapperStyle,
263
- contentStyle: styleObjectToString(style, unit),
264
- src,
265
- mode,
266
- };
267
- }
268
-
269
- if (el.type === 'icon') {
270
- const resolved =
271
- resolveBindingValue(el.binding, data) ??
272
- (el as IconElement).name ??
273
- el.defaultValue ??
274
- '';
275
- const text = mapIconGlyph(`${resolved}`, `${resolved}`);
276
- return {
277
- id: el.id,
278
- type: el.type,
279
- wrapperStyle,
280
- contentStyle: buildBaseContentStyle(el, unit),
281
- name: `${resolved}`,
282
- text,
283
- };
284
- }
285
-
286
- if (el.type === 'custom') {
287
- return {
288
- id: el.id,
289
- type: el.type,
290
- wrapperStyle,
291
- contentStyle: buildBaseContentStyle(el, unit),
292
- };
293
- }
294
-
295
- return null;
296
- };
297
-
298
- const buildRenderNodes = (
299
- children: CardElement[],
300
- data: LayoutData,
301
- unit: 'px' | 'rpx',
302
- ) => {
303
- if (!Array.isArray(children)) return [];
304
- const nodes: RenderElement[] = [];
305
- children.forEach(el => {
306
- if (!el || el.visible === false) return;
307
- const node = buildRenderNode(el, data, unit);
308
- if (node) nodes.push(node);
309
- });
310
- return nodes;
311
- };
312
-
313
- const buildCards = (
314
- layouts: CardLayoutInput,
315
- data: LayoutData,
316
- unit: 'px' | 'rpx',
317
- ) =>
318
- layouts.map(layout => ({
319
- id: layout.name || (layout as any).id || '',
320
- cardStyle: buildCardStyle(layout, unit),
321
- backgroundImage: layout.backgroundImage || '',
322
- backgroundStyle: buildBackgroundStyle(layout, unit),
323
- nodes: buildRenderNodes(layout.children || [], data, unit),
60
+ const mapRenderTree = (nodes: RenderNode[]) =>
61
+ Array.isArray(nodes) ? nodes.map(mapRenderNode) : [];
62
+
63
+ const buildCards = (layouts: CardLayoutSchema[], data: LayoutData) => {
64
+ const renders = buildRenderResult(layouts as CardLayoutInput, data, 'rpx');
65
+ return renders.map((render, idx) => ({
66
+ id: pickCardId(layouts[idx], idx),
67
+ cardStyle: render.cardStyle,
68
+ backgroundImage: render.backgroundImage || '',
69
+ backgroundStyle: render.backgroundStyle,
70
+ nodes: mapRenderTree(render.renderTree),
324
71
  }));
72
+ };
325
73
 
326
74
  Component({
327
75
  options: {
@@ -367,12 +115,7 @@ Component({
367
115
  }
368
116
 
369
117
  const normalizedLayouts = normalizeLayout(layoutInput);
370
- const cards = buildCards(normalizedLayouts, dataInput, 'rpx').map(
371
- (card, idx) => ({
372
- ...card,
373
- id: pickCardId(layoutInput[idx], idx),
374
- }),
375
- );
118
+ const cards = buildCards(normalizedLayouts, dataInput);
376
119
 
377
120
  this.setData({ cards });
378
121
  },
@@ -95,7 +95,9 @@
95
95
  word-break: break-word;
96
96
  vertical-align: middle;
97
97
  }
98
-
98
+ .km-node--text{
99
+ /* 溢出隐藏 */
100
+ }
99
101
  .km-node--text text {
100
102
  display: inline;
101
103
  }
@@ -0,0 +1,84 @@
1
+ import type { CardElement, CardLayoutSchema } from './interface';
2
+ import type {
3
+ TemplateBackground,
4
+ TemplateItem,
5
+ } from './interface/data/payload';
6
+
7
+ export function stripLayoutBindings(
8
+ layouts: CardLayoutSchema[] = []
9
+ ): CardLayoutSchema[] {
10
+ const targetLayouts = Array.isArray(layouts) ? layouts : [];
11
+ const stripElement = (el: CardElement): CardElement => {
12
+ const { binding: _b, defaultValue: _d, ...rest } = el as any;
13
+ if (el.type === 'layout-panel') {
14
+ return {
15
+ ...rest,
16
+ children: (el.children || []).map(stripElement),
17
+ } as CardElement;
18
+ }
19
+ return rest as CardElement;
20
+ };
21
+
22
+ return targetLayouts.map(layout => ({
23
+ ...layout,
24
+ children: (layout.children || []).map(stripElement),
25
+ }));
26
+ }
27
+
28
+ export function applyItemCollectBindings(
29
+ layouts: CardLayoutSchema[] = [],
30
+ items: TemplateItem[] = []
31
+ ): CardLayoutSchema[] {
32
+ const targetLayouts = Array.isArray(layouts) ? layouts : [];
33
+ const metaMap = new Map<string, TemplateItem>();
34
+ const metaList = Array.isArray(items) ? items : [];
35
+ metaList.forEach(item => {
36
+ if (item && item.id !== undefined && item.id !== null) {
37
+ metaMap.set(String(item.id), item);
38
+ }
39
+ });
40
+
41
+ const assignBinding = (el: CardElement): CardElement => {
42
+ const meta = metaMap.get(String(el.id));
43
+ const binding =
44
+ meta && meta.bind !== undefined && meta.bind !== null
45
+ ? meta.bind
46
+ : el.binding;
47
+ const defaultValue =
48
+ meta && meta.default !== undefined ? meta.default : el.defaultValue;
49
+ const key = meta && meta.key !== undefined ? meta.key : el.key;
50
+ const base: any = { ...el };
51
+ if (binding !== undefined) base.binding = binding;
52
+ else delete base.binding;
53
+ if (defaultValue !== undefined) base.defaultValue = defaultValue;
54
+ else delete base.defaultValue;
55
+ if (key !== undefined) base.key = key;
56
+ else delete base.key;
57
+
58
+ if (el.type === 'layout-panel') {
59
+ return {
60
+ ...base,
61
+ children: (el.children || []).map(assignBinding),
62
+ } as CardElement;
63
+ }
64
+ return base as CardElement;
65
+ };
66
+
67
+ return targetLayouts.map(layout => ({
68
+ ...layout,
69
+ children: (layout.children || []).map(assignBinding),
70
+ }));
71
+ }
72
+
73
+ export function getTemplateItems(ids: string, items: TemplateItem[]) {
74
+ const idArray = ids.split(',').map(id => id.trim());
75
+ return items.filter(item => idArray.includes(String(item.id)));
76
+ }
77
+
78
+ export function getTemplateBackgrounds(
79
+ ids: string,
80
+ items: TemplateBackground[]
81
+ ) {
82
+ const idArray = ids.split(',').map(id => id.trim());
83
+ return items.filter(item => idArray.includes(String(item.id)));
84
+ }
@@ -0,0 +1,38 @@
1
+ import { isObject } from './helpers';
2
+
3
+ const pathToSegments = (path: string): string[] =>
4
+ `${path || ''}`
5
+ .replace(/\[(\d+)\]/g, '.$1')
6
+ .split('.')
7
+ .map(p => p.trim())
8
+ .filter(Boolean);
9
+
10
+ const readByPath = (data: any, path: string): any => {
11
+ if (path === undefined || path === null || path === '') return data;
12
+ const segments = pathToSegments(path);
13
+ let cursor: any = data;
14
+ for (let i = 0; i < segments.length; i += 1) {
15
+ if (!isObject(cursor) && !Array.isArray(cursor)) return undefined;
16
+ const key = segments[i];
17
+ if (Array.isArray(cursor)) {
18
+ const idx = Number(key);
19
+ cursor = Number.isNaN(idx) ? undefined : cursor[idx];
20
+ } else {
21
+ cursor = (cursor as Record<string, any>)[key];
22
+ }
23
+ if (cursor === undefined || cursor === null) {
24
+ return cursor;
25
+ }
26
+ }
27
+ return cursor;
28
+ };
29
+
30
+ export const resolveBindingValue = (
31
+ binding: string | undefined,
32
+ rootData: Record<string, any>,
33
+ context?: Record<string, any>
34
+ ): any => {
35
+ if (!binding) return undefined;
36
+ const value = readByPath(rootData, binding);
37
+ return value === undefined ? undefined : value;
38
+ };
@@ -0,0 +1,76 @@
1
+ const DIMENSION_PROPS = new Set([
2
+ 'width',
3
+ 'height',
4
+ 'top',
5
+ 'right',
6
+ 'bottom',
7
+ 'left',
8
+ 'padding',
9
+ 'paddingTop',
10
+ 'paddingBottom',
11
+ 'paddingLeft',
12
+ 'paddingRight',
13
+ 'margin',
14
+ 'marginTop',
15
+ 'marginBottom',
16
+ 'marginLeft',
17
+ 'marginRight',
18
+ 'fontSize',
19
+ 'lineHeight',
20
+ 'borderRadius',
21
+ 'borderWidth',
22
+ 'letterSpacing',
23
+ 'gap',
24
+ 'rowGap',
25
+ 'columnGap',
26
+ ]);
27
+
28
+ export const toNumber = (value: unknown): number | undefined => {
29
+ const num = Number(value);
30
+ return Number.isFinite(num) ? num : undefined;
31
+ };
32
+
33
+ const toKebab = (key: string): string =>
34
+ key.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();
35
+
36
+ export const addUnit = (
37
+ value: string | number | undefined | null,
38
+ unit: 'px' | 'rpx'
39
+ ): string | undefined => {
40
+ if (value === undefined || value === null || value === '') return undefined;
41
+ if (typeof value === 'number') {
42
+ const ratio = unit === 'rpx' ? 2 : 1;
43
+ return `${value * ratio}${unit}`;
44
+ }
45
+ if (typeof value === 'string') {
46
+ const parsed = Number(value);
47
+ if (Number.isFinite(parsed)) {
48
+ const ratio = unit === 'rpx' ? 2 : 1;
49
+ return `${parsed * ratio}${unit}`;
50
+ }
51
+ }
52
+ return `${value}`;
53
+ };
54
+
55
+ export const styleObjectToString = (
56
+ style?: Record<string, any>,
57
+ unit: 'px' | 'rpx' = 'px'
58
+ ): string => {
59
+ if (!style) return '';
60
+ const pairs: string[] = [];
61
+ Object.keys(style).forEach(key => {
62
+ const value = style[key];
63
+ if (value === undefined || value === null || value === '') return;
64
+ const useUnit = DIMENSION_PROPS.has(key)
65
+ ? addUnit(value as any, unit)
66
+ : value;
67
+ if (useUnit === undefined || useUnit === null || useUnit === '') return;
68
+ pairs.push(`${toKebab(key)}:${useUnit}`);
69
+ });
70
+ return pairs.join(';');
71
+ };
72
+
73
+ export const isObject = (
74
+ val: unknown
75
+ ): val is Record<string, any> | any[] =>
76
+ Boolean(val) && typeof val === 'object';