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

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/example/pages/home/index.js +4 -250
  2. package/miniprogram_dist/components/card-layout/index.js +214 -29
  3. package/miniprogram_dist/components/card-layout/index.wxml +29 -5
  4. package/miniprogram_dist/components/card-layout/index.wxss +28 -6
  5. package/miniprogram_dist/vendor/km-card-layout-core/index.js +58 -181
  6. package/miniprogram_dist/vendor/km-card-layout-core/interface/data/payload.js +2 -0
  7. package/miniprogram_dist/vendor/km-card-layout-core/interface/elements.js +2 -0
  8. package/miniprogram_dist/vendor/km-card-layout-core/interface/index.js +20 -0
  9. package/miniprogram_dist/vendor/km-card-layout-core/interface/layout.js +2 -0
  10. package/miniprogram_dist/vendor/km-card-layout-core/interface/render.js +2 -0
  11. package/miniprogram_dist/vendor/km-card-layout-core/utils.js +132 -0
  12. package/package.json +1 -1
  13. package/script/sync-core.js +10 -2
  14. package/src/components/card-layout/index.ts +340 -56
  15. package/src/components/card-layout/index.wxml +29 -5
  16. package/src/components/card-layout/index.wxss +28 -6
  17. package/src/vendor/km-card-layout-core/index.ts +153 -461
  18. package/src/vendor/km-card-layout-core/interface/data/payload.ts +45 -0
  19. package/src/vendor/km-card-layout-core/interface/elements.ts +73 -0
  20. package/src/vendor/km-card-layout-core/interface/index.ts +4 -0
  21. package/src/vendor/km-card-layout-core/interface/layout.ts +19 -0
  22. package/src/vendor/km-card-layout-core/interface/render.ts +52 -0
  23. package/src/vendor/km-card-layout-core/types.d.ts +22 -154
  24. package/src/vendor/km-card-layout-core/utils.ts +141 -0
@@ -1,96 +1,380 @@
1
- import type { CardLayoutInput, RenderNode } from '../../vendor/km-card-layout-core/index'
2
- import { buildRenderResult } from '../../vendor/km-card-layout-core/index'
3
- import { ICON_CODE_MAP } from './icon-map'
1
+ import type {
2
+ CardElement,
3
+ CardLayoutInput,
4
+ CardLayoutSchema,
5
+ IconElement,
6
+ ImageElement,
7
+ LayoutPanelElement,
8
+ TextElement,
9
+ } from '../../vendor/km-card-layout-core/index';
10
+ import {
11
+ addUnit,
12
+ normalizeLayout,
13
+ resolveBindingValue,
14
+ styleObjectToString,
15
+ } from '../../vendor/km-card-layout-core/index';
16
+ import { ICON_CODE_MAP } from './icon-map';
4
17
 
5
18
  type LayoutData = {
6
- user?: Record<string, any>
7
- [key: string]: any
19
+ [key: string]: any;
20
+ };
21
+
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[];
8
43
  }
9
44
 
10
45
  interface RenderCard {
11
- id: string
12
- cardStyle: string
13
- backgroundImage: string
14
- backgroundStyle: string
15
- nodes: RenderNode[]
46
+ id: string;
47
+ cardStyle: string;
48
+ backgroundImage?: string;
49
+ backgroundStyle: string;
50
+ nodes: RenderElement[];
16
51
  }
17
52
 
18
53
  const ensureArray = (input: CardLayoutInput | any): CardLayoutInput => {
19
- if (!input) return []
20
- return Array.isArray(input) ? input : [input]
21
- }
54
+ if (!input) return [];
55
+ return Array.isArray(input) ? input : [input];
56
+ };
22
57
 
23
58
  const pickCardId = (layout: any, idx: number) => {
24
- if (layout && (layout.name || layout.id)) return layout.name || layout.id
25
- return `card-${idx}`
26
- }
59
+ if (layout && (layout.name || layout.id)) return layout.name || layout.id;
60
+ return `card-${idx}`;
61
+ };
62
+
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
+ const mapIconGlyph = (name?: string, fallback?: string) => {
164
+ if (!name) return fallback;
165
+ const glyph = ICON_CODE_MAP[name];
166
+ if (glyph) return String.fromCharCode(parseInt(glyph, 16));
167
+ return fallback || name;
168
+ };
169
+
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;
192
+
193
+ const text = mapIconGlyph(name, name);
194
+ 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',
209
+ };
210
+ };
211
+
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),
324
+ }));
27
325
 
