hyperapp-is 0.1.8 → 0.1.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.
@@ -1,47 +1,22 @@
1
1
  import { VNode, Effect } from "hyperapp";
2
2
  import { Keys_NavigatorItem } from "./state";
3
- /**
4
- * ツリー構造となるナビゲーションオブジェクト
5
- *
6
- * parent - 親アイテム
7
- * name - 名前
8
- * properties - プロパティオブジェクト
9
- * children - 子アイテムリスト (undefinedのときはファイル)
10
- * path - パス
11
- * extension - 拡張情報
12
- */
13
3
  export interface NavigatorItem {
14
4
  parent: NavigatorItem | null;
15
5
  name: string;
6
+ path: string;
16
7
  properties?: Record<string, any>;
17
8
  children?: NavigatorItem[];
18
- path: string;
19
9
  extension?: Record<string, any>;
20
10
  }
21
- /**
22
- * getEntriesで返すオブジェクト
23
- *
24
- * name - 名前
25
- * data - データ
26
- * isNode - ディレクトリか
27
- */
28
11
  export interface JsonEntry<D> {
29
12
  name: string;
30
13
  data: D;
31
14
  isNode: boolean;
32
15
  }
33
- /**
34
- * ヘッダー名と値
35
- */
36
16
  export interface NavigatorColumn {
37
17
  name: string;
38
18
  val: (item: NavigatorItem) => any;
39
19
  }
