hyperapp-is 0.1.50 → 0.2.0

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.
Files changed (36) hide show
  1. package/README.md +220 -1557
  2. package/dist/{hyperapp-is/animation → animation}/easing.d.ts +4 -0
  3. package/dist/{hyperapp-is/animation → animation}/easing.js +4 -4
  4. package/dist/{hyperapp-is/animation → animation}/properties.d.ts +9 -16
  5. package/dist/{hyperapp-is/animation → animation}/properties.js +13 -12
  6. package/dist/{hyperapp-is/animation → animation}/raf.d.ts +11 -12
  7. package/dist/animation/raf.js +192 -0
  8. package/dist/core/component.d.ts +23 -0
  9. package/dist/core/component.js +52 -0
  10. package/dist/core/effects.d.ts +10 -0
  11. package/dist/core/effects.js +34 -0
  12. package/dist/core/state.d.ts +21 -0
  13. package/dist/{hyperapp-is/core → core}/state.js +15 -20
  14. package/dist/dom/dialog.d.ts +6 -0
  15. package/dist/dom/dialog.js +85 -0
  16. package/dist/dom/utils.d.ts +15 -0
  17. package/dist/dom/utils.js +19 -0
  18. package/dist/index.d.ts +1 -0
  19. package/dist/index.js +1 -0
  20. package/dist/services/google.d.ts +69 -0
  21. package/dist/services/google.js +170 -0
  22. package/package.json +14 -40
  23. package/dist/hyperapp-is/animation/raf.js +0 -209
  24. package/dist/hyperapp-is/animationView/carousel.d.ts +0 -48
  25. package/dist/hyperapp-is/animationView/carousel.js +0 -461
  26. package/dist/hyperapp-is/core/component.d.ts +0 -65
  27. package/dist/hyperapp-is/core/component.js +0 -242
  28. package/dist/hyperapp-is/core/navigator.d.ts +0 -115
  29. package/dist/hyperapp-is/core/navigator.js +0 -615
  30. package/dist/hyperapp-is/core/state.d.ts +0 -27
  31. package/dist/hyperapp-is/dom/utils.d.ts +0 -37
  32. package/dist/hyperapp-is/dom/utils.js +0 -69
  33. package/dist/hyperapp-is/index.d.ts +0 -16
  34. package/dist/hyperapp-is/index.js +0 -10
  35. package/dist/hyperapp-is/services/google.d.ts +0 -89
  36. package/dist/hyperapp-is/services/google.js +0 -178
package/README.md CHANGED
@@ -1,1682 +1,345 @@
1
1
  # hyperapp-is
2
2
 
3
- Lightweight. Declarative. Composable.
3
+ hyperapp 用ユーティリティ集
4
4
 
5
- Reusable component foundation library for Hyperapp v2.
6
-
7
- State-structure independent design + RAF-based animation system.
8
-
9
- ---
10
-
11
- ## Features
12
-
13
- - State-path based reusable component design
14
- - Local state helpers
15
- `getLocalState`, `setLocalState`, `createLocalKey`
16
- - Action composition utility
17
- `concatAction`
18
- - Class / props utilities
19
- `getClassList`, `deleteKeys`
20
- - requestAnimationFrame task system
21
- `RAFTask`, `subscription_RAFManager`
22
- - Built-in animated Carousel component
23
-
24
- ---
25
-
26
- ## Installation
27
-
28
- ``` bash
29
- npm install hyperapp-is
30
- ```
31
-
32
- Peer dependencies:
33
-
34
- - hyperapp v2
35
- - hyperapp-jsx-pragma (when using JSX)
36
-
37
- ---
38
-
39
- ## Basic Usage (Carousel Example)
40
-
41
- ``` ts
42
- import { app } from "hyperapp"
43
- import h from "hyperapp-jsx-pragma"
44
- import {
45
- RAFTask, subscription_RAFManager,
46
- Carousel, effect_InitCarousel
47
- } from "hyperapp-is"
48
-
49
- // State
50
- interface State {
51
- tasks: RAFTask<State>[]
52
- }
53
-
54
- const initState: State = {
55
- tasks: []
56
- }
57
-
58
- // Entry Point
59
- app({
60
- node: document.getElementById("app") as HTMLElement,
61
-
62
- init: [initState, effect_InitCarousel(["tasks"], {
63
- id : "carousel",
64
- duration: 2000,
65
- delay : 1000,
66
- step : 1
67
- })],
68
-
69
- subscriptions: (state) => [
70
- subscription_RAFManager(state, ["tasks"])
71
- ],
72
-
73
- view: (state) => (<Carousel
74
- state = { state }
75
- id = "carousel"
76
- keyNames = { ["tasks"] }
77
- >
78
- <div>Slide 1</div>
79
- <div>Slide 2</div>
80
- <div>Slide 3</div>
81
- </Carousel>)
82
- })
83
- ```
5
+ ライブラリ名の is は、is4416 is です\
6
+ 自分用に作成しているユーティリティをまとめたライブラリです
84
7
 
85
8
  ---
86
9
 