28
326
  Component({
29
327
  options: {
30
- styleIsolation: 'apply-shared'
328
+ styleIsolation: 'apply-shared',
31
329
  },
32
330
  properties: {
33
331
  layout: {
34
332
  type: Array,
35
333
  optionalTypes: [Object],
36
- value: []
334
+ value: [],
37
335
  },
38
336
  data: {
39
337
  type: Object,
40
338
  value: {
41
- user: {}
42
- }
43
- }
339
+ user: {},
340
+ },
341
+ },
44
342
  },
45
343
  data: {
46
- cards: [] as RenderCard[]
344
+ cards: [] as RenderCard[],
47
345
  },
48
346
  observers: {
49
347
  layout() {
50
- this.rebuild()
348
+ this.rebuild();
51
349
  },
52
350
  data() {
53
- this.rebuild()
54
- }
351
+ this.rebuild();
352
+ },
55
353
  },
56
354
  lifetimes: {
57
355
  attached() {
58
- this.rebuild()
59
- }
356
+ this.rebuild();
357
+ },
60
358
  },
61
359
  methods: {
62
- normalizeIconGlyph(nodes: RenderNode[]): RenderNode[] {
63
- return nodes.map(node => {
64
- if (node.type === 'icon') {
65
- const glyph = ICON_CODE_MAP[node.name || node.text || '']
66
- const text = glyph ? String.fromCharCode(parseInt(glyph, 16)) : node.text
67
- return { ...node, text }
68
- }
69
- if (node.children && node.children.length) {
70
- return { ...node, children: this.normalizeIconGlyph(node.children) }
71
- }
72
- return node
73
- })
74
- },
75
360
  rebuild() {
76
- const layoutInput = ensureArray(this.data.layout as CardLayoutInput)
77
- const dataInput = (this.data.data || {}) as LayoutData
361
+ const layoutInput = ensureArray(this.data.layout as CardLayoutInput);
362
+ const dataInput = (this.data.data || {}) as LayoutData;
78
363
 
79
364
  if (!layoutInput.length) {
80
- this.setData({ cards: [] })
81
- return
365
+ this.setData({ cards: [] });
366
+ return;
82
367
  }
83
368
 
84
- const rendered = buildRenderResult(layoutInput, dataInput, 'rpx')
85
- const cards = rendered.map((card, idx) => ({
86
- id: pickCardId(layoutInput[idx], idx),
87
- cardStyle: card.cardStyle,
88
- backgroundImage: card.backgroundImage,
89
- backgroundStyle: card.backgroundStyle,
90
- nodes: this.normalizeIconGlyph(card.renderTree)
91
- }))
369
+ 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
+ );
92
376
 
93
- this.setData({ cards })
94
- }
95
- }
96
- })
377
+ this.setData({ cards });
378
+ },
379
+ },
380
+ });
@@ -1,6 +1,6 @@
1
- <view class="km-card-layout-list">
1
+ <view class="km-card-layout">
2
2
  <block wx:for="{{cards}}" wx:key="id">
3
- <view class="km-card-layout-item">
3
+ <view class="km-card-layout__item">
4
4
  <view class="km-card" style="{{item.cardStyle}}">
5
5
  <image
6
6
  wx:if="{{item.backgroundImage}}"
@@ -31,7 +31,7 @@
31
31
  <block wx:elif="{{node.type === 'icon'}}">
32
32
  <view class="km-node km-node--icon" style="{{node.wrapperStyle}}">
33
33
  <view
34
- wx:if="{{node.name === 'dot'}}"
34
+ wx:if="{{node.name === 'dot' || node.name === 'round'}}"
35
35
  class="km-node__icon-dot"
36
36
  style="{{node.contentStyle}}"
