@yanqirenshi/table.wbs 0.0.17 → 0.0.19
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.
- package/.claude/launch.json +11 -0
- package/README.md +102 -0
- package/dist/Core.js +187 -29
- package/dist/Supporter.js +191 -0
- package/dist/WBSTable.js +55 -8
- package/dist/components/ChooserColumn.js +43 -1
- package/dist/components/Controller.js +101 -15
- package/dist/components/TBody.js +3 -1
- package/dist/components/TBodyTr.js +39 -10
- package/dist/components/THead.js +14 -2
- package/dist/index.js +8 -0
- package/example/.env +4 -0
- package/example/package-lock.json +377 -157
- package/example/package.json +6 -3
- package/example/yanqirenshi-table.wbs-0.0.18.tgz +0 -0
- package/package.json +1 -1
- package/tests/Core.filter.test.js +51 -0
- package/tests/Core.selection.test.js +49 -0
- package/tests/Core.width.test.js +70 -0
- package/tests/Supporter.test.js +115 -0
- package/.claude/settings.local.json +0 -17
package/README.md
CHANGED
|
@@ -101,3 +101,105 @@ export const EDGES = [
|
|
|
101
101
|
},
|
|
102
102
|
];
|
|
103
103
|
```
|
|
104
|
+
|
|
105
|
+
### (3) CSV Download
|
|
106
|
+
|
|
107
|
+
The download button in the controller offers three CSV export types out of the box: 階層 (hierarchy, indented by WBS level), フラット (flat), and データ (raw computed records, all fields).
|
|
108
|
+
|
|
109
|
+
Each column can define a `value(d)` function to control what plain text goes into the 階層/フラット CSV cell (`contents` is JSX and isn't used for CSV). Columns without `value` fall back to `d[column.key]`, then `d._id`.
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
{
|
|
113
|
+
label: 'Name',
|
|
114
|
+
required: true,
|
|
115
|
+
value: (d) => d.label,
|
|
116
|
+
contents: (c, d) => {
|
|
117
|
+
return <span>{d.label}</span>;
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
To keep the previous behavior of delegating the click to your own handler instead of the built-in dropdown, pass a `download` prop to `<WBSTable>`:
|
|
123
|
+
|
|
124
|
+
```
|
|
125
|
+
<WBSTable source={wbs_source}
|
|
126
|
+
columns={columns}
|
|
127
|
+
download={() => myOwnExport()}/>
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### (4) Supporter
|
|
131
|
+
|
|
132
|
+
`Supporter` bundles the cell renderers you would otherwise write by hand for every `contents`.
|
|
133
|
+
Node-type paths and the link component are app concerns, so they are passed in through the constructor.
|
|
134
|
+
|
|
135
|
+
```
|
|
136
|
+
import WBSTable, { Supporter } from '@yanqirenshi/table.wbs';
|
|
137
|
+
import { Link } from 'react-router-dom';
|
|
138
|
+
|
|
139
|
+
const supporter = new Supporter({
|
|
140
|
+
paths: { DB_TABLE: 'database/tables', SCREEN: 'screens' }, // keyed by _node_type (falls back to _class)
|
|
141
|
+
defaultPath: 'wbs', // used when the type is not in paths
|
|
142
|
+
link: (to, label, d) => <Link to={to}>{label}</Link>, // omit to render the plain _id
|
|
143
|
+
marks: { true: '○', false: '--' }, // used by cellFlag
|
|
144
|
+
empty: '', // shown for null / undefined values
|
|
145
|
+
indent: ' ', // used by margin / withMargin
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
const columns = [
|
|
149
|
+
{ label: 'ID', leveling: true, required: true, contents: supporter.cellID },
|
|
150
|
+
{ label: 'Name', required: true, contents: supporter.cellLabel },
|
|
151
|
+
{ label: 'Physical', contents: supporter.cellByType({ DB_TABLE: supporter.cellCoreValue('physical_name') }) },
|
|
152
|
+
{ label: 'Use', contents: supporter.cellFlag('use') },
|
|
153
|
+
{ label: 'Description', contents: supporter.cellDescription },
|
|
154
|
+
];
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
| method | returns |
|
|
158
|
+
| --- | --- |
|
|
159
|
+
| `cellID(c, d)` | `link(to, d._id, d)` when `link` is set, otherwise `d._id` |
|
|
160
|
+
| `cellIDNotLink(c, d)` | `d._id` |
|
|
161
|
+
| `cellLabel(c, d)` | `d.label` |
|
|
162
|
+
| `cellDescription(c, d)` | `d.description` |
|
|
163
|
+
| `cellValue(key)` | a `contents` that returns `d[key]` |
|
|
164
|
+
| `cellCoreValue(key)` | a `contents` that returns `d._core[key]` |
|
|
165
|
+
| `cellFlag(key, marks?)` | a `contents` that shows `marks.true` when `d[key]` (or `d._core[key]`) is `true`, else `marks.false` |
|
|
166
|
+
| `cellByType(table, fallback?)` | a `contents` that picks `table[_node_type]`, falling back to `cellLabel` |
|
|
167
|
+
| `withMargin(contents)` | wraps a `contents` with a `margin(d._level)` indent (for columns without `leveling`) |
|
|
168
|
+
| `margin(level)` / `path(d)` / `to(d)` | the building blocks above |
|
|
169
|
+
|
|
170
|
+
### (5) Column width (Min / Max)
|
|
171
|
+
|
|
172
|
+
Each column can have a minimum and maximum width in px. Set them in the column definition, or let users change them at runtime from the **Columns** panel (the Width cell has Min and Max inputs).
|
|
173
|
+
|
|
174
|
+
```
|
|
175
|
+
{
|
|
176
|
+
label: 'Description',
|
|
177
|
+
width: { min: 200, max: 400 }, // either side is optional
|
|
178
|
+
contents: (c, d) => d.description,
|
|
179
|
+
},
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
`max` is applied as the column's `width` on the `<th>` and as `max-width` on a wrapper `<div>` inside each cell, so the column stays at `max` and long text wraps. The table's leftover width goes to the columns without `max`; when every visible column has one, the table stops stretching to the container width. A `leveling` column cannot shrink below the room its indent cells and caret need.
|
|
183
|
+
|
|
184
|
+
### (6) Row selection
|
|
185
|
+
|
|
186
|
+
Pass `selection` to make rows clickable. Selected rows get a `wbs-selected` class and a `#F2F2B0` background (text color is left as is). Override the background with `style.selected`:
|
|
187
|
+
|
|
188
|
+
```
|
|
189
|
+
const style_table = { selected: { backgroundColor: '#ffe' } };
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
```
|
|
193
|
+
<WBSTable source={wbs_source}
|
|
194
|
+
columns={columns}
|
|
195
|
+
selection="multiple" // 'single' | 'multiple' (true means 'single')
|
|
196
|
+
onSelect={(records, ids, clicked) => ...}/> // called after every toggle
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
| prop | description |
|
|
200
|
+
| --- | --- |
|
|
201
|
+
| `selection` | `'single'` (one row at a time, click again to clear) or `'multiple'` (toggle each row). Omit or `false` to disable. |
|
|
202
|
+
| `onSelect(records, ids, clicked)` | Called after every click with the selected records (in table order), their `_id`s, and the record that was clicked. |
|
|
203
|
+
| `selected` | Optional array of `_id`s. When given, the table is *controlled*: it renders exactly these rows as selected and leaves updating them to your `onSelect`. |
|
|
204
|
+
|
|
205
|
+
Clicking the collapse caret on a WBS row does not change the selection.
|
package/dist/Core.js
CHANGED
|
@@ -9,6 +9,14 @@ var _deepmerge = _interopRequireDefault(require("deepmerge"));
|
|
|
9
9
|
|
|
10
10
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
|
|
11
11
|
|
|
12
|
+
function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
|
|
13
|
+
|
|
14
|
+
function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
|
15
|
+
|
|
16
|
+
function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter); }
|
|
17
|
+
|
|
18
|
+
function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }
|
|
19
|
+
|
|
12
20
|
function _createForOfIteratorHelper(o, allowArrayLike) { var it; if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = o[Symbol.iterator](); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; }
|
|
13
21
|
|
|
14
22
|
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
|
|
@@ -57,6 +65,10 @@ var Core = /*#__PURE__*/function () {
|
|
|
57
65
|
label: 'col-' + (i + 1),
|
|
58
66
|
required: false,
|
|
59
67
|
visible: true,
|
|
68
|
+
width: {
|
|
69
|
+
min: null,
|
|
70
|
+
max: null
|
|
71
|
+
},
|
|
60
72
|
contents: function contents(c, d) {
|
|
61
73
|
return d._id;
|
|
62
74
|
}
|
|
@@ -111,6 +123,90 @@ var Core = /*#__PURE__*/function () {
|
|
|
111
123
|
|
|
112
124
|
return tmp;
|
|
113
125
|
}
|
|
126
|
+
/* **************************************************************** *
|
|
127
|
+
* Width *
|
|
128
|
+
* **************************************************************** */
|
|
129
|
+
// key は 'min' か 'max'。v は px の数値。空文字・null・NaN・0 以下は未設定にする。
|
|
130
|
+
|
|
131
|
+
}, {
|
|
132
|
+
key: "changeColumnWidth",
|
|
133
|
+
value: function changeColumnWidth(number, key, v, columns) {
|
|
134
|
+
var n = v === null || v === undefined || v === '' ? null : Number(v);
|
|
135
|
+
var value = n === null || Number.isNaN(n) || n <= 0 ? null : n;
|
|
136
|
+
return columns.map(function (d) {
|
|
137
|
+
if (d.number !== number) return d;
|
|
138
|
+
return _objectSpread(_objectSpread({}, d), {}, {
|
|
139
|
+
width: _objectSpread(_objectSpread({}, d.width || {}), {}, _defineProperty({}, key, value))
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
} // th に当てるスタイルを返す。
|
|
143
|
+
// max を列の width として明示すると、テーブルの余り幅は幅指定のない列に配られ、
|
|
144
|
+
// この列は max より広がらなくなる。
|
|
145
|
+
|
|
146
|
+
}, {
|
|
147
|
+
key: "widthCellStyle",
|
|
148
|
+
value: function widthCellStyle(column) {
|
|
149
|
+
var w = column.width || {};
|
|
150
|
+
return w.max ? {
|
|
151
|
+
width: w.max
|
|
152
|
+
} : {};
|
|
153
|
+
} // 表示中の列がすべて max を持つと余り幅の行き先がないので、
|
|
154
|
+
// その場合はテーブルを横幅いっぱいに広げない。
|
|
155
|
+
|
|
156
|
+
}, {
|
|
157
|
+
key: "fitsFullWidth",
|
|
158
|
+
value: function fitsFullWidth(columns) {
|
|
159
|
+
return !columns.every(function (c) {
|
|
160
|
+
return c.width && c.width.max;
|
|
161
|
+
});
|
|
162
|
+
} // セルの中身を包む div に当てるスタイルを返す。
|
|
163
|
+
// table の td は max-width が効かないので、中の div で幅を抑える。
|
|
164
|
+
|
|
165
|
+
}, {
|
|
166
|
+
key: "widthStyle",
|
|
167
|
+
value: function widthStyle(column) {
|
|
168
|
+
var w = column.width || {};
|
|
169
|
+
var out = {};
|
|
170
|
+
if (w.min) out.minWidth = w.min;
|
|
171
|
+
|
|
172
|
+
if (w.max) {
|
|
173
|
+
out.maxWidth = w.max;
|
|
174
|
+
out.overflowWrap = 'break-word';
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return out;
|
|
178
|
+
}
|
|
179
|
+
/* **************************************************************** *
|
|
180
|
+
* selection *
|
|
181
|
+
* **************************************************************** */
|
|
182
|
+
// selected: { [_id]: true } 形式。mode は 'single' か 'multiple'。
|
|
183
|
+
|
|
184
|
+
}, {
|
|
185
|
+
key: "toggleSelected",
|
|
186
|
+
value: function toggleSelected(_id, selected, mode) {
|
|
187
|
+
if (mode === 'multiple') {
|
|
188
|
+
var tmp = _objectSpread({}, selected);
|
|
189
|
+
|
|
190
|
+
if (tmp[_id]) delete tmp[_id];else tmp[_id] = true;
|
|
191
|
+
return tmp;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return selected[_id] ? {} : _defineProperty({}, _id, true);
|
|
195
|
+
}
|
|
196
|
+
}, {
|
|
197
|
+
key: "selectedIds",
|
|
198
|
+
value: function selectedIds(selected) {
|
|
199
|
+
return Object.keys(selected).map(function (k) {
|
|
200
|
+
return k * 1;
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
}, {
|
|
204
|
+
key: "selectedRecords",
|
|
205
|
+
value: function selectedRecords(selected, records) {
|
|
206
|
+
return records.filter(function (d) {
|
|
207
|
+
return selected[d._id];
|
|
208
|
+
});
|
|
209
|
+
}
|
|
114
210
|
/* **************************************************************** *
|
|
115
211
|
* filter *
|
|
116
212
|
* **************************************************************** */
|
|
@@ -144,7 +240,9 @@ var Core = /*#__PURE__*/function () {
|
|
|
144
240
|
try {
|
|
145
241
|
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
146
242
|
var rec = _step.value;
|
|
147
|
-
if (rec._class === "WORKPACKAGE" && !this.filterWp(rec, filter_wp)) continue;
|
|
243
|
+
if (rec._class === "WORKPACKAGE" && !this.filterWp(rec, filter_wp)) continue; // 閉じた祖先の配下にいる行は、その行自身が閉じていても表示しない。
|
|
244
|
+
|
|
245
|
+
if (lev !== null && rec._level > lev) continue;
|
|
148
246
|
|
|
149
247
|
if (closed_wbs[rec._id]) {
|
|
150
248
|
lev = rec._level;
|
|
@@ -152,7 +250,6 @@ var Core = /*#__PURE__*/function () {
|
|
|
152
250
|
continue;
|
|
153
251
|
}
|
|
154
252
|
|
|
155
|
-
if (lev !== null && rec._level > lev) continue;
|
|
156
253
|
lev = null;
|
|
157
254
|
out.push(rec);
|
|
158
255
|
}
|
|
@@ -169,33 +266,94 @@ var Core = /*#__PURE__*/function () {
|
|
|
169
266
|
* **************************************************************** */
|
|
170
267
|
|
|
171
268
|
}, {
|
|
172
|
-
key: "
|
|
173
|
-
value: function
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
269
|
+
key: "escapeCSVField",
|
|
270
|
+
value: function escapeCSVField(v) {
|
|
271
|
+
var s = v === null || v === undefined ? '' : String(v);
|
|
272
|
+
if (/[",\r\n]/.test(s)) return '"' + s.replace(/"/g, '""') + '"';
|
|
273
|
+
return s;
|
|
274
|
+
}
|
|
275
|
+
}, {
|
|
276
|
+
key: "toCSVString",
|
|
277
|
+
value: function toCSVString(headers, rows) {
|
|
278
|
+
var _this2 = this;
|
|
279
|
+
|
|
280
|
+
return [headers].concat(_toConsumableArray(rows)).map(function (row) {
|
|
281
|
+
return row.map(function (v) {
|
|
282
|
+
return _this2.escapeCSVField(v);
|
|
283
|
+
}).join(',');
|
|
284
|
+
}).join('\r\n');
|
|
285
|
+
}
|
|
286
|
+
}, {
|
|
287
|
+
key: "columnValue",
|
|
288
|
+
value: function columnValue(column, record) {
|
|
289
|
+
if (typeof column.value === 'function') return column.value(record);
|
|
290
|
+
if (column.key !== undefined && record[column.key] !== undefined) return record[column.key];
|
|
291
|
+
return record._id;
|
|
292
|
+
}
|
|
293
|
+
}, {
|
|
294
|
+
key: "makeCSVFlat",
|
|
295
|
+
value: function makeCSVFlat(columns, records) {
|
|
296
|
+
var _this3 = this;
|
|
297
|
+
|
|
298
|
+
var headers = columns.map(function (c) {
|
|
299
|
+
return c.label;
|
|
300
|
+
});
|
|
301
|
+
var rows = records.map(function (r) {
|
|
302
|
+
return columns.map(function (c) {
|
|
303
|
+
return _this3.columnValue(c, r);
|
|
304
|
+
});
|
|
305
|
+
});
|
|
306
|
+
return this.toCSVString(headers, rows);
|
|
307
|
+
}
|
|
308
|
+
}, {
|
|
309
|
+
key: "makeCSVHierarchy",
|
|
310
|
+
value: function makeCSVHierarchy(columns, records) {
|
|
311
|
+
var _this4 = this;
|
|
312
|
+
|
|
313
|
+
var headers = columns.map(function (c) {
|
|
314
|
+
return c.label;
|
|
315
|
+
});
|
|
316
|
+
var rows = records.map(function (r) {
|
|
317
|
+
return columns.map(function (c) {
|
|
318
|
+
var v = _this4.columnValue(c, r);
|
|
319
|
+
|
|
320
|
+
return c.leveling ? _this4.margin(r._level) + v : v;
|
|
321
|
+
});
|
|
322
|
+
});
|
|
323
|
+
return this.toCSVString(headers, rows);
|
|
324
|
+
}
|
|
325
|
+
}, {
|
|
326
|
+
key: "jsonStringifySafe",
|
|
327
|
+
value: function jsonStringifySafe(v) {
|
|
328
|
+
var seen = new WeakSet();
|
|
329
|
+
return JSON.stringify(v, function (key, val) {
|
|
330
|
+
if (val !== null && _typeof(val) === 'object') {
|
|
331
|
+
if (seen.has(val)) return '[Circular]';
|
|
332
|
+
seen.add(val);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
return val;
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
}, {
|
|
339
|
+
key: "makeCSVData",
|
|
340
|
+
value: function makeCSVData(records) {
|
|
341
|
+
var _this5 = this;
|
|
342
|
+
|
|
343
|
+
var keys = records.reduce(function (set, r) {
|
|
344
|
+
Object.keys(r).forEach(function (k) {
|
|
345
|
+
return set.add(k);
|
|
346
|
+
});
|
|
347
|
+
return set;
|
|
348
|
+
}, new Set());
|
|
349
|
+
var headers = Array.from(keys);
|
|
350
|
+
var rows = records.map(function (r) {
|
|
351
|
+
return headers.map(function (k) {
|
|
352
|
+
var v = r[k];
|
|
353
|
+
return v !== null && _typeof(v) === 'object' ? _this5.jsonStringifySafe(v) : v;
|
|
354
|
+
});
|
|
355
|
+
});
|
|
356
|
+
return this.toCSVString(headers, rows);
|
|
199
357
|
}
|
|
200
358
|
/* **************************************************************** *
|
|
201
359
|
* utils *
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports["default"] = void 0;
|
|
7
|
+
|
|
8
|
+
var _react = _interopRequireDefault(require("react"));
|
|
9
|
+
|
|
10
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
|
|
11
|
+
|
|
12
|
+
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
|
13
|
+
|
|
14
|
+
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
|
|
15
|
+
|
|
16
|
+
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
|
|
17
|
+
|
|
18
|
+
/*
|
|
19
|
+
* Supporter
|
|
20
|
+
*
|
|
21
|
+
* 列定義の `contents` に渡す定型のセル描画関数をまとめたクラス。
|
|
22
|
+
* 利用側で毎回書いていたヘルパーをライブラリ側で提供する。
|
|
23
|
+
*
|
|
24
|
+
* ノード種別ごとのパスやリンクの描画方法はアプリ側の都合なので、
|
|
25
|
+
* コンストラクタのオプションで外から渡す。
|
|
26
|
+
*
|
|
27
|
+
* const supporter = new Supporter({
|
|
28
|
+
* paths: { DB_TABLE: 'database/tables', SCREEN: 'screens' },
|
|
29
|
+
* defaultPath: 'wbs',
|
|
30
|
+
* link: (to, label) => <Link to={to}>{label}</Link>,
|
|
31
|
+
* });
|
|
32
|
+
*
|
|
33
|
+
* const columns = [
|
|
34
|
+
* { label: 'ID', leveling: true, contents: supporter.cellID },
|
|
35
|
+
* { label: 'Name', contents: supporter.cellLabel },
|
|
36
|
+
* { label: 'Use', contents: supporter.cellFlag('use') },
|
|
37
|
+
* ];
|
|
38
|
+
*/
|
|
39
|
+
var Supporter = /*#__PURE__*/function () {
|
|
40
|
+
function Supporter(options) {
|
|
41
|
+
_classCallCheck(this, Supporter);
|
|
42
|
+
|
|
43
|
+
var opts = options || {};
|
|
44
|
+
this.indent = opts.indent !== undefined ? opts.indent : ' ';
|
|
45
|
+
this.paths = opts.paths || {};
|
|
46
|
+
this.defaultPath = opts.defaultPath !== undefined ? opts.defaultPath : 'wbs';
|
|
47
|
+
this.link = typeof opts.link === 'function' ? opts.link : null;
|
|
48
|
+
this.marks = Object.assign({
|
|
49
|
+
"true": '○',
|
|
50
|
+
"false": '--'
|
|
51
|
+
}, opts.marks || {});
|
|
52
|
+
this.empty = opts.empty !== undefined ? opts.empty : ''; // contents にそのまま渡せるように this を束縛する。
|
|
53
|
+
|
|
54
|
+
this.cellID = this.cellID.bind(this);
|
|
55
|
+
this.cellIDNotLink = this.cellIDNotLink.bind(this);
|
|
56
|
+
this.cellLabel = this.cellLabel.bind(this);
|
|
57
|
+
this.cellDescription = this.cellDescription.bind(this);
|
|
58
|
+
}
|
|
59
|
+
/* **************************************************************** *
|
|
60
|
+
* leveling *
|
|
61
|
+
* **************************************************************** */
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
_createClass(Supporter, [{
|
|
65
|
+
key: "margin",
|
|
66
|
+
value: function margin(level) {
|
|
67
|
+
var out = '';
|
|
68
|
+
|
|
69
|
+
for (var i = 0; i < level; i++) {
|
|
70
|
+
out += this.indent;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return out;
|
|
74
|
+
} // 任意の contents 関数を、階層レベル分インデントして返すように包む。
|
|
75
|
+
|
|
76
|
+
}, {
|
|
77
|
+
key: "withMargin",
|
|
78
|
+
value: function withMargin(contents) {
|
|
79
|
+
var _this = this;
|
|
80
|
+
|
|
81
|
+
return function (column, data, key) {
|
|
82
|
+
return /*#__PURE__*/_react["default"].createElement(_react["default"].Fragment, null, /*#__PURE__*/_react["default"].createElement("span", null, _this.margin(data._level || 0)), /*#__PURE__*/_react["default"].createElement("span", null, contents(column, data, key)));
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
/* **************************************************************** *
|
|
86
|
+
* link *
|
|
87
|
+
* **************************************************************** */
|
|
88
|
+
|
|
89
|
+
}, {
|
|
90
|
+
key: "nodeType",
|
|
91
|
+
value: function nodeType(data) {
|
|
92
|
+
return data._node_type !== undefined ? data._node_type : data._class;
|
|
93
|
+
}
|
|
94
|
+
}, {
|
|
95
|
+
key: "path",
|
|
96
|
+
value: function path(data) {
|
|
97
|
+
var p = this.paths[this.nodeType(data)];
|
|
98
|
+
return p !== undefined ? p : this.defaultPath;
|
|
99
|
+
}
|
|
100
|
+
}, {
|
|
101
|
+
key: "to",
|
|
102
|
+
value: function to(data) {
|
|
103
|
+
return "/".concat(this.path(data), "/").concat(data._id);
|
|
104
|
+
}
|
|
105
|
+
/* **************************************************************** *
|
|
106
|
+
* cells *
|
|
107
|
+
* **************************************************************** */
|
|
108
|
+
|
|
109
|
+
}, {
|
|
110
|
+
key: "cellID",
|
|
111
|
+
value: function cellID(column, data) {
|
|
112
|
+
if (!this.link) return this.cellIDNotLink(column, data);
|
|
113
|
+
return this.link(this.to(data), data._id, data);
|
|
114
|
+
}
|
|
115
|
+
}, {
|
|
116
|
+
key: "cellIDNotLink",
|
|
117
|
+
value: function cellIDNotLink(column, data) {
|
|
118
|
+
return data._id;
|
|
119
|
+
}
|
|
120
|
+
}, {
|
|
121
|
+
key: "cellLabel",
|
|
122
|
+
value: function cellLabel(column, data) {
|
|
123
|
+
return this.valueOrEmpty(data.label);
|
|
124
|
+
}
|
|
125
|
+
}, {
|
|
126
|
+
key: "cellDescription",
|
|
127
|
+
value: function cellDescription(column, data) {
|
|
128
|
+
return this.valueOrEmpty(data.description);
|
|
129
|
+
}
|
|
130
|
+
/* **************************************************************** *
|
|
131
|
+
* cell factories *
|
|
132
|
+
* **************************************************************** */
|
|
133
|
+
// data[key] をそのまま返す contents 関数を作る。
|
|
134
|
+
|
|
135
|
+
}, {
|
|
136
|
+
key: "cellValue",
|
|
137
|
+
value: function cellValue(key) {
|
|
138
|
+
var _this2 = this;
|
|
139
|
+
|
|
140
|
+
return function (column, data) {
|
|
141
|
+
return _this2.valueOrEmpty(data[key]);
|
|
142
|
+
};
|
|
143
|
+
} // data._core[key] をそのまま返す contents 関数を作る。
|
|
144
|
+
|
|
145
|
+
}, {
|
|
146
|
+
key: "cellCoreValue",
|
|
147
|
+
value: function cellCoreValue(key) {
|
|
148
|
+
var _this3 = this;
|
|
149
|
+
|
|
150
|
+
return function (column, data) {
|
|
151
|
+
return _this3.valueOrEmpty(data._core ? data._core[key] : undefined);
|
|
152
|
+
};
|
|
153
|
+
} // 真偽値を marks(既定は ○ と --)で表示する contents 関数を作る。
|
|
154
|
+
|
|
155
|
+
}, {
|
|
156
|
+
key: "cellFlag",
|
|
157
|
+
value: function cellFlag(key, marks) {
|
|
158
|
+
var m = Object.assign({}, this.marks, marks || {});
|
|
159
|
+
return function (column, data) {
|
|
160
|
+
var v = data[key] !== undefined ? data[key] : data._core ? data._core[key] : undefined;
|
|
161
|
+
return v === true ? m["true"] : m["false"];
|
|
162
|
+
};
|
|
163
|
+
} // ノード種別ごとに contents 関数を切り替える。該当がなければ fallback を使う。
|
|
164
|
+
|
|
165
|
+
}, {
|
|
166
|
+
key: "cellByType",
|
|
167
|
+
value: function cellByType(table, fallback) {
|
|
168
|
+
var _this4 = this;
|
|
169
|
+
|
|
170
|
+
var fb = fallback || this.cellLabel;
|
|
171
|
+
return function (column, data, key) {
|
|
172
|
+
var f = table[_this4.nodeType(data)];
|
|
173
|
+
|
|
174
|
+
return (typeof f === 'function' ? f : fb)(column, data, key);
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
/* **************************************************************** *
|
|
178
|
+
* utils *
|
|
179
|
+
* **************************************************************** */
|
|
180
|
+
|
|
181
|
+
}, {
|
|
182
|
+
key: "valueOrEmpty",
|
|
183
|
+
value: function valueOrEmpty(v) {
|
|
184
|
+
return v === null || v === undefined ? this.empty : v;
|
|
185
|
+
}
|
|
186
|
+
}]);
|
|
187
|
+
|
|
188
|
+
return Supporter;
|
|
189
|
+
}();
|
|
190
|
+
|
|
191
|
+
exports["default"] = Supporter;
|
package/dist/WBSTable.js
CHANGED
|
@@ -39,6 +39,21 @@ function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
|
|
|
39
39
|
|
|
40
40
|
var core = new _Core["default"]();
|
|
41
41
|
var colon = new _wnqiBig["default"]();
|
|
42
|
+
var BOM = '';
|
|
43
|
+
|
|
44
|
+
function downloadCSV(filename, csv) {
|
|
45
|
+
var blob = new Blob([BOM + csv], {
|
|
46
|
+
type: 'text/csv;charset=utf-8;'
|
|
47
|
+
});
|
|
48
|
+
var url = URL.createObjectURL(blob);
|
|
49
|
+
var a = document.createElement('a');
|
|
50
|
+
a.href = url;
|
|
51
|
+
a.download = filename;
|
|
52
|
+
document.body.appendChild(a);
|
|
53
|
+
a.click();
|
|
54
|
+
document.body.removeChild(a);
|
|
55
|
+
URL.revokeObjectURL(url);
|
|
56
|
+
}
|
|
42
57
|
|
|
43
58
|
function WBSTable(props) {
|
|
44
59
|
var _useState = (0, _react.useState)(core.makeColumns(props.columns)),
|
|
@@ -76,11 +91,24 @@ function WBSTable(props) {
|
|
|
76
91
|
bilding = _useState14[0],
|
|
77
92
|
setBilding = _useState14[1];
|
|
78
93
|
|
|
94
|
+
var _useState15 = (0, _react.useState)({}),
|
|
95
|
+
_useState16 = _slicedToArray(_useState15, 2),
|
|
96
|
+
selected_inner = _useState16[0],
|
|
97
|
+
setSelectedInner = _useState16[1];
|
|
98
|
+
|
|
79
99
|
var data = props.source;
|
|
80
100
|
var options = props.options;
|
|
81
101
|
var start_id = props.start_id;
|
|
82
|
-
var
|
|
83
|
-
var style = props.style || {};
|
|
102
|
+
var download = props.download;
|
|
103
|
+
var style = props.style || {}; // 行の選択。selection は 'single' | 'multiple' | false(既定)。
|
|
104
|
+
// selected が渡されたときは controlled として扱い、内部状態は使わない。
|
|
105
|
+
|
|
106
|
+
var selection = props.selection === true ? 'single' : props.selection || false;
|
|
107
|
+
var controlled = Array.isArray(props.selected);
|
|
108
|
+
var selected = controlled ? props.selected.reduce(function (o, id) {
|
|
109
|
+
o[id] = true;
|
|
110
|
+
return o;
|
|
111
|
+
}, {}) : selected_inner;
|
|
84
112
|
(0, _react.useEffect)(function () {
|
|
85
113
|
_asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee() {
|
|
86
114
|
return regeneratorRuntime.wrap(function _callee$(_context) {
|
|
@@ -124,7 +152,15 @@ function WBSTable(props) {
|
|
|
124
152
|
row: {
|
|
125
153
|
visible: function visible(number, v) {
|
|
126
154
|
return setColumns(core.changeColumnsVisible(number, v, columns));
|
|
127
|
-
}
|
|
155
|
+
},
|
|
156
|
+
width: function width(number, key, v) {
|
|
157
|
+
return setColumns(core.changeColumnWidth(number, key, v, columns));
|
|
158
|
+
},
|
|
159
|
+
select: selection ? function (record) {
|
|
160
|
+
var next = core.toggleSelected(record._id, selected, selection);
|
|
161
|
+
if (!controlled) setSelectedInner(next);
|
|
162
|
+
if (props.onSelect) props.onSelect(core.selectedRecords(next, records), core.selectedIds(next), record);
|
|
163
|
+
} : null
|
|
128
164
|
},
|
|
129
165
|
wbs: {
|
|
130
166
|
"switch": function _switch(action, _id) {
|
|
@@ -137,8 +173,18 @@ function WBSTable(props) {
|
|
|
137
173
|
return setFilterWp(v);
|
|
138
174
|
}
|
|
139
175
|
},
|
|
140
|
-
download: function
|
|
141
|
-
|
|
176
|
+
download: download ? function () {
|
|
177
|
+
return download();
|
|
178
|
+
} : {
|
|
179
|
+
hierarchy: function hierarchy() {
|
|
180
|
+
return downloadCSV('wbs-hierarchy.csv', core.makeCSVHierarchy(columns_filterd, records));
|
|
181
|
+
},
|
|
182
|
+
flat: function flat() {
|
|
183
|
+
return downloadCSV('wbs-flat.csv', core.makeCSVFlat(columns_filterd, records));
|
|
184
|
+
},
|
|
185
|
+
data: function data() {
|
|
186
|
+
return downloadCSV('wbs-data.csv', core.makeCSVData(records));
|
|
187
|
+
}
|
|
142
188
|
}
|
|
143
189
|
};
|
|
144
190
|
var style_loading = {
|
|
@@ -152,8 +198,7 @@ function WBSTable(props) {
|
|
|
152
198
|
open: chooser_column,
|
|
153
199
|
visible_wp: visible_wp,
|
|
154
200
|
filter_wp: filter_wp,
|
|
155
|
-
callbacks: callbacks
|
|
156
|
-
csv: core.makeCSV()
|
|
201
|
+
callbacks: callbacks
|
|
157
202
|
}), chooser_column && /*#__PURE__*/_react["default"].createElement("div", {
|
|
158
203
|
style: {
|
|
159
204
|
marginBottom: 11
|
|
@@ -169,7 +214,7 @@ function WBSTable(props) {
|
|
|
169
214
|
color: '#888'
|
|
170
215
|
}
|
|
171
216
|
}, "Waiting for ........")), !bilding && /*#__PURE__*/_react["default"].createElement("table", {
|
|
172
|
-
className:
|
|
217
|
+
className: 'table is-striped is-narrow is-hoverable' + (core.fitsFullWidth(columns_filterd) ? ' is-fullwidth' : ''),
|
|
173
218
|
style: style
|
|
174
219
|
}, /*#__PURE__*/_react["default"].createElement(Comps.THead, {
|
|
175
220
|
columns: columns_filterd,
|
|
@@ -182,6 +227,8 @@ function WBSTable(props) {
|
|
|
182
227
|
callbacks: callbacks,
|
|
183
228
|
visible_wp: visible_wp,
|
|
184
229
|
closed_wbs: closed_wbs,
|
|
230
|
+
selected: selected,
|
|
231
|
+
style_selected: style.selected,
|
|
185
232
|
filter_wp: filter_wp,
|
|
186
233
|
style: style.body
|
|
187
234
|
})));
|