jsbox-cview 1.6.8 → 1.6.9

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.
@@ -4,29 +4,52 @@ export interface DynamicItemSizeSectionMatrixSection {
4
4
  title: string;
5
5
  items: Record<string, unknown>[];
6
6
  }
7
- export interface DynamicItemSizeSectionMatrixProps extends Omit<UiTypes.MatrixProps, "data" | "itemSize" | "autoItemSize" | "estimatedItemSize" | "columns" | "square" | "waterfall" | "reorder" | "menu"> {
8
- data?: DynamicItemSizeSectionMatrixSection[];
7
+ export interface DynamicItemSizeSectionMatrixCustomSection {
8
+ title: Record<string, unknown>;
9
+ titleHeight: number;
10
+ items: Record<string, unknown>[];
11
+ }
12
+ export type DynamicItemSizeSectionMatrixAnySection = DynamicItemSizeSectionMatrixSection | DynamicItemSizeSectionMatrixCustomSection;
13
+ export type DynamicItemSizeSectionMatrixTitleTemplate = NonNullable<UiTypes.MatrixProps["template"]>;
14
+ interface BaseProps extends Omit<UiTypes.MatrixProps, "data" | "itemSize" | "autoItemSize" | "estimatedItemSize" | "columns" | "square" | "waterfall" | "reorder" | "menu"> {
9
15
  fixedItemHeight?: number;
10
16
  minItemWidth?: number;
11
17
  maxColumns?: number;
12
18
  spacing?: number;
13
19
  }
20
+ type TitleProps<T extends DynamicItemSizeSectionMatrixAnySection> = [T] extends [
21
+ DynamicItemSizeSectionMatrixCustomSection
22
+ ] ? {
23
+ sectionTitleTemplate: DynamicItemSizeSectionMatrixTitleTemplate;
24
+ } : [T] extends [DynamicItemSizeSectionMatrixSection] ? {
25
+ sectionTitleTemplate?: never;
26
+ } : never;
27
+ export type DynamicItemSizeSectionMatrixProps<T extends DynamicItemSizeSectionMatrixAnySection = DynamicItemSizeSectionMatrixSection> = BaseProps & TitleProps<T> & {
28
+ data?: T[];
29
+ };
14
30
  export interface DynamicItemSizeSectionMatrixEvents extends Omit<UiTypes.MatrixEvents, "itemSize" | "reorderBegan" | "reorderMoved" | "canMoveItem" | "reorderFinished"> {
15
31
  itemHeight?: (width: number) => number;
16
32
  }
17
33
  /**
18
34
  * # CView Dynamic ItemSize Section Matrix
19
35
  *
20
- * 此组件是为了在 Dynamic ItemSize Matrix 的基础上添加 SectionTitle
36
+ * 此组件是为了在 Dynamic ItemSize Matrix 的基础上添加 SectionTitle
37
+ * SectionTitle 实际上是一个位于每个 section 首位的全宽 header cell,为兼容现有API继续使用title命名。
21
38
  *
22
- * - 使用此组件必须在每个section中添加title,如果title为空字符串,依然添加高度35的空格(包含spacing)
23
- * - sectionTitle的字体为font(13),左右边距为16(不含spacing),即总宽度为 totalWidth - 2 * spacing - 32
24
- * - 由于它必然和底下的item会有spacing,所以不建议spacing设的太大,那样会很违和
25
- * - sectionTitle会使得section之间的间隔增加自身的高度
26
- * - matrix事件会自动调整indexPath,包括didSelect、didLongPress、forEachItem
27
- * - matrix的方法都在该组件中重新实现,自动调整indexPath
28
- * - 不支持matrix原有的重新排序、自动大小功能,为防止sectionTitle暴露,也不支持menu
29
- * - 不支持Dynamic ItemSize Matrix的dynamicHeightEnabledheightToWidth
39
+ * 注意事项:
40
+ * 1. 默认模式中每个section的title为字符串;空字符串依然添加高度35的空格(该高度包含spacing)
41
+ * 2. 默认title的字体为font(13),左右边距为10,即文本宽度为 totalWidth - 2 * spacing - 20
42
+ * 3. 提供sectionTitleTemplate后进入自定义模式,title为模板数据,titleHeight为标题cell的实际高度(不包含spacing)
43
+ * 4. sectionTitle会使得section之间的间隔增加自身的高度
44
+ * 5. 由于sectionTitle必然和底下的item会有spacing,所以不建议spacing设的太大,那样会很违和
45
+ * 6. 每个section会使用不可见item补齐最后一行,避免原生Flow Layout将未满行居中排列
46
+ * 7. matrix事件会自动过滤sectionTitle和不可见item,并调整indexPath,包括didSelectdidLongPress、forEachItem
47
+ * 8. matrix的方法都在该组件中重新实现,自动调整indexPath
48
+ *
49
+ * 不支持:
50
+ * 1. 不支持matrix原有的重新排序、自动大小功能
51
+ * 2. 为防止sectionTitle暴露,也不支持menu
52
+ * 3. 不支持Dynamic ItemSize Matrix的dynamicHeightEnabled、heightToWidth
30
53
  *
31
54
  * ## 动态调整 itemSize
32
55
  *
@@ -48,11 +71,15 @@ export interface DynamicItemSizeSectionMatrixEvents extends Omit<UiTypes.MatrixE
48
71
  *
49
72
  * 可以使用 matrix 的全部属性。
50
73
  *
51
- * data的类型调整为:
74
+ * 默认模式的data类型为:
52
75
  * { title: string; items: Record<string, unknown>[] }
53
76
  *
77
+ * 提供sectionTitleTemplate后,自定义模式的data类型为:
78
+ * { title: Record<string, unknown>; titleHeight: number; items: Record<string, unknown>[] }
79
+ *
54
80
  * 特殊属性:
55
81
  *
82
+ * - sectionTitleTemplate 自定义section title模板;是否提供该属性即为模式开关
56
83
  * - fixedItemHeight 固定 itemSize 高度
57
84
  * - minItemWidth 最小的 itemSize 宽度
58
85
  * - maxColumns 最大列数
@@ -60,7 +87,7 @@ export interface DynamicItemSizeSectionMatrixEvents extends Omit<UiTypes.MatrixE
60
87
  *
61
88
  * events:
62
89
  *
63
- * 可以使用 matrix itemSize 以外的全部事件
90
+ * 可以使用 matrix 事件,但不包括 itemSize 以及与重新排序相关的事件
64
91
  *
65
92
  * 其他特殊事件:
66
93
  *
@@ -77,11 +104,12 @@ export interface DynamicItemSizeSectionMatrixEvents extends Omit<UiTypes.MatrixE
77
104
  * - cell(indexPath: NSIndexPath): AllUIView;
78
105
  * - scrollTo(args: { indexPath: NSIndexPath; animated?: boolean }): void;
79
106
  */
80
- export declare class DynamicItemSizeSectionMatrix extends Base<UIView, UiTypes.ViewOptions> {
107
+ export declare class DynamicItemSizeSectionMatrix<TSection extends DynamicItemSizeSectionMatrixAnySection = DynamicItemSizeSectionMatrixSection> extends Base<UIView, UiTypes.ViewOptions> {
81
108
  _defineView: () => UiTypes.ViewOptions;
82
109
  private _props;
83
110
  private _data;
84
111
  private _events;
112
+ private _sectionTitleTemplate?;
85
113
  private _itemSizeWidth;
86
114
  private _itemSizeHeight;
87
115
  private _totalWidth;
@@ -92,14 +120,16 @@ export declare class DynamicItemSizeSectionMatrix extends Base<UIView, UiTypes.V
92
120
  private _spacing;
93
121
  matrix: Matrix;
94
122
  constructor({ props, layout, events, }: {
95
- props: DynamicItemSizeSectionMatrixProps;
123
+ props: DynamicItemSizeSectionMatrixProps<TSection>;
96
124
  layout: (make: MASConstraintMaker, view: UIView) => void;
97
125
  events: DynamicItemSizeSectionMatrixEvents;
98
126
  });
127
+ private _isOriginalItem;
128
+ private _getSectionTitleHeight;
99
129
  private _mapData;
100
130
  private _mapTemplate;
101
- get data(): DynamicItemSizeSectionMatrixSection[];
102
- set data(data: DynamicItemSizeSectionMatrixSection[]);
131
+ get data(): TSection[];
132
+ set data(data: TSection[]);
103
133
  reload(): void;
104
134
  object(indexPath: NSIndexPath): Record<string, unknown>;
105
135
  insert({ indexPath, value }: {
@@ -113,3 +143,4 @@ export declare class DynamicItemSizeSectionMatrix extends Base<UIView, UiTypes.V
113
143
  animated?: boolean;
114
144
  }): void;
115
145
  }
146
+ export {};
@@ -25,19 +25,27 @@ function _getTextHeight(text, width) {
25
25
  inset: 0,
26
26
  });
27
27
  }