37
37
  />
@@ -39,7 +39,7 @@
39
39
  wx:else
40
40
  class="km-node__icon icon"
41
41
  style="{{node.contentStyle}}"
42
- >{{node.text}}</view>
42
+ >{{node.text || ''}}</view>
43
43
  </view>
44
44
  </block>
45
45
  <block wx:elif="{{node.type === 'layout-panel'}}">
@@ -60,7 +60,31 @@
60
60
  </block>
61
61
  <block wx:else>
62
62
  <view class="km-node km-node--text" style="{{node.wrapperStyle}}">
63
- <view style="{{node.contentStyle}}">{{node.text}}</view>
63
+ <view class="km-node__text" style="{{node.contentStyle}}">
64
+ <block wx:if="{{node.icon && node.icon.name}}">
65
+ <view class="km-node__text-content">
66
+ <block wx:if="{{node.icon.align === 'right'}}">
67
+ <text class="km-node__text-value">{{node.text || ''}}</text>
68
+ <view style="{{node.icon.wrapperStyle}}">
69
+ <text
70
+ class="km-node__text-icon icon"
71
+ style="font-size: {{node.icon.size || '16px'}}; color: {{node.icon.color || ''}}; margin-right: {{node.icon.gap || ''}};">{{node.icon.text || node.icon.name || ''}}</text>
72
+ </view>
73
+ </block>
74
+ <block wx:else>
75
+ <view style="{{node.icon.wrapperStyle}}">
76
+ <text
77
+ class="km-node__text-icon icon"
78
+ style="font-size: {{node.icon.size || '16px'}}; color: {{node.icon.color || ''}}; margin-right: {{node.icon.gap || ''}};">{{node.icon.text || node.icon.name || ''}}</text>
79
+ </view>
80
+ <text class="km-node__text-value">{{node.text || ''}}</text>
81
+ </block>
82
+ </view>
83
+ </block>
84
+ <block wx:else>
85
+ <text class="km-node__text-value">{{node.text || ''}}</text>
86
+ </block>
87
+ </view>
64
88
  </view>
65
89
  </block>
66
90
  </template>
@@ -1,11 +1,12 @@
1
1
 
2
2
 
3
- .km-card-layout-list {
3
+ .km-card-layout {
4
4
  display: flex;
5
5
  flex-direction: column;
6
+ gap: 16rpx;
6
7
  }
7
8
 
8
- .km-card-layout-item {
9
+ .km-card-layout__item {
9
10
  width: 100%;
10
11
  }
11
12
 
@@ -15,7 +16,7 @@
15
16
  box-sizing: border-box;
16
17
  color: inherit;
17
18
  font-family: 'PingFang SC', 'Microsoft Yahei', sans-serif;
18
- background: #0f1115;
19
+ background: transparent;
19
20
  }
20
21
 
21
22
  .km-card__bg {
@@ -32,11 +33,12 @@
32
33
  color: inherit;
33
34
  }
34
35
 
35
- .km-node--icon{
36
+ .km-node--icon {
36
37
  display: flex;
37
38
  align-items: center;
38
- justify-items: center;
39
+ justify-content: center;
39
40
  }
41
+
40
42
  .km-node__image {
41
43
  width: 100%;
42
44
  height: 100%;
@@ -73,9 +75,29 @@
73
75
  box-sizing: border-box;
74
76
  }
75
77
 
76
- .km-node--text text {
78
+ .km-node__text {
79
+ width: 100%;
80
+ height: 100%;
77
81
  display: block;
82
+ box-sizing: border-box;
83
+ text-align: inherit;
84
+ }
85
+
86
+ .km-node__text-content {
87
+ display: inline-block;
88
+ vertical-align: middle;
89
+ height: 100%;
90
+ }
91
+
92
+ .km-node__text-value {
93
+ display: inline-block;
94
+ white-space: pre-wrap;
78
95
  word-break: break-word;
96
+ vertical-align: middle;
97
+ }
98
+
99
+ .km-node--text text {
100
+ display: inline;
79
101
  }
80
102
 
81
103
  /* ICON */