87
- ## Concept
88
-
89
- hyperapp-is allows components to:
90
-
91
- - Remain independent from root state structure
92
- - Store internal state without polluting user state
93
- - Compose actions safely
94
- - Manage animations declaratively via RAFTask
95
-
96
- Designed for building reusable UI components on top of Hyperapp.
97
-
98
- ---
99
-
100
- ## Documentation
101
-
102
- Full documentation and detailed design notes are available on GitHub:
103
-
104
- https://github.com/is4416/hyperapp-is
105
-
106
- ---
107
-
108
- ## License
109
-
110
- MIT
111
-
112
- ---
113
-
114
- # hyperapp-is
115
-
116
- Hyperapp で再利用可能なコンポーネントを作成するためのライブラリです
117
- hyperapp-is の is は、is4416 の略です
118
-
119
- [example](https://is4416.github.io/hyperapp-is/)
120
- ※ 本ライブラリの実装サンプル
121
-
122
- 本ライブラリは **イミュータブルなステート更新** と **シンプルな副作用管理** を前提として設計されています。
123
- JSX を使用する場合は `hyperapp-jsx-pragma` を前提としています。
124
-
125
- ## Functions / 関数リスト
126
-
127
- npm で非公開の関数 (実験用)は、解説に記載します
128
-
129
- **core / state.ts**
130
- - [Keys](#keys)
131
- - [getValue](#getvalue)
132
- - [setValue](#setvalue)
133
- - [getLocalState](#getlocalstate)
134
- - [setLocalState](#setlocalstate)
135
- - [createLocalKey](#createlocalkey)
136
-
137
- **core / component.ts**
138
- - [Keys_String](#keys_string-keys_arraystring)
139
- - [Keys_ArrayString](#keys_string-keys_arraystring)
140
- - [el](#el)
141
- - [concatAction](#concataction)
142
- - [getClassList](#getclasslist)
143
- - [deleteKeys](#deletekeys)
144
- - [Route](#route)
145
- - [SelectButton](#selectbutton)
146
- - [OptionButton](#optionbutton)
147
- - [HistoryInput](#historyinput)
148
-
149
- **core / navigator.ts**
150
- - [Keys_NavigatorItem](#keys_navigatoritem)
151
- - [NavigatorItem](#navigatoritem)
152
- - [JsonEntry](#jsonentry)
153
- - [NavigatorColumn](#navigatorcolumn)
154
- - [convertJsonToNavigatorItem](#convertjsontonavigatoritem)
155
- - [getParentItems](#getparentitems)
156
- - [NavigatorFinder](#navigatorfinder)
157
- - [SearchResult](#searchresult)
158
- - [NavigatorSearch](#navigatorsearch)
159
-
160
- **animation / step.ts**
161
- - [effect_throwMessageStart](#effect_throwmessagestart)
162
- - [effect_throwMessagePause](#effect_throwmessagepause--effect_throwmessageresume)
163
- - [effect_throwMessageResume](#effect_throwmessagepause--effect_throwmessageresume)
164
- - [marquee](#marquee)
165
-
166
- **animation / raf.ts**
167
- - [InternalEffect](#internaleffect)
168
- - [RAFEvent](#rafevent)
169
- - [RAFTask](#raftask)
170
- - [subscription_RAFManager](#subscription_rafmanager)
171
-
172
- **animation / properties.ts**
173
- - [CSSProperty](#cssproperty)
174
- - [createUnits](#createunits)
175
- - [createRAFProperties](#createrafproperties)
176
- - [effect_RAFProperties](#effect_rafproperties)
177
-
178
- **animation / easing.ts**
179
- - [progress_easing](#progress_easing)
180
-
181
- **animation / translate.ts**
182
- - [TranslateState](#translatestate)
183
- - [createRAFTranslate](#createraftranslate)
184
- - [effect_translateStart](#effect_translatestart)
185
- - [effect_translateRollback](#effect_translaterollback)
186
- - [effect_translateRollforward](#effect_translaterollforward)
187
- - [effect_translateSlide](#effect_translateslide)
188
-
189
- **animationView / carouselts**
190
- - [CarouselState](#carouselstate)
191
- - [CarouselController](#carouselcontroller)
192
- - [Carousel](#carousel)
193
- - [effect_InitCarousel](#effect_initcarousel)
194
-
195
- **dom / utils.ts**
196
- - [ScrollMargin](#scrollmargin)
197
- - [getScrollMargin](#getscrollmargin)
198
- - [MatrixState](#matrixstate)
199
- - [getMatrixState](#getmatrixstate)
200
-
201
- **dom / lifecycle.ts**
202
- - [effect_setTimedValue](#effect_settimedvalue)
203
- - [effect_nodesInitialize](#effect_nodesinitialize)
204
- - [subscription_nodesCleanup](#subscription_nodescleanup)
205
- - [subscription_nodesLifecycleByIds](#subscription_nodeslifecyclebyids)
206
-
207
- **services / google**
208
- - [getGoogle](#getgoogle)
209
- - [getGoogleAuthResult](#getgoogleauthresult)
210
- - [getAccessToken](#getaccesstoken)
211
- - [GoogleAuth](#googleauth)
212
-
213
- ## source file / ソースファイル
10
+ ## ファイル構成
214
11
 
215
12
  ```
216
- src
217
- └ hyperapp-is
218
- ├ index.ts
219
-
220
- ├ core
221
- │ ├ state.ts
222
- │ │ Keys
223
- │ │ getValue, setValue, getLocalState, setLocalState, createLocalKey
224
- │ │
225
- │ ├ component.ts
226
- │ │ Keys_String, Keys_ArrayString
227
- │ │ el, concatAction, getClassList, deleteKeys
228
- │ │ Route, SelectButton, OptionButton
229
- │ │
230
- │ └ navigator.ts
231
- │ Keys_NavigatorItem, NavigatorItem, JsonEntry, NavigatorColumn
232
- │ convertJsonToNavigatorItem, getParentItems, NavigatorFinder
233
- │ SearchResult, NavigatorSearch
234
-
235
- ├ animation
236
- │ ├ step.ts
237
- │ │ effect_throwMessageStart, effect_throwMessagePause, effect_throwMessageResume, marquee
238
- │ │
239
- │ ├ raf.ts
240
- │ │ InternalEffect
241
- │ │ RAFEvent
242
- │ │ RAFTask
243
- │ │ subscription_RAFManager
244
- │ │
245
- │ ├ properties.ts
246
- │ │ CSSProperty
247
- │ │ createRAFProperties
248
- │ │ effect_RAFProperties
249
- │ │
250
- │ ├ easing.ts
251
- │ │ progress_easing
252
- │ │
253
- │ └ translate.ts
254
- │ TranslateState
255
- │ createRAFTranslate
256
- │ effect_translateStart
257
- │ effect_translateRollback
258
- │ effect_translateRollforward
259
- │ effect_translateSlide
260
-
261
- ├ animationView
262
- │ └ carousel.ts
263
-
264
- ├ dom
265
- │ ├ utils.ts
266
- │ │ ScrollMargin
267
- │ │ getScrollMargin
268
- │ │ MatrixState
269
- │ │ getMatrixState
270
- │ │
271
- │ └ lifecycle.ts
272
- │ effect_setTimedValue
273
- │ effect_nodesInitialize
274
- │ subscription_nodesCleanup
275
- │ subscription_nodesLifecycleByIds
276
-
277
- └ service
278
- └ google.ts
279
- getGoogle
280
- googleAuth
281
- getAccessToken
282
- ```
283
-
284
- ## hyperapp-is/core
13
+ index.ts: エクスポートの一覧
285
14
 
286
- ### Keys
287
- ステートの任意の値までのパスを表す、文字配列の型エイリアス
288
-
289
- ```ts
290
- export type Keys = readonly string[]
291
- ```
15
+ [core]
16
+ ├ state.ts: state 操作に関するもの
17
+ ├ component.ts: VNode の作成に関するもの
18
+ └ effects.ts: Effectに関するもの
292
19
 
293
- ---
20
+ [dom]
21
+ ├ utils.ts: DOM 操作に関するユーティリティ
22
+ └ dialog.ts: DOM を利用したダイアログ
294
23
 
295
- ### getValue
296
- パスを辿ってステートから値を取得
297
- 安全にアクセス可能
24
+ [animation]
25
+ ├ raf.ts: requestAnimationFrame を state と連携する仕組み
26
+ ├ easing.ts: イージング関数集
27
+ └ properties.ts: raf.tsを利用した CSS アニメーション
298
28
 
299
- ```ts
300
- export const getValue = function <S, D> (
301
- state : S,
302
- keyNames: string[],
303
- def : D
304
- ): D
29
+ [services]
30
+ google.ts: Google 認証ユーティリティ
305
31
  ```
306
- *型保証は呼び出し側の責任*
307
-
308
- - state : ステート
309
- - keyNames: 値までのパス
310
- - def : デフォルト値
311
32
 
312
33
  ---
313
34
 
314
- ### setValue
315
- パスを辿ってステートに値を設定し、immutable な新しいステートを返す
316
-
317
- ```ts
318
- export const setValue = function <S> (
319
- state : S,
320
- keyNames: string[],
321
- value : any
322
- ): S
323
- ```
324
- - state : ステート
325
- - keyNames: 値までのパス
326
- - value : 設定する値
327
-
328
- ---
329
-
330
- ### getLocalState
331
- ID に紐づいたローカルステートを取得
332
-
333
- ```ts
334
- export const getLocalState = function <S> (
335
- state: S,
336
- id : string,
337
- def : { [key: string]: any }
338
- ): { [key: string]: any }
339
- ```
340
-
341
- - state: ステート
342
- - id : ユニークID
343
- - def : 初期値
344
-
345
- ---
346
-
347
- ### setLocalState
348
- ローカルステートを更新して新しいステートを返す
349
-
350
- ```ts
351
- export const setLocalState = function <S> (
352
- state: S,
353
- id : string,
354
- value: { [key: string]: any }
355
- ): S
356
- ```
357
-
358
- - state: ステート
359
- - id : ユニークID
360
- - value: 設定するローカルステート
361
-
362
- ---
363
-
364
- ### createLocalKey
365
- ID からユニーク文字列を作成する
366
-
367
- ```ts
368
- export const createLocalKey = (id: string): string => `local_key_${ id }`
369
- ```
370
-
371
- ---
372
-
373
- ### Keys_String, Keys_ArrayString
374
- ステートの任意の値までのパスを表す、文字配列の型エイリアス
375
-
376
- ---
377
-
378
- ### el
379
- Hyperapp の h 関数ラッパー。JSX と競合する場合に使用
380
- children の処理も同時に行っているため、本ライブラリでは VNode を作成する際に使用しています
381
-
382
- ```ts
383
- export const el = <S = any> (tag: string) => (
384
- props ?: { [key: string]: any },
385
- children?: Array
386
- ): VNode<S>
387
- ```
388
-
389
- - tag: タグ名
390
-
391
- ---
392
-
393
- ### concatAction
394
- アクションを結合して結果を返す
395
- `effect_nodesInitialize` と組み合わせ可能
396
-
397
- ```ts
398
- export const concatAction = function <S, E> (
399
- action : undefined | ((state: S, e: E) => S | [S, Effect<S>]),
400
- newState: S,
401
- e : E
402
- ): S | [S, Effect<S>]
403
- ```
404
- *newStateを設定後、DOM描画を待ち、次の action に結合します*
405
-
406
- - action : 結合するアクション
407
- - newState: 結合するステート
408
- - e : イベント (任意のイベント型)
409
-
410
- ---
411
-
412
- ### getClassList
413
- props オブジェクトから classList を取得
414
-
415
- ```ts
416
- export const getClassList = (
417
- props: { [key: string]: any }
418
- ): string[]
419
- ```
420
-
421
- - props: props
422
-
423
- ---
424
-
425
- ### deleteKeys
426
- props から不要なキーを除去
427
-
428
- ```ts
429
- export const deleteKeys = <T extends Record<string, any>> (
430
- props : T,
431
- ...keys: (keyof T)[]
432
- ): Omit<T, (typeof keys)[number]>
433
- ```
434
-
435
- - props : props
436
- - ...keys: 削除するキー
437
-
438
- ---
439
-
440
- ### Route
441
- ステート値と一致した場合に VNode を返す
442
- 条件付きレンダリングに利用
443
-
444
- ```ts
445
- export const Route = function <S> (
446
- props: {
447
- state : S
448
- keyNames: string[]
449
- match : string
450
- },
451
- children: any
452
- ): VNode<S> | null
453
- ```
454
- *返値に `null` が設定された場合 `VNode` は生成されません*
455
-
456
- - props : props
457
- - props.state : ステート
458
- - props.keyNames: ステート内の文字までのパス
459
- - props.match : 一致判定する文字
460
- - children : 出力する内容 (VNode / 配列 / 文字など)
35
+ ## 関数一覧 (interfaceは省略)
461
36
 
462
- ---
37
+ **core**
38
+ - state: getValue, setValue, getLocalState, setLocalState, createLocalKey
39
+ - component: el, concatAction, getClassList, deleteKeys
40
+ - effects: effect_toast
463
41
 
464
- ### SelectButton
465
- クラス名 `select` をトグルするボタン
466
- 複数選択可能
42
+ **dom**
43
+ - utils: getScrollMargin
44
+ - dialog: withLoadingDialog
467
45
 
468
- ```ts
469
- export const SelectButton = function <S> (
470
- props: {
471
- state : S
472
- keyNames : string[]
473
- id : string
474
- reverse? : boolean
475
- [key: string]: any
476
- },
477
- children: any
478
- ): VNode<S>
479
- ```
480
- *クリックにより、クラス名 `select` がトグルされます*
46
+ **animation**
47
+ - raf: RAFTask, subscription_RAFManager
48
+ - easing: progress_easing
49
+ - properties: createRAFProperties, effect_RAFProperties
481
50
 
482
- - props : props
483
- - props.state : ステート
484
- - props.keyNames: ステート内の文字配列までのパス
485
- - props.id : ユニークID
486
- - props.reverse?: 反転選択するか
487
- - children : 子要素 (VNode / string / 配列など)
51
+ **services**
52
+ - google: getAccessToken, GoogleAuth
488
53
 
489
54
  ---
490
55
 
491
- ### OptionButton
492
- クラス名 `select` を単一選択で切り替えるボタン
493
- 単一選択用
56
+ ## 使い方
494
57
 
495
- ```ts
496
- export const OptionButton = function <S> (
497
- props: {
498
- state : S
499
- keyNames : string[]
500
- id : string
501
- reverse? : boolean
502
- [key: string]: any
503
- },
504
- children: any
505
- ): VNode<S>
506
- ```
507
- *クリックにより、クラス名 `select` が排他的に選択されます*
508
-
509
- - props : props
510
- - props.state : ステート
511
- - props.keyNames: ステート内の文字までのパス
512
- - props.id : ユニークID
513
- - props.reverse?: 反転選択するか
514
- - children : 子要素 (VNode / string / 配列など)
58
+ **core**
515
59
 
516
- ---
60
+ - `getValue`, `setValue`: keyNames には、取得・設定したい値までのパス
517
61
 
518
- ### HistoryInput
519
- 履歴付きインプットボックス
520
- localState: { histories: string[] }
62
+ > ステートの構造に依存せずに目的の値が取得・設定できます\
63
+ > 汎用コンポーネントの作成に利用できます
521
64
 
522
- ```ts
523
- export const HistoryInput = function <S> (
524
- props: {
525
- state : S
526
- id : string
527
- keyNames : Keys_String
528
- historyLimit ?: number
529
- afterRender ?: (state: S, localState: Record<string, any>, vnode: VNode<S>[]) => VNode<S>[]
530
- [key: string] : any
65
+
66
+ ```js
67
+ const state = {
68
+ title: "ユーザー情報",
69
+ user: {
70
+ name: "青年",
71
+ age : 20
531
72
  }
532
- ): VNode<S>[]
533
- ```
534
-
535
- - props : props
536
- - props.state : ステート
537
- - props.id : ユニークID
538
- - props.keyNames: ステート内の文字までのパス
539
- - props.historyLimit: 保持する履歴の最大数 (default = 10)
540
- - props.afterRender : 全体のレンダーフック
541
- - returns : [HTMLInputElement, HTMLDataListElement]
542
-
543
- 履歴は `[props.id].histories` に保存されます
544
- 履歴を動的にクリアしたい場合 `setLocalState(state, id, { histories: [] })` などとして下さい
545
-
546
- ---
547
-
548
- ### Keys_NavigatorItem
549
- ステートの任意の値までのパスを表す、文字配列の型エイリアス
550
-
551
- ---
552
-
553
- ### NavigatorItem
554
- ツリー構造となるナビゲーションオブジェクト
555
-
556
- ```ts
557
- export interface NavigatorItem {
558
- parent : NavigatorItem | null
559
- name : string
560
- properties?: Record<string, any>
561
- children ?: NavigatorItem[]
562
- path : string
563
- extension ?: Record<string, any>
564
- }
565
- ```
566
-
567
- - parent : 親アイテム
568
- - name : 名前
569
- - properties: プロパティ
570
- - children : 子アイテム
571
- - path : パス
572
- - extension : 拡張オブジェクト
573
-
574
- ---
575
-
576
- ### JsonEntry
577
- getEntriesの返す値
578
-
579
- ```ts
580
- export interface JsonEntry <D> {
581
- name : string
582
- data : D
583
- isNode: boolean
584
73
  }
585
- ```
586
74
 
587
- - name : 名前
588
- - data : データ
589
- - isNode: ディレクトリか
590
-
591
- ---
592
-
593
- ### NavigatorColumn
594
- NavigatorFinder に渡すヘッダーと値
595
-
596
- ```ts
597
- export interface NavigatorColumn {
598
- name : string
599
- val : (item: NavigatorItem) => any
600
- text ?: (item: NavigatorItem) => string
601
- compare?: (a: NavigatorItem, b: NavigatorItem) => number
602
- }
75
+ const title = getValue(state, ["title"], "タイトル情報") // res: ユーザー情報
76
+ const name = getValue(state, ["user", "name"], "名無し") // res: 青年
77
+ const age = getValue(state, ["user", "age"], 0) // res: 20
603
78
  ```
604
79
 
605
- - name : 名前
606
- - val : 値を返す関数 (VNode等も可)
607
- - text : val が string ではない時に設定するテキスト
608
- - compare: 比較関数
609
-
610
- `compare` を設置したカラムだけ、ソート対象となります
611
-
612
80
  ---
613
81
 
614
- ### convertJsonToNavigatorItem
615
- Json から NavigatorItem に変換
616
- getEntries の採用により、JSON の形を問わない
617
-
618
- ```ts
619
- export const convertJsonToNavigatorItem = function <D> (
620
- props: {
621
- parent : NavigatorItem | null
622
- name : string
623
- data : D
624
- getEntries : (data: D, depth: number) => JsonEntry<D>[]
625
- isNode : boolean
626
- depth ?: number
627
- extension ?: (item: NavigatorItem, data: D, depth: number) => Record<string, any> | undefined
628
- }
629
- ): NavigatorItem
630
- ```
631
-
632
- - parent : 親アイテム
633
- - name : 名前
634
- - data : データ
635
- - getEntries: JsonEntry配列を返す関数
636
- - isNode : ディレクトリか
637
- - depth : 階層の深さ
638
- - extension : NavigatorItem.extensionに格納する拡張データ
82
+ - `getLocalState`, `setLocalState`: ローカルステートのように値を取得・設定できます
83
+ - `createLocalKey` は、基本的には内部で使うための関数ですが、外部からも利用可能です
639
84
 
640
- ---
641
-
642
- ### getParentItems
643
- NavigatorItem から 親アイテムのリストを取得する
644
- 自分自信はリストに含まない
85
+ > `setLocalState` は、メインステートに追加ツリーを追加します\
86
+ > ユーザーはメインステートの改変を意識することなく、ローカルステートのように値を取得・設定できます\
87
+ > 汎用コンポーネントの作成で、ローカルステートが欲しいときに利用できます
645
88
 
89
+
646
90
  ```ts
647
- export const getParentItems = (
648
- item: NavigatorItem | undefined
649
- ): NavigatorItem[]
650
- ```
651
-
652
- ---
653
-
654
- ### NavigatorFinder
655
- ナビゲーターファインダーコンポーネント
656
-
657
- ```ts
658
- export const NavigatorFinder = function <S> (
659
- props: {
660
- state : S
661
- id : string
662
- currentKeys : Keys_NavigatorItem
663
- columns ?: (directory: NavigatorItem | undefined) => NavigatorColumn[]
664
- plugIn ?: (state: S, localState: Record<string, any>) => VNode<S>[]
665
- toolBarNode ?: (state: S, localState: Record<string, any>, vnode: VNode<S>) => VNode<S>
666
- parentItemsNode ?: (state: S, localState: Record<string, any>, vnode: VNode<S>) => VNode<S>
667
- statusBarNode ?: (state: S, localState: Record<string, any>, vnode: VNode<S>) => VNode<S>
668
- afterRender ?: (state: S, localState: Record<string, any>, vnode: VNode<S>) => VNode<S>
669
- [key: string]: any
670
- }
671
- ): VNode<S>
672
- ```
673
-
674
- - state : ステート
675
- - id : ユニークID (DOM ID)
676
- - currentKeys : カレント NavigatorItem までのパス
677
- - columns : NavigatorColumn の配列を返す関数
678
- - plugIn : プラグインの挿入
679
- - toolBarNode : `toolBar` レンダーフック
680
- - parentItemsNode: `parentItems` レンダーフック
681
- - statusBarNode : `statusBar` レンダーフック
682
- - afterRender : 全体のレンダーフック
683
-
684
- localState
685
- - searchText: "" as string // 検索テキスト
686
- - selected : [] as string[] // 選択されているボタン名
687
- - sortType : undefined // ソート用比較関数: (a: NavigatorItem, b: NavigatorItem) => number
688
- - reverse : false as boolean // ソートを逆順にするか
689
- - sortKey : undefined as undefined | string // 使用されているソート名 (column.name)
690
-
691
- vnode
692
- ```html
693
- <div id={id}>
694
- <div class="rapper">
695
- <div class="toolBar">
696
- <div>
697
- <input type="text" id={id}_searchText list={id}_searchText-history/>
698
- <datalist id={id}_searchText-history />
699
- </div>
700
- <input type="text" />
701
- <button type="button">FILTER</button>
702
- </div>
703
-
704
- <div class="parentItems">
705
- <ol>
706
- <li>parent</li>
707
- </ol>
708
- <button type="button">COPY</button>
709
- </div>
710
-
711
- <div class="items">
712
- <table>
713
- <thead>
714
- <tr>
715
- <th>ヘッダー</th>
716
- <th class="sort">ソート付きヘッダー</th>
717
- </tr>
718
- </thead>
719
- <tbody>
720
- <tr>
721
- <td class="file"><span>value</span></td>
722
- <td class="directory"><span>value</span></td>
723
- </tr>
724
- </tbody>
725
- </table>
726
- </div>
727
-
728
- <div class="statusBar">message</div>
729
- </div>
730
-
731
- <!-- plugIn 必要な分だけ追加 -->
732
- <div id={plugInID}>
733
- </div>
734
- </div>
735
- ```
736
-
737
- ---
738
-
739
- ### SearchResult
740
- `NavigatorSearch` で検索結果となる構造体
741
-
742
- ```ts
743
- export interface SearchResult {
744
- item : NavigatorItem
745
- depth: number
91
+ interface LocalState {
92
+ color?: string
93
+ size ?: number
746
94
  }
747
- ```
748
95
 
749
- ---
96
+ const localState = getLocalState(state, name, {
97
+ color: "red"
98
+ }) // res: { color: "red" }
750
99
 
751
- ### NavigatorSearch
752
- `NavigatorFinder` プラグイン
100
+ const newState = setLocalState(state, name, {
101
+ color: "blue"
102
+ size : 180
103
+ })
753
104
 
754
- ```ts
755
- export const NavigatorSearch = function <S> (
756
- props: {
757
- state : S
758
- id : string
759
- currentKeys : Keys_NavigatorItem
760
- searchResult : (item: NavigatorItem, depth: number) => VNode<S> | VNode<S>[]
761
- hitTest : (item: NavigatorItem) => boolean
762
- maxItemsCount : number
763
- toolBarNode ?: (state: S, localState: Record<string, any>, vnode: VNode<S>) => VNode<S>
764
- parentItemsNode?: (state: S, localState: Record<string, any>, vnode: VNode<S>) => VNode<S>
765
- statusBarNode ?: (state: S, localState: Record<string, any>, vnode: VNode<S>) => VNode<S>
766
- afterRender ?: (state: S, localState: Record<string, any>, vnode: VNode<S>) => VNode<S>
767
- [key: string] : any
105
+ /* res:
106
+ {
107
+ title: "ユーザ情報",
108
+ user : {
109
+ name: "青年",
110
+ age : 20
111
+ },
112
+ local_key_青年: {
113
+ color : "blue",
114
+ size : 180
115
+ }
768
116
  }
769
- ): VNode<S>
117
+ */
770
118
  ```
771
119
 
772
- - state : ステート
773
- - id : ユニークID (DOM ID)
774
- - currentKeys : カレント NavigatorItem までのパス
775
- - searchResult : カードとして表示する VNode
776
- - hitTest : 抽出条件
777
- - maxItemsCount : 最初に表示するカードの最大数
778
- - toolBarNode : `toolBar` レンダーフック
779
- - parentItemsNode: `parentItems` レンダーフック
780
- - statusBarNode : `statusBar` レンダーフック
781
- - afterRender : 全体のレンダーフック
782
-
783
- localState
784
- - maxItemsCount: props.maxItemsCount as number // カードの最大表示数
785
- - sortName : undefined as undefined | string // ソート名
786
- - sortFn : undefined as undefined | ((a: SearchResult, b: SearchResult) => number) // 比較関数
787
- - isDirectory : true // ディレクトリを表示
788
- - isFile : true // ファイルを表示
789
-
790
120
  ---
791
121
 
792
- ## hyperapp-is/animation
793
-
794
- ### effect_throwMessageStart
795
- 文字を一文字ずつ流し込むエフェクト
796
-
797
- *実験用コードのため、npm 非公開です*
122
+ - `el`: hyperapp の `h` 関数のラッパーです
798
123
 
124
+ > text関数の省略や、配列の省略などが可能です\
125
+ > JSXを使用する場合は、出番がありません
799
126
 
127
+
800
128
  ```ts
801
- export const effect_throwMessageStart = function <S> (
802
- keyNames: string[],
803
- id : string,
804
- text : string,
805
- interval: number,
806
- ): (dispatch: Dispatch<S>) => void
807
- ```
129
+ const body = el("body")
130
+ const div = el("div")
808
131
 
809
- - keyNames: 値までのパス
810
- - id : ユニークID
811
- - text : 流し込む文字
812
- - interval: 次の文字を流し込むまでの間隔 (ms)
813
-
814
- ---
815
-
816
- ### effect_throwMessagePause / effect_throwMessageResume
817
- throwMessage を一時停止・再開
818
-
819
- *実験用コードのため、npm 非公開です*
820
-
821
- ```ts
822
- export const effect_throwMessagePause = function <S> (
823
- id: string
824
- ): (dispatch: Dispatch<S>) => void
132
+ const vnode = body({},
133
+ div({}, "タイトル行"),
134
+ div({}, "メイン")
135
+ )
825
136
  ```
826
- ```ts
827
- export const effect_throwMessageResume = function <S> (
828
- id: string
829
- ): (dispatch: Dispatch<S>) => void
830
- ```
831
-
832
- - id: ユニークID
833
137
 
834
138
  ---
835
139
 
836
- ### marquee
837
- Translate 風に DOM が流れるアニメーションを実行します
140
+ - deleteKeys : props から指定したキーを削除します
141
+ - getClassList: props からクラスリストを取得します
142
+ - concatAction: アクションを結合します
838
143
 
839
- *実験用コードのため、npm 非公開です*
144
+ > 汎用コンポーネントを作成する場合などに使用できます
840
145
 
146
+
841
147
  ```ts
842
- export const marquee = function <S> (
148
+ const button = el("button")
149
+
150
+ const MyButton = function <S> (
843
151
  props: {
844
- ul : HTMLUListElement
845
- duration: number
846
- interval: number
847
- easing ?: (t: number) => number
152
+ state: S
153
+ onclick?: (state: S, e: Event) => S | [S, Effect<S>],
154
+ keyNames: string[] // number までのパス
155
+ [key: string]: any
156
+ },
157
+ child: any
158
+ ): VNode<S> {
159
+ const { state, onclick, keyNames } = props
160
+ const classList = getClassList(props)
161
+
162
+ // action
163
+ const action_click = (state: S, e: Event) => {
164
+ const count = getValue(state, keyNames, 0)
165
+ const newState = setValue(state, keyNames, count + 1)
166
+
167
+ // ユーザーが指定したアクションとの結合
168
+ return concatAction(onclick, newState, e)
848
169
  }
849
- ): { start: () => void, stop : () => void }
850
- ```
851
- *ステートから独立して `requestAnimationFrame` により直接 DOM を変更します*
852
-
853
- **パラメータ**
854
- - props.ul : アニメーション対象の <ul> エレメント
855
- - props.duration: 実行時間 (ms)
856
- - props.interval: 待機時間 (ms)
857
- - props.easing : easing 関数
858
-
859
- **戻値**
860
- - start(): アニメーションを開始
861
- - stop() : アニメーションを停止
862
-
863
- ---
864
-
865
- ### InternalEffect
866
- Dispatch の内部処理(finish / action)から呼び出されることを前提としたエフェクト
867
- Action の戻り値としては返されず、Dispatch の実行フロー内で直接実行される
868
- 型としては `Effect<S>` と同一で「Dispatch 内部専用」という役割と設計意図を明示するための型エイリアス
869
-
870
- ```ts
871
- type InternalEffect<S> = Effect<S>
872
- ```
873
-
874
- **説明**
875
-
876
- Dispatch で呼ばれるアクションでは、エフェクトを直接返すことはできません
877
- しかし、非同期処理でエフェクトを使用したいこともあります
878
-
879
- このときアクションを `(state: S) => S | [S, Effect<S>]` とせず `(state: S) => S | [S, InternalEffect<S>]`とすることで、エフェクトが戻り値とならないことを明示します ( Dispatch 内でエフェクトが戻せない罠対策です )
880
- 具体的には `requestAnimationFrame` などを使用して時間差で Dispatch してください
881
-
882
- **ポイント**
883
-
884
- - 戻り値として使用されず、Dispatch 内でのみ実行される
885
- - `Effect<S>` と同一
886
- - 内部専用であることを、型で明示
887
-
888
- **Dispatch例**
889
-
890
- ```ts
891
- return [state, (dispatch: Dispatch) => {
892
- requestAnimationFrame(() =>
893
- dispatch(state: S) => [state, effect]
894
- )
895
- }]
896
- ```
897
- *requestAnimationFrame などで、Dispatch 外部に移動した後、Dispatch します (どうしても1フレーム遅延します)*
898
-
899
- ---
900
-
901
- ### RAFEvent
902
- RAFTask で使用されるイベント
903
- hyperapp で使用される通常のアクション と同一なものですが
904
- `payload` と `Effect` の型名が再定義されています
905
-
906
- ```ts
907
- type RAFEvent = <S> (state: S, rafTask: RAFTask<S>) => S | [S, InternalEffect<S>]
908
- ```
909
-
910
- ---
911
-
912
- ### RAFTask
913
- requestAnimationFrame (rAF) を管理するためのクラス
914
170
 
915
- ```ts
916
- export class RAFTask <S> {
917
- // constructor
918
- constructor (props: {
919
- id : string
920
- groupID ?: string
921
- duration : number
922
- delay ?: number
923
- action : RAFEvent<S>
924
- finish ?: RAFEvent<S>
925
- priority ?: number
926
- extension?: Record<string, any>
927
- })
928
-
929
- // getter
930
- id : string
931
- duration : number
932
- delay : number
933
- action : RAFEvent<S>
934
- finish : RAFEvent<S>
935
- progress : number
936
- deltaTime: number
937
-
938
- // getter / setter
939
- groupID : string | undefined
940
- priority : number
941
- extension: Record<string, any>
942
- isDone : boolean
943
- paused : boolean
944
-
945
- // method
946
- clone: RAFTask<S>
171
+ // vnode
172
+ return button({
173
+ type: "button",
174
+ ...deleteKeys(props, "state", "keyNames"), // DOM に設定しない値を削除
175
+ class : classList.concat("MyButton").join(" "), // 取得したクラス名に MyButton を追加
176
+ onclick: action_click
177
+ }, child)
947
178
  }
948
179
  ```
949
180
 
950
- **constructor**
951
-
952
- 基本
953
- - id : rAF のユニークID
954
- - duration: 1回の実行時間 (ms)
955
- - action : 毎フレームごとのアクション
956
-
957
- 拡張
958
- - groupID : 任意のグループID
959
- - delay : 初回実行までの待機時間 (ms)
960
- - finish : 最終フレームで実行されるアクション
961
- - priority : 処理の優先順位
962
- - extension: 任意の拡張データ
963
-
964
- **property**
965
-
966
- getter
967
- - id : rAF のユニークID
968
- - duration : 1回の実行時間 (ms)
969
- - delay : 初回実行までの待機時間 (ms)
970
- - action : 毎フレームごとのアクション
971
- - finish : 最終フレームで実行されるアクション
972
- - progress : 進捗状況 (0 - 1)
973
- - deltaTime: 前回アクションからの経過時間 (ms)
974
-
975
- getter / setter
976
- - groupID : 任意のグループID
977
- - priority : 処理の優先順位
978
- - extension: 任意の拡張データ
979
- - isDone : 終了状況の取得 / 設定
980
- - paused : 一時停止状況の取得 / 設定
981
-
982
- **method**
983
-
984
- - clone: 時間を初期化したクローンを作成して返す
985
-
986
181
  ---
987
182
 
988
- ### subscription_RAFManager
989
- RAFTask 配列をフレームごとに実行するサブスクリプション
183
+ - effect_toast: 設定した値を、一定時間経過後に元に戻します
990
184
 
185
+
991
186
  ```ts
992
- export const subscription_RAFManager = function <S> (
993
- state : S,
994
- keyNames: string[]
995
- ): Subscription<S>
996
- ```
997
-
998
- - state : ステート
999
- - keyNames: RAFTask 配列までのパス
1000
-
1001
- [詳細説明](animation-system.md)
1002
-
1003
- ---
1004
-
1005
- ### CSSProperty
1006
- CSS設定用オブジェクト
1007
-
1008
- ```ts
1009
- export interface CSSProperty {
1010
- [selector: string]: {
1011
- [name: string]: (progress: number) => string
1012
- }
187
+ const action = (state: S) => {
188
+ return [
189
+ {
190
+ ...state,
191
+ message: "ready"
192
+ },
193
+ effect_toast({
194
+ keyNames: ["message"],
195
+ value : "toast message",
196
+ duration: 1000
197
+ })
198
+ ]
1013
199
  }
1014
200
  ```
1015
201
 
1016
- - selector: セレクター
1017
- - selector.[name] => fn
1018
- - name: CSS プロパティ名
1019
- - fn(progress): CSS 値を計算する関数
1020
-
1021
- ---
1022
-
1023
- ### createUnits
1024
- CSSProperty を変換する補助関数
1025
- selector から、doms を取得してセットにします
1026
-
1027
- ```ts
1028
- export const createUnits = function (
1029
- properties: CSSProperty[]
1030
- ): {
1031
- doms : HTMLElement[],
1032
- styles: {
1033
- [name: string]: (progress: number) => string
1034
- }
1035
- }[]
1036
- ```
1037
-
1038
- - properties: プロパティ配列
202
+ > `message` に 1秒間 "toast message" と表示された後\
203
+ > "ready" という値に戻ります
1039
204
 
1040
205
  ---
1041
206
 
1042
- ### createRAFProperties
1043
- subscription_RAFManager をベースにした CSS アニメーション RAFTask を作成する
1044
-
1045
- ```ts
1046
- export const createRAFProperties = function <S> (
1047
- props: {
1048
- id : string
1049
- groupID?: string
1050
- duration: number
1051
- delay ?: number
1052
-
1053
- finish?: (state: S, rafTask: RAFTask<S>) => S | [S, InternalEffect<S>]
1054
-
1055
- priority ?: number
1056
- extension?: Record<string, any>
207
+ **dom**
1057
208
 
1058
- properties: CSSProperty[]
1059
- }
1060
- ): RAFTask<S>
1061
- ```
209
+ - getScrollMargin: スクロール状態を取得
1062
210
 
1063
- props は、基本的に RAFTask の値
1064
-
1065
- - properties: セレクタとスタイル設定のセット配列
1066
-
1067
- ---
1068
-
1069
- ### effect_RAFProperties
1070
- CSS プロパティをフレーム単位で段階的に変更
211
+ > 無限スクロールなどに利用できます
1071
212
 
213
+ 例:
1072
214
  ```ts
1073
- export const effect_RAFProperties = function <S>(
1074
- props: {
1075
- id : string
1076
- groupID?: string
1077
- duration: number
1078
- delay ?: number
1079
-
1080
- finish?: (state: S, rafTask: RAFTask<S>) => S | [S, InternalEffect<S>]
1081
-
1082
- priority ?: number
1083
- extension?: Record<string, any>
215
+ const action = (state: S, e: Event) => {
216
+ const scrollMargin = getScrollMargin(e)
1084
217
 
1085
- properties: CSSProperty[]
1086
- keyNames : string[]
218
+ if (scrollMargin.bottom < 100) {
219
+ // 残り 100px で、アイテムの追加処理などを実行
220
+ const newState = {
221
+ ...state,
222
+ count: count + 10
223
+ }
224
+ return newState
1087
225
  }
1088
- ): (dispatch : Dispatch<S>) => void
1089
- ```
1090
-
1091
- props は、基本的に RAFTask の値
1092
-
1093
- - properties: セレクタとスタイル設定のセット配列
1094
- - keyNames : RAFTask 配列までのパス
1095
-
1096
- ---
1097
-
1098
- ### TranslateState
1099
- Translate 管理用オブジェクト
1100
226
 
1101
- *実験用コードのため、npm 非公開です*
1102
-
1103
- ```ts
1104
- export interface TranslateState {
1105
- width : number
1106
- index : number
1107
- total : number
1108
- easing: (t: number) => number
227
+ return state
1109
228
  }
1110
229
  ```
1111
230
 
1112
- - width : 移動量
1113
- - index : 先頭のインデックス
1114
- - total : 子の数
1115
- - easing: easing 関数
1116
-
1117
231
  ---
1118
232
 
1119
- ### createRAFTranslate
1120
- subscription_RAFManager をベースにした Translate アニメーション RAFTask を作成する
233
+ - withLoadingDialog: task 実行中、ローディングダイアログを表示します
1121
234
 
1122
- *実験用コードのため、npm 非公開です*
235
+ > onCancel を指定した場合は、Esc キーで onCancel が呼び出されます
1123
236
 
237
+
1124
238
  ```ts
1125
- export const createRAFTranslate = function <S> (
1126
- props: {
1127
- id : string
1128
- groupID?: string
1129
- duration: number
1130
- delay : number
1131
-
1132
- finish?: (state: S, rafTask: RAFTask<S>) => S | [S, InternalEffect<S>]
1133
-
1134
- priority ?: number
1135
- extension?: Record<string, any>
1136
-
1137
- translateState: TranslateState
1138
- }
1139
- ): RAFTask<S>
1140
- ```
1141
-
1142
- - props は、基本的に RAFTask の値
1143
- - translateState: Translate情報
1144
-
1145
- ---
1146
-
1147
- ### effect_translateStart
1148
- `subscription_RAFManager` をベースにした Translate アニメーションエフェクトです
1149
-
1150
- *実験用コードのため、npm 非公開です*
1151
-
1152
- ```ts
1153
- export const effect_translateStart = function <S> (
1154
- props: {
1155
- id : string
1156
- groupID?: string
1157
- duration: number
1158
- delay : number
1159
-
1160
- finish?: (state: S, rafTask: RAFTask<S>) => S | [S, InternalEffect<S>]
1161
-
1162
- priority ?: number
1163
- extension?: Record<string, any>
1164
-
1165
- easing ?: (t: number) => number
1166
-
1167
- keyNames: string[]
1168
- }
1169
- ): (dispatch: Dispatch<S>) => void
1170
- ```
1171
-
1172
- - props は、基本的に RAFTask の値
1173
- - easing : easing 関数
1174
- - keyNames: RAFTask 配列までのパス
1175
-
1176
- **説明**
1177
-
1178
- marquee は単純な DOM に対しての副作用で、Translate としての動作は
1179
- ステート経由で rAF を制御しているこちらに集約されることになります
1180
-
1181
- marquee はステートを通さず直接 DOM に対して副作用を発生させるため
1182
- 用途によっては marquee に優位性があります
1183
-
1184
- - marquee : DOM 直接操作。軽量で即時反映
1185
- - effect_translateStart : Hyperapp のステート経由で管理。RAFManager と連携可能
1186
-
1187
- ### effect_translateRollback
1188
- アニメーション中のTranslateを、元の位置に戻す
1189
-
1190
- *実験用コードのため、npm 非公開です*
1191
-
1192
- ```ts
1193
- export const effect_translateRollback = function <S> (
1194
- props: {
1195
- id : string
1196
- keyNames: string[]
1197
- paused ?: boolean
1198
- finish ?: RAFEvent<S>
1199
- }
1200
- ): (dispatch: Dispatch<S>)
1201
- ```
1202
-
1203
- - id : ユニークID
1204
- - keyNames: RAFTask 配列までのパス
1205
- - paused : 実行後、一時停止するか
1206
- - finish : 実行後に呼び出されるイベント
1207
-
1208
- **注意**
1209
- `effect_translateStart` に依存しています
1210
- 上記エフェクトでループしている id に対して使用します
1211
-
1212
- ---
1213
-
1214
- ### effect_translateRollforward
1215
- アニメーション中のTranslateを、早送りする
1216
-
1217
- *実験用コードのため、npm 非公開です*
1218
-
1219
- ```ts
1220
- export const effect_translateRollforward = function <S> (
1221
- props: {
1222
- id : string
1223
- keyNames: string[]
1224
- paused ?: boolean
1225
- finish ?: RAFEvent<S>
1226
- }
1227
- ): (dispatch: Dispatch<S>)
239
+ const json = await withLoadingDialog(async () => {
240
+ const res = await fetch("/api/hoge")
241
+ if (!res.ok) throw new Error("error: task")
242
+ return res.json()
243
+ })
1228
244
  ```
1229
245
 
1230
- - id : ユニークID
1231
- - keyNames: RAFTask 配列までのパス
1232
- - paused : 実行後、一時停止するか
1233
- - finish : 実行後に呼び出されるイベント
1234
-
1235
- **注意**
1236
- `effect_translateStart` に依存しています
1237
- 上記エフェクトでループしている id に対して使用します
1238
-
1239
246
  ---
1240
247
 
1241
- ### effect_translateSlide
1242
- Translateを任意のインデックスまで移動する
1243
-
1244
- *実験用コードのため、npm 非公開です*
1245
-
1246
- ```ts
1247
- export const effect_translateSlide = function <S> (
1248
- props: {
1249
- id : string
1250
- keyNames: string[]
1251
- index : number
1252
- paused ?: boolean
1253
- finish ?: RAFEvent<S>
1254
- }
1255
- ): (dispatch: Dispatch<S>) => void
1256
- ```
248
+ **animation**
1257
249
 
1258
- - id : ユニークID
1259
- - keyNames: RAFTask 配列までのパス
1260
- - index : 移動先のインデックス
1261
- - paused : 実行後、一時停止するか
1262
- - finish : 実行後に呼び出されるイベント
1263
-
1264
- **注意**
1265
- `effect_translateStart` に依存しています
1266
- 上記エフェクトでループしている id に対して使用します
1267
-
1268
- ---
250
+ - subscription_RAFManager: RAFTask 配列の実行を管理するサブスクリプション
1269
251
 
1270
- ### progress_easing
1271
- easing プリセット
252
+ > ステートに `RAFTask[]` の配列を用意します\
253
+ > `RAFTask` が追加されると、自動的にタスクの実行管理を開始します\
254
+ > タスクが完了すると、自動的に `RAFTask[]` から削除されます
1272
255
 
256
+ 例:
1273
257
  ```ts
1274
- export const progress_easing = {
1275
-
1276
- // basic
1277
- linear : (t: number) => t,
1278
- easeInQuad : (t: number) => t * t,
1279
- easeOutQuad : (t: number) => 1 - (1 - t) * (1 - t),
1280
- easeInOutQuad: (t: number) => t < 0.5
1281
- ? 2 * t * t
1282
- : 1 - Math.pow(-2 * t + 2, 2) / 2,
1283
-
1284
- // cubic
1285
- easeInCubic : (t: number) => t * t * t,
1286
- easeOutCubic : (t: number) => 1 - Math.pow(1 - t, 3),
1287
- easeInOutCubic: (t: number) => t < 0.5
1288
- ? 4 * t * t * t
1289
- : 1 - Math.pow(-2 * t + 2, 3) / 2,
1290
-
1291
- // quart
1292
- easeInQuart : (t: number) => t * t * t * t,
1293
- easeOutQuart : (t: number) => 1 - Math.pow(1 - t, 4),
1294
- easeInOutQuart: (t: number) => t < 0.5
1295
- ? 8 * t * t * t * t
1296
- : 1 - Math.pow(-2 * t + 2, 4) / 2,
1297
-
1298
- // back (跳ねる)
1299
- easeOutBack: (t: number) => {
1300
- const c1 = 1.70158
1301
- const c3 = c1 + 1
1302
- return 1 + c3 * Math.pow(t - 1, 3) + c1 * Math.pow(t - 1, 2)
1303
- },
1304
-
1305
- // bounce
1306
- easeOutBounce: (t: number) => {
1307
- const n1 = 7.5625
1308
- const d1 = 2.75
1309
-
1310
- if (t < 1 / d1) {
1311
- return n1 * t * t
1312
- } else if (t < 2 / d1) {
1313
- return n1 * (t -= 1.5 / d1) * t + 0.75
1314
- } else if (t < 2.5 / d1) {
1315
- return n1 * (t -= 2.25 / d1) * t + 0.9375
1316
- } else {
1317
- return n1 * (t -= 2.625 / d1) * t + 0.984375
258
+ app({
259
+ node,
260
+ init: [
261
+ {
262
+ tasks : [] as RAFTask[],
263
+ message: ""
264
+ },
265
+ (dispatch: S) => {
266
+ const newTask = new RAFTask({
267
+ id : "uniqueID",
268
+ duration: 2000,
269
+ action : (state: S, task: RAFTask<S>) => {
270
+ return {
271
+ ...state,
272
+ message: `progress = ${ task.progress }`
273
+ }
274
+ }
275
+ })
1318
276
  }
1319
- },
1320
-
1321
- // elastic
1322
- easeOutElastic: (t: number) => {
1323
- const c4 = (2 * Math.PI) / 3
1324
-
1325
- return t === 0
1326
- ? 0
1327
- : t === 1
1328
- ? 1
1329
- : Math.pow(2, -10 * t) * Math.sin((t * 10 - 0.75) * c4) + 1
1330
- }
1331
- }
1332
- ```
1333
-
1334
- ## hyperapp-is/animationView
1335
-
1336
- ### CarouselState
1337
- Carousel コンポーネント情報
1338
- RAFTask.extension に保存されます
1339
-
1340
- ```ts
1341
- export interface CarouselState <S> {
1342
- id : string
1343
- step: number
1344
-
1345
- // option
1346
- groupID ?: string
1347
- duration ?: number
1348
- delay ?: number
1349
- priority ?: number
1350
- extension?: Record<string, any>
1351
-
1352
- // event
1353
- action?: RAFEvent<S>
1354
- finish?: RAFEvent<S>
1355
-
1356
- // animation
1357
- easing?: (t: number) => number
1358
-
1359
- // report
1360
- reportPageIndex?: string[]
1361
- }
1362
- ```
1363
- 基本的には `RAFTask` に準拠します
1364
- カルーセルを動作せるため `effect_InitCarousel` に渡すための値です
1365
-
1366
- 必須項目
1367
- - id: `Carousel` コンポーネントの id を指定してください
1368
- - step: 移動するアイテムの数で、負の数は逆回転。0 は停止となります
1369
-
1370
- 拡張項目
1371
- - easing: アニメーションの動作を指定する関数
1372
- 指定しない場合は `(t: number) => t` (linear) となります
1373
-
1374
- - reportPagreIndex: 現在一番左端に表示されているアイテムのインデックスを受けるパス
1375
- コントロールバーを自作したい時などに利用できます
1376
-
1377
- ---
1378
-
1379
- ### CarouselController
1380
- 外部から Carousel コンポーネントを操作するためのクラス
1381
- RAFTask.extension に保存されます
1382
-
1383
- ```ts
1384
- export interface CarouselController <S> {
1385
- step : (rafTask: RAFTask<S>, delta: number, skipSpeedRate?: number) => Promise <RAFTask<S>>
1386
- moveTo: (rafTask: RAFTask<S>, index: number, skipSpeedRage?: number) => Promise <RAFTask<S>>
1387
- }
1388
- ```
1389
-
1390
- - step: `delta` で指定した方向にページを移動します (相対値)
1391
- スキップ時のスピードは、変更前の `duration` に `skipSpeedRate` を乗じたものになります
1392
- 終了時に処理を割り込ませたい場合は、`Promsie` により処理します
1393
-
1394
- - moveTo: `index` で指定したページに移動します (絶対値)
1395
- スキップ時のスピードは、変更前の `duration` に `skipSpeedRate` を乗じたものになります
1396
- 終了時に処理を割り込ませたい場合は、`Promsie` により処理します
1397
-
1398
- ### Carousel
1399
- カルーセルコンポーネントです
1400
- `CarouselState` を引数に `effect_InitCarousel` を実行することで動作開始します
1401
- `subscription_RAFManager` を使用するので、予め `subscriptions` に追加してください
1402
-
1403
- ```ts
1404
- export const Carousel = function <S> (
1405
- props: {
1406
- state : S
1407
- id : string
1408
- keyNames : string[]
1409
- controlButton?: boolean
1410
- controlBar ?: boolean
1411
- skipSpeedRate?: number
1412
- [key: string] : any
1413
- },
1414
- children: any
1415
- ): VNode<S>
1416
- ```
1417
-
1418
- - state : ステート
1419
- - id : ユニークID (DOM の id)
1420
- - keyNames : RAFTask 配列までのパス
1421
- - controlButton: 操作ボタンを表示するか (未実装)
1422
- - controlBar : 操作バーを表示するか
1423
- - skipSpeedRate: スキップ時の動作速度
1424
-
1425
- ### effect_InitCarousel
1426
- カルーセルを初期化し起動するエフェクト
1427
-
1428
- ```ts
1429
- export const effect_InitCarousel = function <S> (
1430
- keyNames : string[],
1431
- carouselState: CarouselState<S>
1432
- ): (dispatch: Dispatch<S>) => void
277
+ ],
278
+ view,
279
+ subscription: (state: S) => [
280
+ subscription_RAFManager(state, ["tasks"])
281
+ ]
282
+ })
1433
283
  ```
1434
284
 
1435
- - keyNames : RAFTask 配列までのパス
1436
- - carouselState: カルーセルの動作設定
285
+ `RAFTask` クラスの作成には、最低限次の項目を設定する必要があります
1437
286
 
1438
- ## hyperapp-is/dom
1439
-
1440
- ### ScrollMargin
1441
- スクロールの余白を管理するオブジェクト
1442
-
1443
- ```ts
1444
- export interface ScrollMargin {
1445
- top : number
1446
- left : number
1447
- right : number
1448
- bottom: number
1449
- }
1450
- ```
1451
-
1452
- - top : 上までの余白
1453
- - left : 左までの余白
1454
- - right : 右までの余白
1455
- - bottom: 下までの余白
287
+ - id: タスク管理用のユニークIDを設定してください。DOMのIDとは関係ありません
288
+ - duration: 実行時間をミリ秒で指定してください
289
+ - action: 1フレームごとの処理内容を指定します。進捗状況は `RAFTask.progress` から取得できます
1456
290
 
1457
291
  ---
1458
292
 
1459
- ### getScrollMargin
1460
- スクロールの余白を取得
293
+ - effect_RAFProperties: RAFTask を利用して CSS プロパティをアニメーションする Effect
1461
294
 
1462
- ```ts
1463
- export const getScrollMargin = function (e: Event): ScrollMargin
1464
- ```
1465
-
1466
- - e: イベント
1467
-
1468
- ---
1469
-
1470
- ### MatrixState
1471
- transform 情報
295
+ > CSSProperty により、複数の CSS アニメーションを同時処理することが可能です
1472
296
 
297
+ 例:
1473
298
  ```ts
1474
- export interface MatrixState {
1475
- translate: {
1476
- x: number
1477
- y: number
1478
- z: number
1479
- }
1480
-
1481
- scale: {
1482
- x: number
1483
- y: number
1484
- z: number
1485
- }
299
+ const action = (state: S) => {
300
+ const property: CSSProperty = {
301
+ "button": {
302
+ "fontSize": (progress: number) => `${ 1 + 1 * progress }rem`,
303
+ "color" : (progress: number) => `rgb(${ 255 * progress }, 0, 0)`
304
+ }
305
+ }
1486
306
 
1487
- // radian
1488
- rotate: {
1489
- x: number
1490
- y: number
1491
- z: number
307
+ return [state, effect_RAFProperties({
308
+ id : "uniqueID",
309
+ duration : 1000,
310
+ properties: [ property ]
311
+ })]
1492
312
  }
1493
- }
1494
- ```
1495
-
1496
- ---
1497
-
1498
- ### getMatrixState
1499
- DOM から transfrom 情報を取得する
1500
-
1501
- ```ts
1502
- export const getMatrixState = (
1503
- dom: HTMLElement
1504
- ): MatrixState | null
1505
313
  ```
1506
314
 
1507
- - dom : 情報を取得する DOM
315
+ > すべてのボタンに対して、1秒かけて文字サイズを拡大し、文字色を赤へ変化させるアニメーションのサンプルです
1508
316
 
1509
317
  ---
1510
318
 
1511
- ### effect_setTimedValue
1512
- ステートに存在時間制限付きの値を設定
319
+ **services**
1513
320
 
1514
- *実験用コードのため、npm 非公開です*
321
+ - GoogleAuth: Google Identity Services を利用した Google 認証クラス
1515
322
 
1516
- ```ts
1517
- export const effect_setTimedValue = function <S, T> (
1518
- keyNames: string[],
1519
- id : string,
1520
- timeout : number,
1521
- value : T,
1522
- reset : T | null = null
1523
- ): (dispatch: Dispatch<S>) => void
1524
- ```
1525
-
1526
- - keyNames: 値までのパス
1527
- - id : ユニークID
1528
- - timeout : 存在可能時間 (ms)
1529
- - value : 一時的に設定する値
1530
- - reset : タイムアウト後に設定する値
1531
-
1532
- ---
1533
-
1534
- ### effect_nodesInitialize
1535
- VNode マウント後の初期化処理を実行
1536
-
1537
- *実験用コードのため、npm 非公開です*
323
+ > ログインボタンを作成しない場合は、`new GoogleAuth({ clientId: "hoge" })` だけで利用できます\
324
+ > インスタンス作成後、`initialize` を実行すると認証処理を開始します
1538
325
 
326
+
1539
327
  ```ts
1540
- export const effect_nodesInitialize = function <S> (
1541
- nodes: {
1542
- id : string
1543
- event: (state: S, element: Element) => S | [S, Effect<S>]
1544
- }[]
1545
- ): (dispatch: Dispatch<S>) => void
1546
- ```
1547
-
1548
- - nodes : 初期化対象ノード定義配列
1549
- - nodes.id : ユニークID
1550
- - nodes.event: 初期化イベント
1551
-
1552
- ---
1553
-
1554
- ### subscription_nodesCleanup
1555
- DOM 消失時にクリーンアップ処理を実行
1556
-
1557
- *実験用コードのため、npm 非公開です*
1558
-
1559
- ```ts
1560
- export const subscription_nodesCleanup = function <S>(
1561
- nodes: {
1562
- id : string
1563
- finalize: (state: S) => S | [S, Effect<S>]
1564
- }[]
1565
- ): Subscription<S>[]
1566
- ```
1567
-
1568
- - nodes : クリーンアップ対象ノード定義配列
1569
- - nodes.id : ユニークID
1570
- - nodes.finalize: 終了時イベント
1571
-
1572
- ---
1573
-
1574
- ### subscription_nodesLifecycleByIds
1575
- ステート上の ID 配列変化に応じて initialize / finalize を自動管理
1576
-
1577
- *実験用コードのため、npm 非公開です*
1578
-
1579
- ```ts
1580
- export const subscription_nodesLifecycleByIds = function <S> (
1581
- keyNames: string[],
1582
- nodes: {
1583
- id : string
1584
- initialize: (state: S, element: Element | null) => S | [S, Effect<S>]
1585
- finalize : (state: S, element: Element | null) => S | [S, Effect<S>]
1586
- }[]
1587
- ): Subscription<S>[]
1588
- ```
1589
-
1590
- - keyNames : 文字配列までのパス
1591
- - nodes : 監視対象ノード定義配列
1592
- - nodes.id : ユニークID
1593
- - nodes.initialize: 初期化イベント
1594
- - nodes.finalize : 終了時イベント
1595
-
1596
- ---
1597
-
1598
- ### getGoogle
1599
- Google GIS 変数を取得する
1600
-
1601
- ```ts
1602
- export const getGoogle = async (): Promise<Google>
1603
- ```
1604
-
1605
- `<script src="https://accounts.google.com/gsi/client">` を読み込み、`window.google`の値(Promise結果)返却
1606
-
1607
- ---
1608
-
1609
- ### getGoogleAuthResult
1610
- ユーザートークンから、ユーザー情報を取得する
1611
-
1612
- ```ts
1613
- export const getGoogleAuthResult = (idToken: string): GoogleAuthResult
1614
- ---
1615
-
1616
- ### getAccessToken
1617
- Googleのaccess token を取得
1618
-
1619
- ```ts
1620
- export const getAccessToken = (config: GetAccessTokenConfig): Promise<string>
1621
- ```
1622
-
1623
- ```ts
1624
- export interface GetAccessTokenConfig {
1625
- clientId: string
1626
- scope : GoogleScope[]
1627
- prompt ?: "none" | "select_account" | "consent" // default: "select_account"
328
+ const config: GoogleAuthConfig = {
329
+ clientId: "your google api key"
1628
330
  }
1629
- ```
1630
331
 
1631
- - clientId: アプリケーションのID
1632
- - scope : スコープの配列
1633
- - prompt ?: ログイン制御 `none` の場合、先にログインしていなければエラーとなります
1634
-
1635
- ---
1636
-
1637
- ### GoogleAuth
1638
- Google 認証をラップしたクラス
1639
-
1640
- ```ts
1641
- interface GoogleAuthConfig {
1642
- clientId : string
1643
- autoSelect?: boolean // default: true
1644
- uxMode ?: "popup" | "redirect" // default: "popup"
332
+ const option: GoogleButtonOption = {
333
+ renderButton: document.querySelector("article")
1645
334
  }
1646
335
 
1647
- interface GoogleButtonOptions {
1648
- renderButton?: HTMLElement
1649
- theme ?: "outline" | "filled_blue" | "filled_black" // default: outline
1650
- size ?: "large" | "medium" | "small" // default: medium
1651
- text ?: "signin_with" | "signup_with" | "continue_with" // default: signin_with
1652
- shape ?: "rectangular" | "pill" | "circle" | "square" // default: rectangular
1653
- }
336
+ const auth = new GoogleAuth(config, option)
1654
337
 
1655
- interface GoogleAuthResult {
1656
- idToken: string
1657
- user : GoogleUser
1658
- }
338
+ auth.initialize().then((res: GoogleAuthResult) => {
339
+ const idToken = res.idToken
340
+ const user = res.user
1659
341
 
1660
- interface GoogleUser {
1661
- name : string
1662
- email : string
1663
- picture: string
1664
- sub : string
1665
- }
1666
- ```
342
+ alert("ログイン成功")
343
+ })
1667
344
 
1668
- ```ts
1669
- class GoogleAuth {
1670
- constructor (config: GoogleAuthConfig, option?: GoogleButtonOptions)
1671
- async initialize(): Promise<GoogleAuthResult | null>
1672
- logout (hit: string): void
1673
- }
1674
345
  ```
1675
-
1676
- *MEMO*
1677
- 必ず一度だけ `initialize` を実行します
1678
- プロンプトがスキップされた場合 `null` が返ります
1679
- `logout` を実行したあとであれば `initialize` を再実行することが可能です
1680
- `logout` 前に 2度 `initialize` を実行した場合は、エラーが発生します
1681
- `logout` の `hit` は `email' か 'sub` を指定する必要があります
1682
-