hyperapp-is 0.1.7 → 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.
package/README.md CHANGED
@@ -532,16 +532,14 @@ getEntriesの返す値
532
532
 
533
533
  ```ts
534
534
  export interface JsonEntry <D> {
535
- name : string
536
- data : D
537
- isProperty: boolean
538
- isNode : boolean
535
+ name : string
536
+ data : D
537
+ isNode: boolean
539
538
  }
540
539
  ```
541
540
 
542
541
  - name : 名前
543
542
  - data : データ
544
- - isProperty: プロパティか
545
543
  - isNode : ディレクトリか
546
544
 
547
545
  ---
@@ -609,7 +607,7 @@ export const NavigatorFinder = function <S> (
609
607
  props: {
610
608
  state : S
611
609
  currentKeys : Keys_NavigatorItem
612
- columns ?: NavigatorColumn[]
610
+ columns ?: (directory: NavigatorItem | undefined) => NavigatorColumn[]
613
611
  maxItemsCount?: number
614
612
  itemClick ?: (state: S, item: NavigatorItem) => S | [S, Effect<S>]
615
613
  afterRender ?: (props: {
@@ -625,7 +623,7 @@ export const NavigatorFinder = function <S> (
625
623
 
626
624
  - state : ステート
627
625
  - currentKeys : カレント NavigatorItem までのパス
628
- - columns : NavigatorColumn の配列
626
+ - columns : NavigatorColumn の配列を返す関数
629
627
  - maxItemsCount: 最大表示するアイテム数
630
628
  - itemClick : アイテムをクリックした時のアクション
631
629
  - afterRender : レンダーフック
@@ -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)
@@ -109,8 +122,6 @@ export const NavigatorFinder = function (props) {
109
122
  // get properties
110
123
  // 子アイテムのプロパティをすべて確認して抽出しています
111
124
  children.forEach(child => {
112
- if (!child.children)
113
- return;
114
125
  if (child.properties) {
115
126
  Object.keys(child.properties).forEach(key => names.push(key));
116
127
  }
@@ -136,25 +147,60 @@ export const NavigatorFinder = function (props) {
136
147
  const parentItems = getParentItems(current);
137
148
  if (current)
138
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
+ // ---------- ---------- ----------
139
166
  // getItems
167
+ // ---------- ---------- ----------
140
168
  const getItems = (item) => {
141
169
  if (!item || item.children === undefined)
142
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
143
178
  const count = maxItemsCount === 0
144
- ? item.children.length
145
- : Math.min(maxItemsCount, item.children.length);
146
- return item.children.slice(0, count);
179
+ ? result.length
180
+ : Math.min(maxItemsCount, result.length);
181
+ return result.slice(0, count);
147
182
  }; // end getItems
148
183
  // items
149
184
  const items = getItems(current);
185
+ // items count
150
186
  const count = current
151
187
  ? (_b = current.children) === null || _b === void 0 ? void 0 : _b.length
152
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
+ // ---------- ---------- ----------
153
194
  // action_parentClick
195
+ // ---------- ---------- ----------
154
196
  const action_parentClick = (state, item) => {
155
- return setValue(state, currentKeys, item);
197
+ return setLocalState(setValue(state, currentKeys, item), id, {
198
+ selected: []
199
+ });
156
200
  };
201
+ // ---------- ---------- ----------
157
202
  // action_itemClick
203
+ // ---------- ---------- ----------
158
204
  const action_itemClick = (state, item) => {
159
205
  // 子アイテムがすべてプロパティのときは移動しない
160
206
  const children = item.children;
@@ -162,16 +208,54 @@ export const NavigatorFinder = function (props) {
162
208
  return state;
163
209
  if (!children.some(child => typeof child === "object" && !Array.isArray(child)))
164
210
  return state;
165
- return setValue(state, currentKeys, item);
211
+ // filterは解除する
212
+ return setLocalState(setValue(state, currentKeys, item), id, {
213
+ selected: []
214
+ });
166
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
+ // ---------- ---------- ----------
167
237
  // VNode
238
+ // ---------- ---------- ----------
168
239
  const vnode = div({
169
240
  ...deleteKeys(props, "state", "currentKeys", "columns", "itemClick", "afterRender", "extension")
170
241
  },
171
242
  // toolBar
172
243
  div({
173
244
  class: "toolBar"
174
- }, "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")),
175
259
  // parentItems
176
260
  ol({}, parentItems.map(parent => li({
177
261
  key: parent.path,
@@ -185,11 +269,15 @@ export const NavigatorFinder = function (props) {
185
269
  : [action_itemClick, item]
186
270
  }, columns.map(col => td({
187
271
  title: col.val(item)
188
- }, col.val(item))))))),
272
+ }, span({
273
+ class: hitTest(col.val(item)) ? "hit" : ""
274
+ }, col.val(item)))))))),
189
275
  // statusBar
190
276
  div({
191
277
  class: "statusBar"
192
- }, `items ${items.length} / ${count}`));
278
+ }, `items ${items.length} / ${count}` + (searchText !== "" ? ` (${hitCount} hit)` : "")));
279
+ // ---------- ---------- ----------
193
280
  // afterRender
281
+ // ---------- ---------- ----------
194
282
  return afterRender ? afterRender({ state, current, extension }, vnode) : vnode;
195
283
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hyperapp-is",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "description": "UI foundation library for Hyperapp by is4416",
5
5
  "license": "MIT",
6
6
  "type": "module",