40
- /**
41
- * Json から NavigatorItem に変換
42
- * getEntries の採用により、JSON の形を問わない
43
- * extension により、任意情報を保存できる
44
- */
45
20
  export declare const convertJsonToNavigatorItem: <D>(props: {
46
21
  parent: NavigatorItem | null;
47
22
  name: string;
@@ -51,15 +26,25 @@ export declare const convertJsonToNavigatorItem: <D>(props: {
51
26
  depth?: number;
52
27
  extension?: (item: NavigatorItem, data: D, depth: number) => Record<string, any> | undefined;
53
28
  }) => NavigatorItem;
54
- /**
55
- * NavigatorItem の親をリストで取得する
56
- */
57
29
  export declare const getParentItems: (item: NavigatorItem | undefined) => NavigatorItem[];
58
30
  /**
59
- * ナビゲーションファインダーコンポーネント
31
+ * @template S
32
+ * @param props - props
33
+ *
34
+ * @param {S} props.state
35
+ * @param {string} props.id
36
+ * @param {Keys_NavigatorItem} props.currentKeys
37
+ * @param {(directory: NavigatorItem | undefined) => NavigatorColumn[]} [props.columns]
38
+ * @param {number} [props.maxItemsCount]
39
+ * @param {(state: S, item: NavigatorItem) => S | [S, Effect<S>]} [props.itemClick]
40
+ * @param {(props: {state: S, current?: NavigatorItem, extension?: Record<string, any>}, vnode: VNode<S>) => VNode<S>} [props.afterRender]
41
+ * @param {Record<string, any>} [props.extension]
42
+ *
43
+ * @returns {VNode<S>}
60
44
  */
61
45
  export declare const NavigatorFinder: <S>(props: {
62
46
  state: S;
47
+ id: string;
63
48
  currentKeys: Keys_NavigatorItem;
64
49
  columns?: (directory: NavigatorItem | undefined) => NavigatorColumn[];
65
50
  maxItemsCount?: number;
@@ -1,28 +1,9 @@
1
1
  // hyperapp-is / core / navigator.ts
2
- import { getValue, setValue } from "./state";
3
- import { el, deleteKeys } from "./component";
4
- /* element */
5
- const div = el("div");
6
- const table = el("table");
7
- const thead = el("thead");
8
- const tbody = el("tbody");
9
- const tr = el("tr");
10
- const th = el("th");
11
- const td = el("td");
12
- const ul = el("ul");
13
- const ol = el("ol");
14
- const li = el("li");
15
- const button = el("button");
16
- const input = el("input");
17
- const span = el("span");
2
+ import { getValue, setValue, getLocalState, setLocalState, createLocalKey } from "./state";
3
+ import { el, deleteKeys, SelectButton } from "./component";
18
4
  // ---------- ---------- ---------- ---------- ----------
19
5
  // convertJsonToNavigatorItem
20
6
  // ---------- ---------- ---------- ---------- ----------
21
- /**
22
- * Json から NavigatorItem に変換
23
- * getEntries の採用により、JSON の形を問わない
24
- * extension により、任意情報を保存できる
25
- */
26
7
  export const convertJsonToNavigatorItem = function (props) {
27
8
  const { parent, name, data, getEntries, isNode, depth = 0, extension } = props;
28
9
  const result = {
@@ -30,7 +11,6 @@ export const convertJsonToNavigatorItem = function (props) {
30
11
  name,
31
12
  path: parent ? parent.path + "/" + name : "/" + name
32
13
  };
33
- // 拡張情報
34
14
  if (extension) {
35
15
  const ext = extension(result, data, depth);
36
16
  if (ext)
@@ -41,11 +21,9 @@ export const convertJsonToNavigatorItem = function (props) {
41
21
  const children = [];
42
22
  getEntries(data, depth).forEach(entry => {
43
23
  const isProperty = typeof entry.data !== "object" || Array.isArray(entry.data);
44
- // プロパティ
45
24
  if (isProperty) {
46
25
  properties[entry.name] = entry.data;
47
26
  hasProperty = true;
48
- // 子アイテム
49
27
  }
50
28
  else {
51
29
  children.push(convertJsonToNavigatorItem({
@@ -62,15 +40,12 @@ export const convertJsonToNavigatorItem = function (props) {
62
40
  if (hasProperty)
63
41
  result.properties = properties;
64
42
  if (isNode)
65
- result.children = children; // ファイルのときは undefined
43
+ result.children = children;
66
44
  return result;
67
45
  };
68
46
  // ---------- ---------- ---------- ---------- ----------
69
47
  // getParentItems
70
48
  // ---------- ---------- ---------- ---------- ----------
71
- /**
72
- * NavigatorItem の親をリストで取得する
73
- */
74
49
  export const getParentItems = (item) => {
75
50
  if (!item)
76
51
  return [];
@@ -83,16 +58,54 @@ export const getParentItems = (item) => {
83
58
  return result.reverse();
84
59
  };
85
60
  // ---------- ---------- ---------- ---------- ----------
61
+ // vnodes
62
+ // ---------- ---------- ---------- ---------- ----------
63
+ const div = el("div");
64
+ const table = el("table");
65
+ const thead = el("thead");
66
+ const tbody = el("tbody");
67
+ const tr = el("tr");
68
+ const th = el("th");
69
+ const td = el("td");
70
+ const ul = el("ul");
71
+ const ol = el("ol");
72
+ const li = el("li");
73
+ const button = el("button");
74
+ const input = el("input");
75
+ const span = el("span");
76
+ // ---------- ---------- ---------- ---------- ----------
86
77
  // NavigatorFinder Component
87
78
  // ---------- ---------- ---------- ---------- ----------
88
79
  /**
89
- * ナビゲーションファインダーコンポーネント
80
+ * @template S
81
+ * @param props - props
82
+ *
83
+ * @param {S} props.state
84
+ * @param {string} props.id
85
+ * @param {Keys_NavigatorItem} props.currentKeys
86
+ * @param {(directory: NavigatorItem | undefined) => NavigatorColumn[]} [props.columns]
87
+ * @param {number} [props.maxItemsCount]
88
+ * @param {(state: S, item: NavigatorItem) => S | [S, Effect<S>]} [props.itemClick]
89
+ * @param {(props: {state: S, current?: NavigatorItem, extension?: Record<string, any>}, vnode: VNode<S>) => VNode<S>} [props.afterRender]
90
+ * @param {Record<string, any>} [props.extension]
91
+ *
92
+ * @returns {VNode<S>}
90
93
  */
91
94
  export const NavigatorFinder = function (props) {
92
95
  var _a, _b;
93
- const { state, currentKeys, maxItemsCount = 0, itemClick, afterRender, extension } = props;
96
+ // variable
97
+ const { state, id, currentKeys, maxItemsCount = 0, itemClick, afterRender, extension } = props;
94
98
  const current = getValue(state, currentKeys, undefined);
99
+ // localState
100
+ const { searchText, selected } = getLocalState(state, id, {
101
+ searchText: "",
102
+ selected: []
103
+ });
104
+ // selected filter
105
+ const isFilter = selected.includes(`${createLocalKey(id)}_filter`);
106
+ // ---------- ---------- ----------
95
107
  // createColumns
108
+ // ---------- ---------- ----------
96
109
  const createColumns = (_a = props.columns) !== null && _a !== void 0 ? _a : ((directory) => {
97
110
  const result = [];
98
111
  if (!directory)
@@ -134,25 +147,60 @@ export const NavigatorFinder = function (props) {
134
147
  const parentItems = getParentItems(current);
135
148
  if (current)
136
149
  parentItems.push(current);
150
+ // ---------- ---------- ----------
151
+ // hitTest
152
+ // ---------- ---------- ----------
153
+ const hitTest = (text) => {
154
+ const S = searchText.trim().toLowerCase();
155
+ if (S === "")
156
+ return false;
157
+ const keys = S.replace(/[  ]+/g, " ").split(" ").filter(Boolean);
158
+ if (keys.length === 0)
159
+ return false;
160
+ if (typeof text !== "string")
161
+ return false;
162
+ const sText = text.toLowerCase();
163
+ return keys.every(key => sText.includes(key));
164
+ }; // end hitTest
165
+ // ---------- ---------- ----------
137
166
  // getItems
167
+ // ---------- ---------- ----------
138
168
  const getItems = (item) => {
139
169
  if (!item || item.children === undefined)
140
170
  return [];
171
+ // filter
172
+ const result = isFilter && searchText !== ""
173
+ ? item.children.filter(child => {
174
+ return columns.some(col => hitTest(col.val(child)));
175
+ })
176
+ : item.children;
177
+ // maxCount
141
178
  const count = maxItemsCount === 0
142
- ? item.children.length
143
- : Math.min(maxItemsCount, item.children.length);
144
- return item.children.slice(0, count);
179
+ ? result.length
180
+ : Math.min(maxItemsCount, result.length);
181
+ return result.slice(0, count);
145
182
  }; // end getItems
146
183
  // items
147
184
  const items = getItems(current);
185
+ // items count
148
186
  const count = current
149
187
  ? (_b = current.children) === null || _b === void 0 ? void 0 : _b.length
150
188
  : 0;
189
+ // items hitCount
190
+ const hitCount = isFilter
191
+ ? items.length
192
+ : items.filter(item => columns.some(col => hitTest(col.val(item)))).length;
193
+ // ---------- ---------- ----------
151
194
  // action_parentClick
195
+ // ---------- ---------- ----------
152
196
  const action_parentClick = (state, item) => {
153
- return setValue(state, currentKeys, item);
197
+ return setLocalState(setValue(state, currentKeys, item), id, {
198
+ selected: []
199
+ });
154
200
  };
201
+ // ---------- ---------- ----------
155
202
  // action_itemClick
203
+ // ---------- ---------- ----------
156
204
  const action_itemClick = (state, item) => {
157
205
  // 子アイテムがすべてプロパティのときは移動しない
158
206
  const children = item.children;
@@ -160,16 +208,54 @@ export const NavigatorFinder = function (props) {
160
208
  return state;
161
209
  if (!children.some(child => typeof child === "object" && !Array.isArray(child)))
162
210
  return state;
163
- return setValue(state, currentKeys, item);
211
+ // filterは解除する
212
+ return setLocalState(setValue(state, currentKeys, item), id, {
213
+ selected: []
214
+ });
164
215
  };
216
+ // ---------- ---------- ----------
217
+ // action_inputSearchText
218
+ // ---------- ---------- ----------
219
+ const action_inputSearchText = (state, e) => {
220
+ const input = e.currentTarget;
221
+ if (!input)
222
+ return state;
223
+ return setLocalState(state, id, {
224
+ searchText: input.value
225
+ });
226
+ }; // end action_inputSearchText
227
+ // ---------- ---------- ----------
228
+ // action_copyFolderPath
229
+ // ---------- ---------- ----------
230
+ const action_copyFolderPath = (state) => {
231
+ if (!current)
232
+ return state;
233
+ navigator.clipboard.writeText(current.path);
234
+ return state;
235
+ }; // end action_copyFolderPath
236
+ // ---------- ---------- ----------
165
237
  // VNode
238
+ // ---------- ---------- ----------
166
239
  const vnode = div({
167
240
  ...deleteKeys(props, "state", "currentKeys", "columns", "itemClick", "afterRender", "extension")
168
241
  },
169
242
  // toolBar
170
243
  div({
171
244
  class: "toolBar"
172
- }, "toolBar (未実装)"),
245
+ }, input({
246
+ type: "text",
247
+ placeholder: "search keys",
248
+ value: searchText,
249
+ oninput: action_inputSearchText
250
+ }), SelectButton({
251
+ state: state,
252
+ id: `${createLocalKey(id)}_filter`,
253
+ keyNames: [createLocalKey(id), "selected"]
254
+ }, "FILTER"), button({
255
+ type: "button",
256
+ title: "現在のフォルダパスを、クリップボードにコピー",
257
+ onclick: action_copyFolderPath
258
+ }, "COPY")),
173
259
  // parentItems
174
260
  ol({}, parentItems.map(parent => li({
175
261
  key: parent.path,
@@ -183,11 +269,15 @@ export const NavigatorFinder = function (props) {
183
269
  : [action_itemClick, item]
184
270
  }, columns.map(col => td({
185
271
  title: col.val(item)
186
- }, col.val(item))))))),
272
+ }, span({
273
+ class: hitTest(col.val(item)) ? "hit" : ""
274
+ }, col.val(item)))))))),
187
275
  // statusBar
188
276
  div({
189
277
  class: "statusBar"
190
- }, `items ${items.length} / ${count}`));
278
+ }, `items ${items.length} / ${count}` + (searchText !== "" ? ` (${hitCount} hit)` : "")));
279
+ // ---------- ---------- ----------
191
280
  // afterRender
281
+ // ---------- ---------- ----------
192
282
  return afterRender ? afterRender({ state, current, extension }, vnode) : vnode;
193
283
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hyperapp-is",
3
- "version": "0.1.8",
3
+ "version": "0.1.9",
4
4
  "description": "UI foundation library for Hyperapp by is4416",
5
5
  "license": "MIT",
6
6
  "type": "module",