misaki-studio-internal 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/next-env.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ /// <reference types="next" />
2
+ /// <reference types="next/image-types/global" />
3
+
4
+ // NOTE: This file should not be edited
5
+ // see https://nextjs.org/docs/basic-features/typescript for more information.
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "misaki-studio-internal",
3
+ "version": "0.1.0",
4
+ "scripts": {
5
+ "build": "webpack",
6
+ "publish": "npm publish --access public"
7
+ },
8
+ "dependencies": {
9
+ "@types/lodash": "4.14.202",
10
+ "axios": "0.25.0",
11
+ "joi": "18.0.1",
12
+ "lodash": "4.17.21",
13
+ "styled-components": "6.1.11",
14
+ "use-immer": "0.9.0"
15
+ },
16
+ "peerDependencies": {
17
+ "react": ">=18.2.0",
18
+ "react-dom": ">=18.2.0",
19
+ "next": ">=15.5.4"
20
+ },
21
+ "devDependencies": {
22
+ "sass": "1.42.1",
23
+ "webpack": "5.103.0",
24
+ "webpack-cli": "6.0.1",
25
+ "typescript": "5.9.3",
26
+ "postcss": "8.3.6",
27
+ "style-loader": "4.0.0",
28
+ "sass-loader": "16.0.6",
29
+ "css-loader": "7.1.2",
30
+ "@types/node": "18.11.19",
31
+ "@types/react": "18.2.61",
32
+ "@types/react-dom": "18.2.19",
33
+ "@types/styled-components": "5.1.26"
34
+ }
35
+ }
package/src/index.ts ADDED
@@ -0,0 +1,26 @@
1
+ import * as api from "./utils/api";
2
+ import * as appear from "./utils/appear";
3
+ import * as blog from "./utils/blog";
4
+ import * as document from "./utils/document";
5
+ import * as operations from "./utils/operations";
6
+ import * as popup from "./utils/popup";
7
+ import * as resizeDetecter from "./utils/resize-detecter";
8
+ import * as storage from "./utils/storage";
9
+ import * as timeline from "./utils/timeline";
10
+ import * as types from "./utils/types";
11
+ import * as useResize from "./utils/use-resize";
12
+ import * as utils from "./utils/utils";
13
+ export {
14
+ api,
15
+ appear,
16
+ blog,
17
+ document,
18
+ operations,
19
+ popup,
20
+ resizeDetecter,
21
+ storage,
22
+ timeline,
23
+ types,
24
+ useResize,
25
+ utils,
26
+ };
@@ -0,0 +1,4 @@
1
+ declare module "*.module.scss" {
2
+ const classes: { [key: string]: string };
3
+ export default classes;
4
+ }
@@ -0,0 +1,87 @@
1
+ import _ from "lodash";
2
+ import { deepEqual } from "fast-equals";
3
+ import { useMemo, useState } from "react";
4
+ import axios from "axios";
5
+
6
+ export type Request<Query, Body> = {
7
+ config: {
8
+ host: string;
9
+ path: string;
10
+ };
11
+ query: Query;
12
+ body: Body;
13
+ };
14
+
15
+ export class ApiLoader<Query, Body, Response> {
16
+ response?: Response;
17
+ error?: string;
18
+ loading: boolean = false;
19
+ onUpdate?: () => void;
20
+ request?: Request<Query, Body>;
21
+ controller = new AbortController();
22
+
23
+ constructor(onUpdate?: () => void) {
24
+ this.onUpdate = onUpdate;
25
+ }
26
+
27
+ load(request: Request<Query, Body>) {
28
+ if (deepEqual(request, this.request)) return;
29
+ if (this.loading) this.controller.abort();
30
+ this.controller = new AbortController();
31
+ this.request = request;
32
+ this.loading = true;
33
+ this.onUpdate?.();
34
+ const { config, body } = request;
35
+ const query = Object.entries(request.query || {}).reduce(
36
+ (prev, [key, param], index) => {
37
+ if (index === 0) {
38
+ return `?${key}=${param}`;
39
+ } else {
40
+ return `${prev}&${key}=${param}`;
41
+ }
42
+ },
43
+ "",
44
+ );
45
+ axios({
46
+ signal: this.controller.signal,
47
+ method: "post",
48
+ url: `${config.host}/${config.path.startsWith("/") ? config.path.slice(1) : config.path}${query}`,
49
+ headers: {
50
+ "Content-Type": "application/json",
51
+ },
52
+ data: body,
53
+ })
54
+ .then((response) => {
55
+ this.response = response.data;
56
+ this.error = undefined;
57
+ this.loading = false;
58
+ this.onUpdate?.();
59
+ })
60
+ .catch((error: any) => {
61
+ this.response = undefined;
62
+ this.error = error?.message || "Unknown Error";
63
+ this.loading = false;
64
+ this.onUpdate?.();
65
+ });
66
+ }
67
+ }
68
+
69
+ export const useApi = <Query, Body, Response>(
70
+ request: Request<Query, Body>,
71
+ ) => {
72
+ const [, update] = useState({});
73
+ const loader = useMemo(() => {
74
+ return new ApiLoader<Query, Body, Response>(() => {
75
+ update({});
76
+ });
77
+ }, []);
78
+ loader.load(request);
79
+ return {
80
+ loading: loader.loading,
81
+ response: {
82
+ body: loader.response,
83
+ },
84
+ error: loader.error,
85
+ request: loader.request,
86
+ };
87
+ };
@@ -0,0 +1,270 @@
1
+ const appear = function (window: Window & typeof globalThis) {
2
+ var scrollLastPos: number | null = null,
3
+ scrollTimer = 0 as any,
4
+ scroll = {} as any;
5
+
6
+ function track() {
7
+ var newPos = window.scrollY || window.pageYOffset; // pageYOffset for IE9
8
+ if (scrollLastPos != null) {
9
+ scroll.velocity = newPos - scrollLastPos;
10
+ scroll.delta =
11
+ scroll.velocity >= 0 ? scroll.velocity : -1 * scroll.velocity;
12
+ }
13
+ scrollLastPos = newPos;
14
+ if (scrollTimer) {
15
+ clearTimeout(scrollTimer);
16
+ }
17
+ scrollTimer = setTimeout(function () {
18
+ scrollLastPos = null;
19
+ }, 30);
20
+ }
21
+ addEventListener("scroll", track, false);
22
+
23
+ function viewable(el: HTMLElement, bounds: any) {
24
+ var rect = el.getBoundingClientRect();
25
+ return (
26
+ rect.top + rect.height >= 0 &&
27
+ rect.left + rect.width >= 0 &&
28
+ rect.bottom - rect.height <=
29
+ (window.innerHeight || document.documentElement.clientHeight) +
30
+ bounds &&
31
+ rect.right - rect.width <=
32
+ (window.innerWidth || document.documentElement.clientWidth) + bounds
33
+ );
34
+ }
35
+
36
+ return function (obj: any) {
37
+ const fn = function () {
38
+ var initd = false,
39
+ elements: any[] = [],
40
+ elementsLength: number,
41
+ reappear: boolean[] = [],
42
+ appeared = 0,
43
+ disappeared = 0,
44
+ timer: string | number | NodeJS.Timeout | undefined,
45
+ deltaSet: boolean,
46
+ opts = {} as any,
47
+ done: boolean;
48
+
49
+ // called on scroll and resize event, so debounce the actual function that does
50
+ // the heavy work of determining if an item is viewable and then "appearing" it
51
+ function checkAppear() {
52
+ if (scroll.delta < opts.delta.speed) {
53
+ if (!deltaSet) {
54
+ deltaSet = true;
55
+ doCheckAppear();
56
+ setTimeout(function () {
57
+ deltaSet = false;
58
+ }, opts.delta.timeout);
59
+ }
60
+ }
61
+ doCheckAppear();
62
+ }
63
+
64
+ function begin() {
65
+ // initial appear check before any scroll or resize event
66
+ setTimeout(() => {
67
+ doCheckAppear();
68
+ }, 100);
69
+
70
+ // add relevant listeners
71
+ addEventListener("scroll", checkAppear, false);
72
+ addEventListener("wheel", checkAppear, false);
73
+ addEventListener("resize", checkAppear, false);
74
+ }
75
+
76
+ function end() {
77
+ elements = [];
78
+ if (timer) {
79
+ clearTimeout(timer);
80
+ }
81
+ removeListeners();
82
+ }
83
+
84
+ function removeListeners() {
85
+ removeEventListener("scroll", checkAppear, false);
86
+ removeEventListener("resize", checkAppear, false);
87
+ }
88
+
89
+ function doCheckAppear() {
90
+ if (done) {
91
+ return;
92
+ }
93
+ elements.forEach(function (n, i) {
94
+ if (n && viewable(n, opts.bounds)) {
95
+ // only act if the element is eligible to reappear
96
+ if (reappear[i]) {
97
+ // mark this element as not eligible to appear
98
+ reappear[i] = false;
99
+ // increment the count of appeared items
100
+ appeared++;
101
+ // call the appear fn
102
+ if (opts.appear) {
103
+ opts.appear(n);
104
+ }
105
+ // if not tracking reappears or disappears, need to remove node here
106
+ if (!opts.disappear && !opts.reappear) {
107
+ // stop tracking this node, which is now viewable
108
+ elements[i] = null;
109
+ }
110
+ }
111
+ } else {
112
+ if (reappear[i] === false) {
113
+ if (opts.disappear) {
114
+ opts.disappear(n);
115
+ }
116
+ // increment the dissappeared count
117
+ disappeared++;
118
+ // if not tracking reappears, need to remove node here
119
+ if (!opts.reappear) {
120
+ // stop tracking this node, which is now viewable
121
+ elements[i] = null;
122
+ }
123
+ }
124
+ // element is out of view and eligible to be appeared again
125
+ reappear[i] = true;
126
+ }
127
+ });
128
+
129
+ // remove listeners if all items have (re)appeared
130
+ if (
131
+ !opts.reappear &&
132
+ (!opts.appear || (opts.appear && appeared === elementsLength)) &&
133
+ (!opts.disappear ||
134
+ (opts.disappear && disappeared === elementsLength))
135
+ ) {
136
+ // ensure done is only called once (could be called from a trailing debounce/throttle)
137
+ done = true;
138
+ removeListeners();
139
+ // all items have appeared, so call the done fn
140
+ if (opts.done) {
141
+ opts.done();
142
+ }
143
+ }
144
+ }
145
+
146
+ function init() {
147
+ // make sure we only init once
148
+ if (initd) {
149
+ return;
150
+ }
151
+ initd = true;
152
+
153
+ // call the obj init fn
154
+ if (opts.init) {
155
+ opts.init();
156
+ }
157
+ // get the elements to work with
158
+ var els;
159
+ if (typeof opts.elements === "function") {
160
+ els = opts.elements();
161
+ } else {
162
+ els = opts.elements;
163
+ }
164
+ if (els) {
165
+ // put elements into an array object to work with
166
+ elementsLength = els.length;
167
+ for (var i = 0; i < elementsLength; i += 1) {
168
+ elements.push(els[i]);
169
+ reappear.push(true);
170
+ }
171
+ begin();
172
+ }
173
+ }
174
+
175
+ var appear = function (obj: any) {
176
+ obj = obj || {};
177
+
178
+ // assign the fn to execute when a node is visible
179
+ opts = {
180
+ // a function to be run when the dom is ready (allows for any setup work)
181
+ init: obj.init,
182
+ // either an array of elements or a function that will return an htmlCollection
183
+ elements: obj.elements,
184
+ // function to call when an element is "viewable", will be passed the element to work with
185
+ appear: obj.appear,
186
+ // function to call when an element is no longer "viewable", will be passed the element to work with
187
+ disappear: obj.disappear,
188
+ // function to call when all the elements have "appeared"
189
+ done: obj.done,
190
+ // keep tracking the elements
191
+ reappear: obj.reappear,
192
+ // the extra border around an element to make it viewable outside of the true viewport
193
+ bounds: obj.bounds || 0,
194
+ // the debounce timeout
195
+ debounce: obj.debounce || 50,
196
+ // appear.js will also check for items on continuous slow scrolling
197
+ // you can controll how slow the scrolling should be (deltaSpeed)
198
+ // and when it will check again (deltaTimeout) after it has inspected the dom/viewport;
199
+ delta: {
200
+ speed: obj.deltaSpeed || 50,
201
+ timeout: obj.deltaTimeout || 500,
202
+ },
203
+ };
204
+
205
+ // add an event listener to init when dom is ready
206
+ addEventListener("DOMContentLoaded", init, false);
207
+
208
+ // http://stackoverflow.com/questions/9900311/how-do-i-target-only-internet-explorer-10-for-certain-situations-like-internet-e/13971998#13971998
209
+ var isIE10 = false;
210
+ if (Function("/*@cc_on return document.documentMode===10@*/")()) {
211
+ isIE10 = true;
212
+ }
213
+ var completeOrLoaded =
214
+ document.readyState === "complete" ||
215
+ document.readyState === ("loaded" as any);
216
+
217
+ // call init if document is ready to be worked with and we missed the event
218
+ if (isIE10) {
219
+ if (completeOrLoaded) {
220
+ init();
221
+ }
222
+ } else {
223
+ if (completeOrLoaded || document.readyState === "interactive") {
224
+ init();
225
+ }
226
+ }
227
+
228
+ return {
229
+ // manually fire check for visibility of tracked elements
230
+ trigger: function trigger() {
231
+ doCheckAppear();
232
+ },
233
+ // pause tracking of elements
234
+ pause: function pause() {
235
+ removeListeners();
236
+ },
237
+ // resume tracking of elements after a pause
238
+ resume: function resume() {
239
+ begin();
240
+ },
241
+ // provide a means to stop monitoring all elements
242
+ destroy: function destroy() {
243
+ end();
244
+ },
245
+ };
246
+ };
247
+ return appear;
248
+ };
249
+ return fn()(obj);
250
+ };
251
+ };
252
+
253
+ type Param = {
254
+ init?: () => void;
255
+ elements: () => HTMLElement[];
256
+ appear: (el: HTMLElement) => void;
257
+ disappear?: (el: HTMLElement) => void;
258
+ bounds: number;
259
+ reappear: boolean;
260
+ };
261
+
262
+ export type Return = {
263
+ trigger: () => void;
264
+ pause: () => void;
265
+ resume: () => void;
266
+ destroy: () => void;
267
+ };
268
+
269
+ export const onAppear: () => (param: Param) => Return = () =>
270
+ appear(window) as any;
@@ -0,0 +1,223 @@
1
+ import { useRouter } from "next/router";
2
+ import { CSSProperties, useState } from "react";
3
+ import styled from "styled-components";
4
+
5
+ export type Blog<T = never> = {
6
+ pagePath: string;
7
+ name: string;
8
+ children?: Blog<T>[];
9
+ properties?: T;
10
+ };
11
+
12
+ export const Item = styled.div`
13
+ display: flex;
14
+ flex-direction: row;
15
+ justify-content: start;
16
+ user-select: none;
17
+ white-space: pre-line;
18
+ width: 100%;
19
+ height: auto;
20
+ justify-content: start;
21
+ align-items: center;
22
+ box-sizing: border-box;
23
+ cursor: pointer;
24
+ border-radius: 8px;
25
+ height: auto;
26
+ &.hide-arrow {
27
+ > .icon {
28
+ display: none;
29
+ }
30
+ > .text {
31
+ padding-left: 8px;
32
+ }
33
+ }
34
+ > .icon {
35
+ display: flex;
36
+ flex-direction: row;
37
+ justify-content: center;
38
+ align-items: center;
39
+ width: 30px;
40
+ height: 30px;
41
+ &.open {
42
+ rotate: 90deg;
43
+ }
44
+ &.hide {
45
+ user-select: none;
46
+ opacity: 0;
47
+ }
48
+ }
49
+ > .text {
50
+ display: flex;
51
+ flex-direction: row;
52
+ justify-content: start;
53
+ align-items: center;
54
+ width: auto;
55
+ height: 30px;
56
+ padding: 8px;
57
+ padding-left: 0px;
58
+ flex: 1;
59
+ }
60
+ &:hover {
61
+ background-color: #0000000a;
62
+ }
63
+ `;
64
+
65
+ export const BlogItem = <T,>({
66
+ item,
67
+ level,
68
+ onClick,
69
+ itemWrapper,
70
+ isRoot,
71
+ }: {
72
+ item: Blog<T>;
73
+ level: number;
74
+ onClick?: (id: string) => void;
75
+ itemWrapper?: BlogItemWrapper;
76
+ isRoot?: boolean;
77
+ }) => {
78
+ const router = useRouter();
79
+ const currentPath = location.pathname.slice(0, -1);
80
+ const isPageOpen =
81
+ location.pathname.startsWith(item.pagePath) &&
82
+ currentPath !== item.pagePath;
83
+ const [isOpen, setOpen] = useState(isPageOpen);
84
+ const ItemWrapper = itemWrapper;
85
+ const rootPath = isRoot
86
+ ? item.pagePath.split("/").slice(0, -1).join("/")
87
+ : undefined;
88
+
89
+ const isSelected =
90
+ router.asPath === item.pagePath || rootPath === router.asPath;
91
+ return (
92
+ <>
93
+ {ItemWrapper ? (
94
+ <ItemWrapper
95
+ properties={{
96
+ title: item.name,
97
+ isSelected,
98
+ }}
99
+ events={{
100
+ click: () => {
101
+ if (onClick) {
102
+ onClick(item.pagePath);
103
+ } else {
104
+ router.push(item.pagePath);
105
+ }
106
+ },
107
+ }}
108
+ style={{
109
+ paddingLeft: `${level * 15}px`,
110
+ }}
111
+ />
112
+ ) : (
113
+ <Item
114
+ style={{
115
+ paddingLeft: `${level * 15}px`,
116
+ }}
117
+ >
118
+ <div
119
+ onClick={() => setOpen(!isOpen)}
120
+ className={`icon ${isOpen ? "open" : "close"}`}
121
+ >
122
+ {item.children?.length ? (
123
+ <svg
124
+ width="7"
125
+ height="12"
126
+ viewBox="0 0 7 12"
127
+ fill="none"
128
+ xmlns="http://www.w3.org/2000/svg"
129
+ >
130
+ <path
131
+ d="M0.0854187 1.36887L0.746734 0.696899L6.09215 5.95757L5.43083 6.62954L0.0854187 1.36887Z"
132
+ fill="black"
133
+ />
134
+ <path
135
+ d="M5.42025 5.29636L6.09215 5.95757L0.831473 11.3032L0.159501 10.6419L5.42025 5.29636Z"
136
+ fill="black"
137
+ />
138
+ </svg>
139
+ ) : (
140
+ <svg
141
+ width="6"
142
+ height="6"
143
+ viewBox="0 0 6 6"
144
+ fill="none"
145
+ xmlns="http://www.w3.org/2000/svg"
146
+ >
147
+ <circle cx="3.44977" cy="2.93213" r="2" stroke="#666666" />
148
+ </svg>
149
+ )}
150
+ </div>
151
+ <div
152
+ onClick={() => {
153
+ if (onClick) {
154
+ onClick(item.pagePath);
155
+ } else {
156
+ router.push(item.pagePath);
157
+ }
158
+ }}
159
+ className="text"
160
+ >
161
+ {item.name}
162
+ </div>
163
+ </Item>
164
+ )}
165
+
166
+ {!!item.children?.length && isOpen && (
167
+ <BlogList
168
+ onClick={onClick}
169
+ list={item.children}
170
+ level={level + 1}
171
+ itemWrapper={itemWrapper}
172
+ />
173
+ )}
174
+ </>
175
+ );
176
+ };
177
+
178
+ export const BlogList = <T = never,>(props: {
179
+ list: Blog<T>[];
180
+ onClick?: (id: string) => void;
181
+ level: number;
182
+ itemWrapper?: BlogItemWrapper;
183
+ isRoot?: boolean;
184
+ }) => {
185
+ return (
186
+ <>
187
+ {props.list.map((item, index) => (
188
+ <BlogItem
189
+ item={item}
190
+ onClick={props.onClick}
191
+ key={item.pagePath}
192
+ level={props.level}
193
+ itemWrapper={props.itemWrapper}
194
+ isRoot={index === 0 && props.isRoot}
195
+ />
196
+ ))}
197
+ </>
198
+ );
199
+ };
200
+
201
+ type BlogItemWrapper = (props: {
202
+ properties?: { title: string; isSelected: boolean };
203
+ events?: { open?: () => void; click?: () => void };
204
+ style?: CSSProperties | undefined;
205
+ }) => JSX.Element;
206
+
207
+ export const BlogSideBar = <T = never,>(props: {
208
+ list: Blog<T>[];
209
+ itemWrapper?: BlogItemWrapper;
210
+ onClick?: (id: string) => void;
211
+ }) => {
212
+ return (
213
+ <>
214
+ <BlogList
215
+ onClick={props.onClick}
216
+ list={props.list}
217
+ level={0}
218
+ itemWrapper={props.itemWrapper}
219
+ isRoot={true}
220
+ />
221
+ </>
222
+ );
223
+ };