hyperapp-is 0.1.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.
- package/LICENSE +21 -0
- package/README.md +1254 -0
- package/README.npm.md +107 -0
- package/dist/hyperapp-is/animation/easing.d.ts +15 -0
- package/dist/hyperapp-is/animation/easing.js +57 -0
- package/dist/hyperapp-is/animation/index.d.ts +5 -0
- package/dist/hyperapp-is/animation/index.js +3 -0
- package/dist/hyperapp-is/animation/properties.d.ts +64 -0
- package/dist/hyperapp-is/animation/properties.js +108 -0
- package/dist/hyperapp-is/animation/raf.d.ts +71 -0
- package/dist/hyperapp-is/animation/raf.js +202 -0
- package/dist/hyperapp-is/animationView/carousel.d.ts +95 -0
- package/dist/hyperapp-is/animationView/carousel.js +472 -0
- package/dist/hyperapp-is/animationView/index.d.ts +2 -0
- package/dist/hyperapp-is/animationView/index.js +1 -0
- package/dist/hyperapp-is/core/component.d.ts +95 -0
- package/dist/hyperapp-is/core/component.js +193 -0
- package/dist/hyperapp-is/core/index.d.ts +2 -0
- package/dist/hyperapp-is/core/index.js +2 -0
- package/dist/hyperapp-is/core/state.d.ts +54 -0
- package/dist/hyperapp-is/core/state.js +118 -0
- package/dist/hyperapp-is/dom/index.d.ts +2 -0
- package/dist/hyperapp-is/dom/index.js +1 -0
- package/dist/hyperapp-is/dom/utils.d.ts +68 -0
- package/dist/hyperapp-is/dom/utils.js +159 -0
- package/dist/hyperapp-is/index.d.ts +7 -0
- package/dist/hyperapp-is/index.js +5 -0
- package/package.json +40 -0
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
// hyperapp-ui / core / component.ts
|
|
2
|
+
import { h, text } from "hyperapp";
|
|
3
|
+
import { getValue, setValue } from "./state";
|
|
4
|
+
// ========== ========== ========== ========== ==========
|
|
5
|
+
// 補助関数
|
|
6
|
+
// ========== ========== ========== ========== ==========
|
|
7
|
+
// ---------- ---------- ---------- ---------- ----------
|
|
8
|
+
// el
|
|
9
|
+
// ---------- ---------- ---------- ---------- ----------
|
|
10
|
+
/**
|
|
11
|
+
* h 関数のラッパー
|
|
12
|
+
* 他でjsxを使用した場合、hが競合する可能性があるので作成した
|
|
13
|
+
*
|
|
14
|
+
* @template S
|
|
15
|
+
* @param {string} tag - タグ名
|
|
16
|
+
* @returns {(props:{ [key: string] any }, ...children:any[]) => VNode<S>}
|
|
17
|
+
*/
|
|
18
|
+
export const el = (tag) => (props, ...children) => h(tag, props !== null && props !== void 0 ? props : {}, children
|
|
19
|
+
.flat()
|
|
20
|
+
.map((child) => typeof child === "object" ? child : text(child)));
|
|
21
|
+
/* element */
|
|
22
|
+
const button = el("button");
|
|
23
|
+
// ---------- ---------- ---------- ---------- ----------
|
|
24
|
+
// concatAction
|
|
25
|
+
// ---------- ---------- ---------- ---------- ----------
|
|
26
|
+
/**
|
|
27
|
+
* アクションを結合して結果を返す
|
|
28
|
+
*
|
|
29
|
+
* @template S
|
|
30
|
+
* @template E
|
|
31
|
+
* @param {undefined | (state: S, e: E) => S | [S, Effect<S>]} action - 結合するアクション
|
|
32
|
+
* @param {S} newState - 結合するステート
|
|
33
|
+
* @param {E} e - イベント (任意のイベント型)
|
|
34
|
+
* @returns {S | [S, Effect<S>]}
|
|
35
|
+
*/
|
|
36
|
+
export const concatAction = function (action, newState, e) {
|
|
37
|
+
if (!action)
|
|
38
|
+
return newState;
|
|
39
|
+
const effect = (dispatch) => {
|
|
40
|
+
// 次の描画を待たないと、newStateと同時にdispatchが走ってしまい、DOMが存在しない可能性がある
|
|
41
|
+
// effect_initializeNodesを機能させるため、dispatch を描画後まで保留する
|
|
42
|
+
requestAnimationFrame(() => {
|
|
43
|
+
dispatch((state) => action(state, e));
|
|
44
|
+
});
|
|
45
|
+
};
|
|
46
|
+
return [newState, effect];
|
|
47
|
+
};
|
|
48
|
+
// ---------- ---------- ---------- ---------- ----------
|
|
49
|
+
// getClassList
|
|
50
|
+
// ---------- ---------- ---------- ---------- ----------
|
|
51
|
+
/**
|
|
52
|
+
* props から classList を取得
|
|
53
|
+
*
|
|
54
|
+
* @param {Record<string, any>} props - props
|
|
55
|
+
* @returns {string[]}
|
|
56
|
+
*/
|
|
57
|
+
export const getClassList = (props) => {
|
|
58
|
+
return props.class
|
|
59
|
+
? props.class.trim().split(" ").filter(Boolean)
|
|
60
|
+
: [];
|
|
61
|
+
};
|
|
62
|
+
// ---------- ---------- ---------- ---------- ----------
|
|
63
|
+
// deleteKeys
|
|
64
|
+
// ---------- ---------- ---------- ---------- ----------
|
|
65
|
+
/**
|
|
66
|
+
* props から不要なキーを削除する
|
|
67
|
+
*
|
|
68
|
+
* @template T
|
|
69
|
+
* @param {T} props - props
|
|
70
|
+
* @param {(keyof T)[])} keys - 削除するキー
|
|
71
|
+
* @returns {Omit<T, (typeof keys)[number]>}
|
|
72
|
+
*/
|
|
73
|
+
export const deleteKeys = (props, ...keys) => {
|
|
74
|
+
const result = { ...props };
|
|
75
|
+
keys.forEach(key => delete result[key]);
|
|
76
|
+
return result;
|
|
77
|
+
};
|
|
78
|
+
// ========== ========== ========== ========== ==========
|
|
79
|
+
// コンポーネント
|
|
80
|
+
// ========== ========== ========== ========== ==========
|
|
81
|
+
// ---------- ---------- ---------- ---------- ----------
|
|
82
|
+
// Route
|
|
83
|
+
// ---------- ---------- ---------- ---------- ----------
|
|
84
|
+
/**
|
|
85
|
+
* ステート内の文字とmatchした時、VNodeを返す
|
|
86
|
+
*
|
|
87
|
+
* @template S
|
|
88
|
+
* @param {Record<string, any>} props - props
|
|
89
|
+
* @param {S} props.state - ステート
|
|
90
|
+
* @param {string[]} props.keyNames - ステート内の文字までのパス
|
|
91
|
+
* @param {string} props.match - 一致判定する文字
|
|
92
|
+
* @param {any} children - 出力する内容 (VNode / 配列 / 文字など)
|
|
93
|
+
* @returns {VNode<S> | null}
|
|
94
|
+
*/
|
|
95
|
+
export const Route = function (props, children) {
|
|
96
|
+
const { state, keyNames, match } = props;
|
|
97
|
+
const selectedName = getValue(state, keyNames, "");
|
|
98
|
+
// nullの場合、VNodeは生成されない
|
|
99
|
+
return selectedName === match ? children : null;
|
|
100
|
+
};
|
|
101
|
+
// ---------- ---------- ---------- ---------- ----------
|
|
102
|
+
// SelectButton
|
|
103
|
+
// ---------- ---------- ---------- ---------- ----------
|
|
104
|
+
const REVERSE_PREFIX = "r_";
|
|
105
|
+
/**
|
|
106
|
+
* クリックで、クラス名のselectをトグルするボタン
|
|
107
|
+
*
|
|
108
|
+
* @template S
|
|
109
|
+
* @param {Record<string, any>} props - props
|
|
110
|
+
* @param {S} props.state - ステート
|
|
111
|
+
* @param {string[]} props.keyNames - ステート内の文字配列までのパス
|
|
112
|
+
* @param {string} props.id - ユニークID
|
|
113
|
+
* @param {boolean} [props.reverse] - 反転選択するか
|
|
114
|
+
* @param {any} children - 子要素 (VNode / string / 配列など)
|
|
115
|
+
* @returns {VNode<S>}
|
|
116
|
+
*/
|
|
117
|
+
export const SelectButton = function (props, children) {
|
|
118
|
+
const { state, keyNames, id, reverse = false } = props;
|
|
119
|
+
// classList
|
|
120
|
+
const classList = getClassList(props).filter(item => {
|
|
121
|
+
const name = item.toLowerCase();
|
|
122
|
+
return name !== "select" && name !== "reverse";
|
|
123
|
+
});
|
|
124
|
+
const selectedNames = getValue(state, keyNames, []);
|
|
125
|
+
if (selectedNames.includes(id))
|
|
126
|
+
classList.push("select");
|
|
127
|
+
if (selectedNames.includes(`${REVERSE_PREFIX}${id}`))
|
|
128
|
+
classList.push("reverse");
|
|
129
|
+
// action
|
|
130
|
+
const action = (state, e) => {
|
|
131
|
+
const selectedNames = getValue(state, keyNames, []);
|
|
132
|
+
const newList = selectedNames.includes(id)
|
|
133
|
+
? reverse
|
|
134
|
+
? selectedNames.filter(item => item !== id).concat(`${REVERSE_PREFIX}${id}`)
|
|
135
|
+
: selectedNames.filter(item => item !== id)
|
|
136
|
+
: selectedNames.includes(`${REVERSE_PREFIX}${id}`)
|
|
137
|
+
? selectedNames.filter(item => item !== `${REVERSE_PREFIX}${id}`)
|
|
138
|
+
: selectedNames.concat(id);
|
|
139
|
+
const newState = setValue(state, keyNames, newList);
|
|
140
|
+
return concatAction(props.onclick, newState, e);
|
|
141
|
+
};
|
|
142
|
+
// VNode
|
|
143
|
+
return button({
|
|
144
|
+
type: "button",
|
|
145
|
+
...deleteKeys(props, "state", "keyNames", "reverse"),
|
|
146
|
+
class: classList.join(" "),
|
|
147
|
+
onclick: action
|
|
148
|
+
}, children);
|
|
149
|
+
};
|
|
150
|
+
// ---------- ---------- ---------- ---------- ----------
|
|
151
|
+
// OptionButton
|
|
152
|
+
// ---------- ---------- ---------- ---------- ----------
|
|
153
|
+
/**
|
|
154
|
+
* クリックで、クラス名のselectを排他的に選択するボタン
|
|
155
|
+
*
|
|
156
|
+
* @template S
|
|
157
|
+
* @param {Record<string, any>} props - props
|
|
158
|
+
* @param {S} props.state - ステート
|
|
159
|
+
* @param {string[]} props.keyNames - ステート内の文字までのパス
|
|
160
|
+
* @param {string} props.id - ユニークID
|
|
161
|
+
* @param {boolean} [props.reverse] - 反転選択するか
|
|
162
|
+
* @param {any} children - 子要素 (VNode / string / 配列など)
|
|
163
|
+
* @returns {VNode<S>}
|
|
164
|
+
*/
|
|
165
|
+
export const OptionButton = function (props, children) {
|
|
166
|
+
const { state, keyNames, id, reverse = false } = props;
|
|
167
|
+
// classList
|
|
168
|
+
const classList = getClassList(props).filter(item => {
|
|
169
|
+
const name = item.toLowerCase();
|
|
170
|
+
return name !== "select" && name !== "reverse";
|
|
171
|
+
});
|
|
172
|
+
const selectedName = getValue(state, keyNames, "");
|
|
173
|
+
if (selectedName === id)
|
|
174
|
+
classList.push("select");
|
|
175
|
+
if (selectedName === `${REVERSE_PREFIX}${id}`)
|
|
176
|
+
classList.push("reverse");
|
|
177
|
+
// action
|
|
178
|
+
const action = (state, e) => {
|
|
179
|
+
const selectedName = getValue(state, keyNames, "");
|
|
180
|
+
const newValue = selectedName === id && reverse
|
|
181
|
+
? `${REVERSE_PREFIX}${id}`
|
|
182
|
+
: id;
|
|
183
|
+
const newState = setValue(state, keyNames, newValue);
|
|
184
|
+
return concatAction(props.onclick, newState, e);
|
|
185
|
+
};
|
|
186
|
+
// VNode
|
|
187
|
+
return button({
|
|
188
|
+
type: "button",
|
|
189
|
+
...deleteKeys(props, "state", "keyNames", "reverse"),
|
|
190
|
+
class: classList.join(" "),
|
|
191
|
+
onclick: action
|
|
192
|
+
}, children);
|
|
193
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* パスを辿って、ステートから値を取得する
|
|
3
|
+
*
|
|
4
|
+
* @template S
|
|
5
|
+
* @template D
|
|
6
|
+
* @param {S} state - ステート
|
|
7
|
+
* @param {string[]} keyNames - 値までのパス
|
|
8
|
+
* @param {D} def - デフォルト値
|
|
9
|
+
* @returns {D} - 型保証は呼び出し側の責任
|
|
10
|
+
*/
|
|
11
|
+
export declare const getValue: <S, D>(state: S, keyNames: string[], def: D) => D;
|
|
12
|
+
/**
|
|
13
|
+
* パスを辿って、ステートに値を設定して返す
|
|
14
|
+
*
|
|
15
|
+
* @template S
|
|
16
|
+
* @param {S} state - ステート
|
|
17
|
+
* @param {string[]} keyNames - 値までのパス
|
|
18
|
+
* @param {any} value - 設定する値
|
|
19
|
+
* @returns {S}
|
|
20
|
+
*/
|
|
21
|
+
export declare const setValue: <S>(state: S, keyNames: string[], value: any) => S;
|
|
22
|
+
/**
|
|
23
|
+
* IDからユニーク文字列を作成する
|
|
24
|
+
*
|
|
25
|
+
* @param {string} id - ユニークID
|
|
26
|
+
* @returns {string}
|
|
27
|
+
*/
|
|
28
|
+
export declare const createLocalKey: (id: string) => string;
|
|
29
|
+
/**
|
|
30
|
+
* ステートから、ローカルステートを取得する
|
|
31
|
+
*
|
|
32
|
+
* @template S
|
|
33
|
+
* @param {S} state - ステート
|
|
34
|
+
* @param {string} id - ユニークID
|
|
35
|
+
* @param {Record<string, any>} def - 初期値
|
|
36
|
+
* @returns {Record<string, any>}
|
|
37
|
+
*/
|
|
38
|
+
export declare const getLocalState: <S>(state: S, id: string, def: {
|
|
39
|
+
[key: string]: any;
|
|
40
|
+
}) => {
|
|
41
|
+
[key: string]: any;
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* ローカルステートを更新してステートを返す
|
|
45
|
+
*
|
|
46
|
+
* @template S
|
|
47
|
+
* @param {S} state - ステート
|
|
48
|
+
* @param {string} id - ユニークID
|
|
49
|
+
* @param {Record<string, any>} value - 設定するローカルステート
|
|
50
|
+
* @returns {S}
|
|
51
|
+
*/
|
|
52
|
+
export declare const setLocalState: <S>(state: S, id: string, value: {
|
|
53
|
+
[key: string]: any;
|
|
54
|
+
}) => S;
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
// hyperapp-ut / core / state.ts
|
|
2
|
+
// ---------- ---------- ---------- ---------- ----------
|
|
3
|
+
// getValue
|
|
4
|
+
// ---------- ---------- ---------- ---------- ----------
|
|
5
|
+
/**
|
|
6
|
+
* パスを辿って、ステートから値を取得する
|
|
7
|
+
*
|
|
8
|
+
* @template S
|
|
9
|
+
* @template D
|
|
10
|
+
* @param {S} state - ステート
|
|
11
|
+
* @param {string[]} keyNames - 値までのパス
|
|
12
|
+
* @param {D} def - デフォルト値
|
|
13
|
+
* @returns {D} - 型保証は呼び出し側の責任
|
|
14
|
+
*/
|
|
15
|
+
export const getValue = function (state, keyNames, def) {
|
|
16
|
+
let result = state;
|
|
17
|
+
for (const key of keyNames) {
|
|
18
|
+
if (result == null ||
|
|
19
|
+
typeof result !== "object")
|
|
20
|
+
return def;
|
|
21
|
+
if (Object.prototype.hasOwnProperty.call(result, key)) {
|
|
22
|
+
result = result[key];
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
return def;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return result;
|
|
29
|
+
};
|
|
30
|
+
// ---------- ---------- ---------- ---------- ----------
|
|
31
|
+
// setValue
|
|
32
|
+
// ---------- ---------- ---------- ---------- ----------
|
|
33
|
+
/**
|
|
34
|
+
* パスを辿って、ステートに値を設定して返す
|
|
35
|
+
*
|
|
36
|
+
* @template S
|
|
37
|
+
* @param {S} state - ステート
|
|
38
|
+
* @param {string[]} keyNames - 値までのパス
|
|
39
|
+
* @param {any} value - 設定する値
|
|
40
|
+
* @returns {S}
|
|
41
|
+
*/
|
|
42
|
+
export const setValue = function (state, keyNames, value) {
|
|
43
|
+
let result = { ...state };
|
|
44
|
+
let current = result;
|
|
45
|
+
for (let i = 0; i < keyNames.length; i++) {
|
|
46
|
+
const key = keyNames[i];
|
|
47
|
+
if (Object.prototype.hasOwnProperty.call(current, key) &&
|
|
48
|
+
current[key] != null &&
|
|
49
|
+
typeof current[key] === "object") {
|
|
50
|
+
current[key] = { ...current[key] };
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
current[key] = {};
|
|
54
|
+
}
|
|
55
|
+
if (keyNames.length - 1 === i) {
|
|
56
|
+
current[key] = value;
|
|
57
|
+
}
|
|
58
|
+
current = current[key];
|
|
59
|
+
}
|
|
60
|
+
return result;
|
|
61
|
+
};
|
|
62
|
+
// ---------- ---------- ---------- ---------- ----------
|
|
63
|
+
// createLocalKey
|
|
64
|
+
// ---------- ---------- ---------- ---------- ----------
|
|
65
|
+
/**
|
|
66
|
+
* IDからユニーク文字列を作成する
|
|
67
|
+
*
|
|
68
|
+
* @param {string} id - ユニークID
|
|
69
|
+
* @returns {string}
|
|
70
|
+
*/
|
|
71
|
+
export const createLocalKey = (id) => `local_key_${id}`;
|
|
72
|
+
// ---------- ---------- ---------- ---------- ----------
|
|
73
|
+
// getLocalState
|
|
74
|
+
// ---------- ---------- ---------- ---------- ----------
|
|
75
|
+
/**
|
|
76
|
+
* ステートから、ローカルステートを取得する
|
|
77
|
+
*
|
|
78
|
+
* @template S
|
|
79
|
+
* @param {S} state - ステート
|
|
80
|
+
* @param {string} id - ユニークID
|
|
81
|
+
* @param {Record<string, any>} def - 初期値
|
|
82
|
+
* @returns {Record<string, any>}
|
|
83
|
+
*/
|
|
84
|
+
export const getLocalState = function (state, id, def) {
|
|
85
|
+
const localKey = createLocalKey(id);
|
|
86
|
+
const obj = Object.prototype.hasOwnProperty.call(state, localKey)
|
|
87
|
+
? state[localKey]
|
|
88
|
+
: {};
|
|
89
|
+
return {
|
|
90
|
+
...def,
|
|
91
|
+
...obj
|
|
92
|
+
};
|
|
93
|
+
};
|
|
94
|
+
// ---------- ---------- ---------- ---------- ----------
|
|
95
|
+
// setLocalState
|
|
96
|
+
// ---------- ---------- ---------- ---------- ----------
|
|
97
|
+
/**
|
|
98
|
+
* ローカルステートを更新してステートを返す
|
|
99
|
+
*
|
|
100
|
+
* @template S
|
|
101
|
+
* @param {S} state - ステート
|
|
102
|
+
* @param {string} id - ユニークID
|
|
103
|
+
* @param {Record<string, any>} value - 設定するローカルステート
|
|
104
|
+
* @returns {S}
|
|
105
|
+
*/
|
|
106
|
+
export const setLocalState = function (state, id, value) {
|
|
107
|
+
const localKey = createLocalKey(id);
|
|
108
|
+
const obj = Object.prototype.hasOwnProperty.call(state, localKey)
|
|
109
|
+
? state[localKey]
|
|
110
|
+
: {};
|
|
111
|
+
return {
|
|
112
|
+
...state,
|
|
113
|
+
[localKey]: {
|
|
114
|
+
...obj,
|
|
115
|
+
...value
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { getScrollMargin, getMatrixState, marquee } from "./utils";
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* スクロールの余白
|
|
3
|
+
*
|
|
4
|
+
* @type {Object} ScrollMargin
|
|
5
|
+
* @property {number} top - 上までの余白
|
|
6
|
+
* @property {number} left - 左までの余白
|
|
7
|
+
* @property {number} right - 右までの余白
|
|
8
|
+
* @property {number} bottom - 下までの余白
|
|
9
|
+
*/
|
|
10
|
+
export interface ScrollMargin {
|
|
11
|
+
top: number;
|
|
12
|
+
left: number;
|
|
13
|
+
right: number;
|
|
14
|
+
bottom: number;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* スクロールの余白を取得する
|
|
18
|
+
*
|
|
19
|
+
* @param {Event} e - イベント
|
|
20
|
+
* @returns {ScrollMargin}
|
|
21
|
+
*/
|
|
22
|
+
export declare const getScrollMargin: (e: Event) => ScrollMargin;
|
|
23
|
+
/**
|
|
24
|
+
* transform 情報
|
|
25
|
+
*/
|
|
26
|
+
export interface MatrixState {
|
|
27
|
+
translate: {
|
|
28
|
+
x: number;
|
|
29
|
+
y: number;
|
|
30
|
+
z: number;
|
|
31
|
+
};
|
|
32
|
+
scale: {
|
|
33
|
+
x: number;
|
|
34
|
+
y: number;
|
|
35
|
+
z: number;
|
|
36
|
+
};
|
|
37
|
+
rotate: {
|
|
38
|
+
x: number;
|
|
39
|
+
y: number;
|
|
40
|
+
z: number;
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* DOM から transfrom 情報を取得する
|
|
45
|
+
*
|
|
46
|
+
* @param {HTMLElement} dom - 情報を取得する DOM
|
|
47
|
+
* @returns {MatrixState}
|
|
48
|
+
*/
|
|
49
|
+
export declare const getMatrixState: (dom: HTMLElement) => MatrixState | null;
|
|
50
|
+
/**
|
|
51
|
+
* Carousel 風に DOM が流れるアニメーションを実行する
|
|
52
|
+
*
|
|
53
|
+
* @param {Object} props - props
|
|
54
|
+
* @param {HTMLElement} props.element - DOM
|
|
55
|
+
* @param {number} props.duration - 実行時間 (ms)
|
|
56
|
+
* @param {number} props.interval - 待機時間 (ms)
|
|
57
|
+
* @param {(t: number) => number} [props.easing] - easing 関数
|
|
58
|
+
* @returns {{start: () => void, stop: () => void}}
|
|
59
|
+
*/
|
|
60
|
+
export declare const marquee: <S>(props: {
|
|
61
|
+
element: HTMLElement;
|
|
62
|
+
duration: number;
|
|
63
|
+
interval: number;
|
|
64
|
+
easing?: (t: number) => number;
|
|
65
|
+
}) => {
|
|
66
|
+
start: () => void;
|
|
67
|
+
stop: () => void;
|
|
68
|
+
};
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
// hyperapp-ui / dom / utils.ts
|
|
2
|
+
// ---------- ---------- ---------- ---------- ----------
|
|
3
|
+
// getScrollMargin
|
|
4
|
+
// ---------- ---------- ---------- ---------- ----------
|
|
5
|
+
/**
|
|
6
|
+
* スクロールの余白を取得する
|
|
7
|
+
*
|
|
8
|
+
* @param {Event} e - イベント
|
|
9
|
+
* @returns {ScrollMargin}
|
|
10
|
+
*/
|
|
11
|
+
export const getScrollMargin = function (e) {
|
|
12
|
+
const el = e.currentTarget;
|
|
13
|
+
if (!el)
|
|
14
|
+
return { top: 0, left: 0, right: 0, bottom: 0 };
|
|
15
|
+
return {
|
|
16
|
+
top: el.scrollTop,
|
|
17
|
+
left: el.scrollLeft,
|
|
18
|
+
right: el.scrollWidth - (el.clientWidth + el.scrollLeft),
|
|
19
|
+
bottom: el.scrollHeight - (el.clientHeight + el.scrollTop)
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
// ---------- ---------- ---------- ---------- ----------
|
|
23
|
+
// getMatrixState
|
|
24
|
+
// ---------- ---------- ---------- ---------- ----------
|
|
25
|
+
/**
|
|
26
|
+
* DOM から transfrom 情報を取得する
|
|
27
|
+
*
|
|
28
|
+
* @param {HTMLElement} dom - 情報を取得する DOM
|
|
29
|
+
* @returns {MatrixState}
|
|
30
|
+
*/
|
|
31
|
+
export const getMatrixState = (dom) => {
|
|
32
|
+
const style = getComputedStyle(dom);
|
|
33
|
+
const transform = style.transform;
|
|
34
|
+
if (!transform || transform === 'none')
|
|
35
|
+
return null;
|
|
36
|
+
let m;
|
|
37
|
+
try {
|
|
38
|
+
m = new DOMMatrix(transform);
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
const scaleX = Math.hypot(m.m11, m.m12, m.m13);
|
|
44
|
+
const scaleY = Math.hypot(m.m21, m.m22, m.m23);
|
|
45
|
+
const scaleZ = Math.hypot(m.m31, m.m32, m.m33);
|
|
46
|
+
let rotateX = 0;
|
|
47
|
+
let rotateY = 0;
|
|
48
|
+
let rotateZ = 0;
|
|
49
|
+
if (scaleX && scaleY && scaleZ) {
|
|
50
|
+
rotateY = Math.asin(-m.m13 / scaleZ);
|
|
51
|
+
const EPS = 1e-8;
|
|
52
|
+
if (Math.cos(rotateY) > EPS) {
|
|
53
|
+
rotateX = Math.atan2(m.m23 / scaleZ, m.m33 / scaleZ);
|
|
54
|
+
rotateZ = Math.atan2(m.m12 / scaleY, m.m11 / scaleX);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
translate: {
|
|
59
|
+
x: m.m41,
|
|
60
|
+
y: m.m42,
|
|
61
|
+
z: m.m43
|
|
62
|
+
},
|
|
63
|
+
scale: {
|
|
64
|
+
x: scaleX,
|
|
65
|
+
y: scaleY,
|
|
66
|
+
z: scaleZ
|
|
67
|
+
},
|
|
68
|
+
// radian
|
|
69
|
+
rotate: {
|
|
70
|
+
x: rotateX,
|
|
71
|
+
y: rotateY,
|
|
72
|
+
z: rotateZ
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
};
|
|
76
|
+
// ---------- ---------- ---------- ---------- ----------
|
|
77
|
+
// marquee
|
|
78
|
+
// ---------- ---------- ---------- ---------- ----------
|
|
79
|
+
/**
|
|
80
|
+
* Carousel 風に DOM が流れるアニメーションを実行する
|
|
81
|
+
*
|
|
82
|
+
* @param {Object} props - props
|
|
83
|
+
* @param {HTMLElement} props.element - DOM
|
|
84
|
+
* @param {number} props.duration - 実行時間 (ms)
|
|
85
|
+
* @param {number} props.interval - 待機時間 (ms)
|
|
86
|
+
* @param {(t: number) => number} [props.easing] - easing 関数
|
|
87
|
+
* @returns {{start: () => void, stop: () => void}}
|
|
88
|
+
*/
|
|
89
|
+
export const marquee = function (props) {
|
|
90
|
+
const { element, duration, interval, easing = (t) => t } = props;
|
|
91
|
+
// function calcWidth
|
|
92
|
+
const calcWidth = () => {
|
|
93
|
+
const children = Array.from(element.children);
|
|
94
|
+
return !children || children.length < 2
|
|
95
|
+
? 0
|
|
96
|
+
: children[1].offsetLeft - children[0].offsetLeft;
|
|
97
|
+
};
|
|
98
|
+
// variable
|
|
99
|
+
let rID = 0;
|
|
100
|
+
let timerID = 0;
|
|
101
|
+
let startTime = 0;
|
|
102
|
+
let width = 0;
|
|
103
|
+
// requestAnimationFrame callback
|
|
104
|
+
const action = (now) => {
|
|
105
|
+
// set startTime
|
|
106
|
+
if (startTime === 0)
|
|
107
|
+
startTime = now;
|
|
108
|
+
// get progress
|
|
109
|
+
const progress = Math.min((now - startTime) / Math.max(1, duration));
|
|
110
|
+
// set property
|
|
111
|
+
element.style.transform = `translateX(${-easing(progress) * width}px)`;
|
|
112
|
+
// next
|
|
113
|
+
if (progress < 1) {
|
|
114
|
+
rID = requestAnimationFrame(action);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
// reset property
|
|
118
|
+
element.style.transform = `translateX(0px)`;
|
|
119
|
+
// set children
|
|
120
|
+
const firstChild = element.children[0];
|
|
121
|
+
if (!firstChild)
|
|
122
|
+
return;
|
|
123
|
+
// loop
|
|
124
|
+
element.appendChild(firstChild);
|
|
125
|
+
// loop
|
|
126
|
+
timerID = window.setTimeout(() => {
|
|
127
|
+
startTime = 0;
|
|
128
|
+
rID = requestAnimationFrame(action);
|
|
129
|
+
}, interval);
|
|
130
|
+
};
|
|
131
|
+
// result
|
|
132
|
+
return {
|
|
133
|
+
start: () => {
|
|
134
|
+
// 二重起動防止
|
|
135
|
+
if (rID !== 0)
|
|
136
|
+
return;
|
|
137
|
+
// get width
|
|
138
|
+
width = calcWidth();
|
|
139
|
+
if (width === 0)
|
|
140
|
+
return;
|
|
141
|
+
// set gpu layer
|
|
142
|
+
element.style.willChange = "transform";
|
|
143
|
+
// start animation
|
|
144
|
+
rID = requestAnimationFrame(action);
|
|
145
|
+
},
|
|
146
|
+
stop: () => {
|
|
147
|
+
// cancel animation
|
|
148
|
+
cancelAnimationFrame(rID);
|
|
149
|
+
// stop timer
|
|
150
|
+
clearTimeout(timerID);
|
|
151
|
+
// clear gpy layer
|
|
152
|
+
element.style.willChange = "";
|
|
153
|
+
element.style.transform = "";
|
|
154
|
+
// clear ID
|
|
155
|
+
rID = 0;
|
|
156
|
+
timerID = 0;
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { getValue, setValue, getLocalState, setLocalState, createLocalKey, el, concatAction, getClassList, deleteKeys, Route, SelectButton, OptionButton } from "./core";
|
|
2
|
+
export type { InternalEffect, RAFEvent, CSSProperty } from "./animation";
|
|
3
|
+
export { RAFTask, subscription_RAFManager, progress_easing, createUnits, createRAFProperties, effect_RAFProperties } from "./animation";
|
|
4
|
+
export type { CarouselState, CarouselController } from "./animationView";
|
|
5
|
+
export { Carousel, effect_InitCarousel } from "./animationView";
|
|
6
|
+
export type { ScrollMargin, MatrixState } from "./dom";
|
|
7
|
+
export { getScrollMargin, getMatrixState, marquee } from "./dom";
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// core
|
|
2
|
+
export { getValue, setValue, getLocalState, setLocalState, createLocalKey, el, concatAction, getClassList, deleteKeys, Route, SelectButton, OptionButton } from "./core";
|
|
3
|
+
export { RAFTask, subscription_RAFManager, progress_easing, createUnits, createRAFProperties, effect_RAFProperties } from "./animation";
|
|
4
|
+
export { Carousel, effect_InitCarousel } from "./animationView";
|
|
5
|
+
export { getScrollMargin, getMatrixState, marquee } from "./dom";
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "hyperapp-is",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "UI foundation library for Hyperapp by is4416",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
|
|
8
|
+
"main": "dist/index.js",
|
|
9
|
+
"types": "dist/index.d.ts",
|
|
10
|
+
"files": ["dist"],
|
|
11
|
+
|
|
12
|
+
"readme": "README.npm.md",
|
|
13
|
+
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "npm run build:lib",
|
|
16
|
+
"build:lib": "tsc -p tsconfig.lib.json",
|
|
17
|
+
"build:docs": "vite build",
|
|
18
|
+
"dev": "vite",
|
|
19
|
+
"pack:check": "npm pack",
|
|
20
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
21
|
+
},
|
|
22
|
+
|
|
23
|
+
"keywords": [
|
|
24
|
+
"hyperapp",
|
|
25
|
+
"ui",
|
|
26
|
+
"animation",
|
|
27
|
+
"raf",
|
|
28
|
+
"carousel"
|
|
29
|
+
],
|
|
30
|
+
|
|
31
|
+
"peerDependencies": {
|
|
32
|
+
"hyperapp": "^2.0.0",
|
|
33
|
+
"hyperapp-jsx-pragma": "^1.3.0"
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"typescript": "^5.0.0",
|
|
38
|
+
"vite": "^7.3.0"
|
|
39
|
+
}
|
|
40
|
+
}
|