28
+ const _defaultSectionTitleHorizontalInset = 10;
28
29
  /**
29
30
  * # CView Dynamic ItemSize Section Matrix
30
31
  *
31
- * 此组件是为了在 Dynamic ItemSize Matrix 的基础上添加 SectionTitle
32
+ * 此组件是为了在 Dynamic ItemSize Matrix 的基础上添加 SectionTitle
33
+ * SectionTitle 实际上是一个位于每个 section 首位的全宽 header cell,为兼容现有API继续使用title命名。
32
34
  *
33
- * - 使用此组件必须在每个section中添加title,如果title为空字符串,依然添加高度35的空格(包含spacing)
34
- * - sectionTitle的字体为font(13),左右边距为16(不含spacing),即总宽度为 totalWidth - 2 * spacing - 32
35
- * - 由于它必然和底下的item会有spacing,所以不建议spacing设的太大,那样会很违和
36
- * - sectionTitle会使得section之间的间隔增加自身的高度
37
- * - matrix事件会自动调整indexPath,包括didSelect、didLongPress、forEachItem
38
- * - matrix的方法都在该组件中重新实现,自动调整indexPath
39
- * - 不支持matrix原有的重新排序、自动大小功能,为防止sectionTitle暴露,也不支持menu
40
- * - 不支持Dynamic ItemSize Matrix的dynamicHeightEnabledheightToWidth
35
+ * 注意事项:
36
+ * 1. 默认模式中每个section的title为字符串;空字符串依然添加高度35的空格(该高度包含spacing)
37
+ * 2. 默认title的字体为font(13),左右边距为10,即文本宽度为 totalWidth - 2 * spacing - 20
38
+ * 3. 提供sectionTitleTemplate后进入自定义模式,title为模板数据,titleHeight为标题cell的实际高度(不包含spacing)
39
+ * 4. sectionTitle会使得section之间的间隔增加自身的高度
40
+ * 5. 由于sectionTitle必然和底下的item会有spacing,所以不建议spacing设的太大,那样会很违和
41
+ * 6. 每个section会使用不可见item补齐最后一行,避免原生Flow Layout将未满行居中排列
42
+ * 7. matrix事件会自动过滤sectionTitle和不可见item,并调整indexPath,包括didSelectdidLongPress、forEachItem
43
+ * 8. matrix的方法都在该组件中重新实现,自动调整indexPath
44
+ *
45
+ * 不支持:
46
+ * 1. 不支持matrix原有的重新排序、自动大小功能
47
+ * 2. 为防止sectionTitle暴露,也不支持menu
48
+ * 3. 不支持Dynamic ItemSize Matrix的dynamicHeightEnabled、heightToWidth
41
49
  *
42
50
  * ## 动态调整 itemSize
43
51
  *
@@ -59,11 +67,15 @@ function _getTextHeight(text, width) {
59
67
  *
60
68
  * 可以使用 matrix 的全部属性。
61
69
  *
62
- * data的类型调整为:
70
+ * 默认模式的data类型为:
63
71
  * { title: string; items: Record<string, unknown>[] }
64
72
  *
73
+ * 提供sectionTitleTemplate后,自定义模式的data类型为:
74
+ * { title: Record<string, unknown>; titleHeight: number; items: Record<string, unknown>[] }
75
+ *
65
76
  * 特殊属性:
66
77
  *
78
+ * - sectionTitleTemplate 自定义section title模板;是否提供该属性即为模式开关
67
79
  * - fixedItemHeight 固定 itemSize 高度
68
80
  * - minItemWidth 最小的 itemSize 宽度
69
81
  * - maxColumns 最大列数
@@ -71,7 +83,7 @@ function _getTextHeight(text, width) {
71
83
  *
72
84
  * events:
73
85
  *
74
- * 可以使用 matrix itemSize 以外的全部事件
86
+ * 可以使用 matrix 事件,但不包括 itemSize 以及与重新排序相关的事件
75
87
  *
76
88
  * 其他特殊事件:
77
89
  *
@@ -96,6 +108,7 @@ class DynamicItemSizeSectionMatrix extends base_1.Base {
96
108
  this._props = props;
97
109
  this._data = this._props.data ?? [];
98
110
  this._events = events;
111
+ this._sectionTitleTemplate = props.sectionTitleTemplate;
99
112
  this._itemSizeWidth = 0;
100
113
  this._itemSizeHeight = 0;
101
114
  this._fixedItemHeight = this._props.fixedItemHeight ?? 96;
@@ -103,9 +116,11 @@ class DynamicItemSizeSectionMatrix extends base_1.Base {
103
116
  this._maxColumns = this._props.maxColumns ?? 5;
104
117
  this._spacing = this._props.spacing ?? 6;
105
118
  const { itemHeight, didSelect, didLongPress, forEachItem, ...otherEvents } = this._events;
119
+ const { sectionTitleTemplate, ...matrixProps } = props;
106
120
  this.matrix = new single_views_1.Matrix({
107
121
  props: {
108
- ...props,
122
+ ...matrixProps,
123
+ spacing: this._spacing,
109
124
  data: this._mapData(this._data),
110
125
  template: this._mapTemplate(props.template),
111
126
  },
@@ -118,8 +133,7 @@ class DynamicItemSizeSectionMatrix extends base_1.Base {
118
133
  }
119
134
  if (indexPath.item === 0) {
120
135
  const width = Math.max(this._totalWidth - 2 * this._spacing, 32);
121
- const textHeight = _getTextHeight(this._data[indexPath.section].title, width - 32);
122
- const height = textHeight + 35 - this._spacing * (indexPath.section === 0 ? 1 : 2);
136
+ const height = this._getSectionTitleHeight(indexPath.section, width);
123
137
  return $size(width, height);
124
138
  }
125
139
  else {
@@ -128,32 +142,23 @@ class DynamicItemSizeSectionMatrix extends base_1.Base {
128
142
  },
129
143
  didSelect: didSelect
130
144
  ? (sender, indexPath) => {
131
- if (indexPath.item === 0) {
145
+ if (!this._isOriginalItem(indexPath))
132
146
  return;
133
- }
134
- else {
135
- didSelect(sender, $indexPath(indexPath.section, indexPath.item - 1), this._data[indexPath.section].items[indexPath.item - 1]);
136
- }
147
+ didSelect(sender, $indexPath(indexPath.section, indexPath.item - 1), this._data[indexPath.section].items[indexPath.item - 1]);
137
148
  }
138
149
  : undefined,
139
150
  didLongPress: didLongPress
140
151
  ? (sender, indexPath) => {
141
- if (indexPath.item === 0) {
152
+ if (!this._isOriginalItem(indexPath))
142
153
  return;
143
- }
144
- else {
145
- didLongPress(sender, $indexPath(indexPath.section, indexPath.item - 1), this._data[indexPath.section].items[indexPath.item - 1]);
146
- }
154
+ didLongPress(sender, $indexPath(indexPath.section, indexPath.item - 1), this._data[indexPath.section].items[indexPath.item - 1]);
147
155
  }
148
156
  : undefined,
149
157
  forEachItem: forEachItem
150
158
  ? (sender, indexPath) => {
151
- if (indexPath.item === 0) {
159
+ if (!this._isOriginalItem(indexPath))
152
160
  return;
153
- }
154
- else {
155
- forEachItem(sender, $indexPath(indexPath.section, indexPath.item - 1));
156
- }
161
+ forEachItem(sender, $indexPath(indexPath.section, indexPath.item - 1));
157
162
  }
158
163
  : undefined,
159
164
  },
@@ -174,36 +179,68 @@ class DynamicItemSizeSectionMatrix extends base_1.Base {
174
179
  return;
175
180
  this._totalWidth = sender.frame.width;
176
181
  const { columns, itemSizeWidth } = _getColumnsAndItemSizeWidth(this._totalWidth, this._minItemWidth, this._maxColumns, this._spacing);
182
+ const columnsChanged = columns !== this._columns;
177
183
  this._columns = columns;
178
184
  this._itemSizeWidth = itemSizeWidth;
179
185
  this._itemSizeHeight = this._events.itemHeight
180
186
  ? this._events.itemHeight(this._itemSizeWidth)
181
187
  : this._fixedItemHeight;
188
+ if (columnsChanged) {
189
+ this.matrix.view.data = this._mapData(this._data);
190
+ }
182
191
  this.matrix.view.reload();
183
192
  },
184
193
  },
185
194
  };
186
195
  };
187
196
  }
197
+ _isOriginalItem(indexPath) {
198
+ const itemCount = this._data[indexPath.section]?.items.length ?? 0;
199
+ return indexPath.item > 0 && indexPath.item <= itemCount;
200
+ }
201
+ _getSectionTitleHeight(section, width) {
202
+ const sectionData = this._data[section];
203
+ if (this._sectionTitleTemplate) {
204
+ return Math.max(sectionData.titleHeight, 1);
205
+ }
206
+ const textHeight = _getTextHeight(sectionData.title, width - _defaultSectionTitleHorizontalInset * 2);
207
+ return textHeight + 35 - this._spacing * (section === 0 ? 1 : 2);
208
+ }
188
209
  _mapData(data) {
189
210
  return data.map((n) => {
190
211
  const mappedItems = n.items.map((n) => {
191
212
  return {
192
213
  __section_title__: { hidden: true },
214
+ __placeholder__: { hidden: true },
193
215
  __original_template__: { hidden: false },
194
216
  ...n,
195
217
  };
196
218
  });
219
+ const placeholderCount = (this._columns - (n.items.length % this._columns)) % this._columns;
220
+ const placeholders = Array.from({ length: placeholderCount }, () => ({
221
+ __section_title__: { hidden: true },
222
+ __placeholder__: { hidden: false },
223
+ __original_template__: { hidden: true },
224
+ }));
225
+ const titleData = this._sectionTitleTemplate
226
+ ? {
227
+ ...n.title,
228
+ __section_title__: { hidden: false },
229
+ __placeholder__: { hidden: true },
230
+ __original_template__: { hidden: true },
231
+ }
232
+ : {
233
+ __section_title__: { hidden: false },
234
+ __placeholder__: { hidden: true },
235
+ __section_title_label__: {
236
+ hidden: false,
237
+ text: n.title,
238
+ },
239
+ __original_template__: { hidden: true },
240
+ };
197
241
  return {
198
242
  title: "",
199
- items: [
200
- {
201
- __section_title__: { hidden: false },
202
- __section_title_label__: { text: n.title },
203
- __original_template__: { hidden: true },
204
- },
205
- ...mappedItems,
206
- ],
243
+ items: [titleData, ...mappedItems, ...placeholders],
207
244
  };
208
245
  });
209
246
  }
@@ -218,6 +255,46 @@ class DynamicItemSizeSectionMatrix extends base_1.Base {
218
255
  id: "__section_title__",
219
256
  },
220
257
  layout: $layout.fill,
258
+ views: this._sectionTitleTemplate
259
+ ? [
260
+ {
261
+ type: "view",
262
+ props: this._sectionTitleTemplate.props ?? {},
263
+ layout: $layout.fill,
264
+ views: this._sectionTitleTemplate.views,
265
+ },
266
+ ]
267
+ : [
268
+ {
269
+ type: "label",
270
+ props: {
271
+ id: "__section_title_label__",
272
+ bgcolor: $color("clear"),
273
+ font: $font(13),
274
+ textColor: $color("secondaryText"),
275
+ lines: 0,
276
+ },
277
+ layout: (make, view) => {
278
+ make.left.right.inset(_defaultSectionTitleHorizontalInset);
279
+ make.bottom.inset(0);
280
+ },
281
+ },
282
+ {
283
+ // 在这里放一个透明且无效果的button,从而取消item自己的highlight效果
284
+ type: "button",
285
+ props: {
286
+ bgcolor: $color("clear"),
287
+ },
288
+ layout: $layout.fill,
289
+ },
290
+ ],
291
+ },
292
+ {
293
+ type: "view",
294
+ props: {
295
+ id: "__placeholder__",
296
+ },
297
+ layout: $layout.fill,
221
298
  views: [
222
299
  {
223
300
  // 在这里放一个透明且无效果的button,从而取消item自己的highlight效果
@@ -227,20 +304,6 @@ class DynamicItemSizeSectionMatrix extends base_1.Base {
227
304
  },
228
305
  layout: $layout.fill,
229
306
  },
230
- {
231
- type: "label",
232
- props: {
233
- id: "__section_title_label__",
234
- bgcolor: $color("clear"),
235
- font: $font(13),
236
- textColor: $color("secondaryText"),
237
- lines: 0,
238
- },
239
- layout: (make, view) => {
240
- make.left.right.inset(16);
241
- make.bottom.inset(0);
242
- },
243
- },
244
307
  ],
245
308
  },
246
309
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jsbox-cview",
3
- "version": "1.6.8",
3
+ "version": "1.6.9",
4
4
  "description": "为 JSBox 设计的微型框架",
5
5
  "repository": {
6
6
  "type": "git",