@yanqirenshi/table.wbs 0.0.18 → 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.
@@ -0,0 +1,11 @@
1
+ {
2
+ "version": "0.0.1",
3
+ "configurations": [
4
+ {
5
+ "name": "example",
6
+ "runtimeExecutable": "npm",
7
+ "runtimeArgs": ["--prefix", "example", "start"],
8
+ "port": 3002
9
+ }
10
+ ]
11
+ }
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: "makeCSV",
173
- value: function makeCSV() {
174
- return {
175
- headers: [{
176
- label: "c1",
177
- key: "firstname"
178
- }, {
179
- label: "c2",
180
- key: "lastname"
181
- }, {
182
- label: "c3",
183
- key: "email"
184
- }],
185
- data: [{
186
- firstname: "AAA",
187
- lastname: "BBB",
188
- email: "CCC"
189
- }, {
190
- firstname: "AAA",
191
- lastname: "BBB",
192
- email: "CCC"
193
- }, {
194
- firstname: "AAA",
195
- lastname: "BBB",
196
- email: "CCC"
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 _download = props.download;
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 download() {
141
- if (_download) _download();
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: "table is-fullwidth is-striped is-narrow is-hoverable",
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
  })));