@yargram/react 1.0.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 (105) hide show
  1. package/.storybook/main.ts +21 -0
  2. package/.storybook/preview.ts +21 -0
  3. package/dist/components/LogWindow/LogEntryRow.d.ts +8 -0
  4. package/dist/components/LogWindow/LogEntryRow.d.ts.map +1 -0
  5. package/dist/components/LogWindow/LogEntryRow.js +14 -0
  6. package/dist/components/LogWindow/LogWindow.d.ts +41 -0
  7. package/dist/components/LogWindow/LogWindow.d.ts.map +1 -0
  8. package/dist/components/LogWindow/LogWindow.js +144 -0
  9. package/dist/components/LogWindow/LogWindow.stories.d.ts +29 -0
  10. package/dist/components/LogWindow/LogWindow.stories.d.ts.map +1 -0
  11. package/dist/components/LogWindow/LogWindow.stories.js +183 -0
  12. package/dist/components/LogWindow/LogWindow.test.d.ts +2 -0
  13. package/dist/components/LogWindow/LogWindow.test.d.ts.map +1 -0
  14. package/dist/components/LogWindow/LogWindow.test.js +61 -0
  15. package/dist/components/LogWindow/LogWindowEscapeDemo.d.ts +12 -0
  16. package/dist/components/LogWindow/LogWindowEscapeDemo.d.ts.map +1 -0
  17. package/dist/components/LogWindow/LogWindowEscapeDemo.js +56 -0
  18. package/dist/components/LogWindow/NetworkEntryRow.d.ts +8 -0
  19. package/dist/components/LogWindow/NetworkEntryRow.d.ts.map +1 -0
  20. package/dist/components/LogWindow/NetworkEntryRow.js +32 -0
  21. package/dist/components/LogWindow/index.d.ts +7 -0
  22. package/dist/components/LogWindow/index.d.ts.map +1 -0
  23. package/dist/components/LogWindow/index.js +4 -0
  24. package/dist/components/LogWindow/types.d.ts +36 -0
  25. package/dist/components/LogWindow/types.d.ts.map +1 -0
  26. package/dist/components/LogWindow/types.js +1 -0
  27. package/dist/components/LoginWindow/LoginForm.d.ts +11 -0
  28. package/dist/components/LoginWindow/LoginForm.d.ts.map +1 -0
  29. package/dist/components/LoginWindow/LoginForm.js +28 -0
  30. package/dist/components/LoginWindow/LoginForm.test.d.ts +2 -0
  31. package/dist/components/LoginWindow/LoginForm.test.d.ts.map +1 -0
  32. package/dist/components/LoginWindow/LoginForm.test.js +34 -0
  33. package/dist/components/LoginWindow/LoginWindow.d.ts +15 -0
  34. package/dist/components/LoginWindow/LoginWindow.d.ts.map +1 -0
  35. package/dist/components/LoginWindow/LoginWindow.js +27 -0
  36. package/dist/components/LoginWindow/index.d.ts +3 -0
  37. package/dist/components/LoginWindow/index.d.ts.map +1 -0
  38. package/dist/components/LoginWindow/index.js +1 -0
  39. package/dist/contexts/ApiContext.d.ts +35 -0
  40. package/dist/contexts/ApiContext.d.ts.map +1 -0
  41. package/dist/contexts/ApiContext.js +82 -0
  42. package/dist/contexts/ApiContext.test.d.ts +2 -0
  43. package/dist/contexts/ApiContext.test.d.ts.map +1 -0
  44. package/dist/contexts/ApiContext.test.js +45 -0
  45. package/dist/contexts/PrinterContext.d.ts +12 -0
  46. package/dist/contexts/PrinterContext.d.ts.map +1 -0
  47. package/dist/contexts/PrinterContext.js +17 -0
  48. package/dist/contexts/PrinterContext.test.d.ts +2 -0
  49. package/dist/contexts/PrinterContext.test.d.ts.map +1 -0
  50. package/dist/contexts/PrinterContext.test.js +19 -0
  51. package/dist/contexts/YahmanContext.d.ts +69 -0
  52. package/dist/contexts/YahmanContext.d.ts.map +1 -0
  53. package/dist/contexts/YahmanContext.js +414 -0
  54. package/dist/contexts/YahmanContext.stories.d.ts +16 -0
  55. package/dist/contexts/YahmanContext.stories.d.ts.map +1 -0
  56. package/dist/contexts/YahmanContext.stories.js +64 -0
  57. package/dist/contexts/YargramContext.d.ts +69 -0
  58. package/dist/contexts/YargramContext.d.ts.map +1 -0
  59. package/dist/contexts/YargramContext.js +414 -0
  60. package/dist/contexts/YargramContext.stories.d.ts +16 -0
  61. package/dist/contexts/YargramContext.stories.d.ts.map +1 -0
  62. package/dist/contexts/YargramContext.stories.js +64 -0
  63. package/dist/contexts/YargramContext.test.d.ts +2 -0
  64. package/dist/contexts/YargramContext.test.d.ts.map +1 -0
  65. package/dist/contexts/YargramContext.test.js +54 -0
  66. package/dist/hooks/useLogWindowShortcut.d.ts +24 -0
  67. package/dist/hooks/useLogWindowShortcut.d.ts.map +1 -0
  68. package/dist/hooks/useLogWindowShortcut.js +61 -0
  69. package/dist/hooks/useLogWindowShortcut.test.d.ts +2 -0
  70. package/dist/hooks/useLogWindowShortcut.test.d.ts.map +1 -0
  71. package/dist/hooks/useLogWindowShortcut.test.js +93 -0
  72. package/dist/index.d.ts +6 -0
  73. package/dist/index.d.ts.map +1 -0
  74. package/dist/index.js +7 -0
  75. package/dist/test/setup.d.ts +2 -0
  76. package/dist/test/setup.d.ts.map +1 -0
  77. package/dist/test/setup.js +1 -0
  78. package/package.json +49 -0
  79. package/src/components/LogWindow/LogEntryRow.tsx +38 -0
  80. package/src/components/LogWindow/LogWindow.css +614 -0
  81. package/src/components/LogWindow/LogWindow.stories.tsx +206 -0
  82. package/src/components/LogWindow/LogWindow.test.tsx +68 -0
  83. package/src/components/LogWindow/LogWindow.tsx +379 -0
  84. package/src/components/LogWindow/LogWindowEscapeDemo.tsx +100 -0
  85. package/src/components/LogWindow/NetworkEntryRow.tsx +102 -0
  86. package/src/components/LogWindow/index.ts +13 -0
  87. package/src/components/LogWindow/types.ts +40 -0
  88. package/src/components/LoginWindow/LoginForm.test.tsx +38 -0
  89. package/src/components/LoginWindow/LoginForm.tsx +78 -0
  90. package/src/components/LoginWindow/LoginWindow.css +198 -0
  91. package/src/components/LoginWindow/LoginWindow.tsx +90 -0
  92. package/src/components/LoginWindow/index.ts +2 -0
  93. package/src/contexts/ApiContext.test.tsx +68 -0
  94. package/src/contexts/ApiContext.tsx +155 -0
  95. package/src/contexts/PrinterContext.test.tsx +37 -0
  96. package/src/contexts/PrinterContext.tsx +35 -0
  97. package/src/contexts/YargramContext.stories.tsx +148 -0
  98. package/src/contexts/YargramContext.test.tsx +105 -0
  99. package/src/contexts/YargramContext.tsx +676 -0
  100. package/src/hooks/useLogWindowShortcut.test.ts +111 -0
  101. package/src/hooks/useLogWindowShortcut.ts +96 -0
  102. package/src/index.ts +14 -0
  103. package/src/test/setup.ts +1 -0
  104. package/tsconfig.json +16 -0
  105. package/vitest.config.ts +18 -0
@@ -0,0 +1,111 @@
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
2
+ import { renderHook, act } from '@testing-library/react';
3
+ import { useLogWindowShortcut } from './useLogWindowShortcut';
4
+
5
+ describe('useLogWindowShortcut', () => {
6
+ beforeEach(() => {
7
+ vi.useFakeTimers();
8
+ });
9
+
10
+ afterEach(() => {
11
+ vi.useRealTimers();
12
+ });
13
+
14
+ it('returns isOpen false initially', () => {
15
+ const { result } = renderHook(() => useLogWindowShortcut());
16
+ expect(result.current.isOpen).toBe(false);
17
+ expect(result.current.escapeCount).toBe(0);
18
+ });
19
+
20
+ it('open() sets isOpen to true', () => {
21
+ const { result } = renderHook(() => useLogWindowShortcut());
22
+ act(() => {
23
+ result.current.open();
24
+ });
25
+ expect(result.current.isOpen).toBe(true);
26
+ });
27
+
28
+ it('close() sets isOpen to false', () => {
29
+ const { result } = renderHook(() => useLogWindowShortcut());
30
+ act(() => {
31
+ result.current.open();
32
+ });
33
+ act(() => {
34
+ result.current.close();
35
+ });
36
+ expect(result.current.isOpen).toBe(false);
37
+ });
38
+
39
+ it('toggle() flips isOpen', () => {
40
+ const { result } = renderHook(() => useLogWindowShortcut());
41
+ expect(result.current.isOpen).toBe(false);
42
+ act(() => {
43
+ result.current.toggle();
44
+ });
45
+ expect(result.current.isOpen).toBe(true);
46
+ act(() => {
47
+ result.current.toggle();
48
+ });
49
+ expect(result.current.isOpen).toBe(false);
50
+ });
51
+
52
+ it('opening after threshold Escape key presses', () => {
53
+ const { result } = renderHook(() =>
54
+ useLogWindowShortcut({ escapeCount: 3, resetAfterMs: 2000 })
55
+ );
56
+ const dispatchEscape = () => {
57
+ window.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape' }));
58
+ };
59
+
60
+ expect(result.current.isOpen).toBe(false);
61
+ act(() => {
62
+ dispatchEscape();
63
+ });
64
+ expect(result.current.escapeCount).toBe(1);
65
+ act(() => {
66
+ dispatchEscape();
67
+ });
68
+ expect(result.current.escapeCount).toBe(2);
69
+ act(() => {
70
+ dispatchEscape();
71
+ });
72
+ expect(result.current.isOpen).toBe(true);
73
+ expect(result.current.escapeCount).toBe(0);
74
+ });
75
+
76
+ it('resets escape count after resetAfterMs', () => {
77
+ const { result } = renderHook(() =>
78
+ useLogWindowShortcut({ escapeCount: 5, resetAfterMs: 500 })
79
+ );
80
+ const dispatchEscape = () => {
81
+ window.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape' }));
82
+ };
83
+
84
+ act(() => {
85
+ dispatchEscape();
86
+ dispatchEscape();
87
+ });
88
+ expect(result.current.escapeCount).toBe(2);
89
+ act(() => {
90
+ vi.advanceTimersByTime(600);
91
+ });
92
+ expect(result.current.escapeCount).toBe(0);
93
+ });
94
+
95
+ it('calls onTrigger when provided and threshold reached', () => {
96
+ const onTrigger = vi.fn();
97
+ const { result } = renderHook(() =>
98
+ useLogWindowShortcut({ escapeCount: 2, onTrigger })
99
+ );
100
+ const dispatchEscape = () => {
101
+ window.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape' }));
102
+ };
103
+
104
+ act(() => {
105
+ dispatchEscape();
106
+ dispatchEscape();
107
+ });
108
+ expect(onTrigger).toHaveBeenCalledTimes(1);
109
+ expect(result.current.isOpen).toBe(false);
110
+ });
111
+ });
@@ -0,0 +1,96 @@
1
+ import { useState, useEffect, useCallback, useRef } from 'react';
2
+
3
+ export type UseLogWindowShortcutOptions = {
4
+ /** ログウィンドウを開くために必要な Escape の連続押下回数 */
5
+ escapeCount?: number;
6
+ /** この時間(ms)以内に次の Escape が押されないとカウントをリセットする */
7
+ resetAfterMs?: number;
8
+ /** ログウィンドウ表示中に Escape を押したら閉じる */
9
+ closeOnEscape?: boolean;
10
+ /** 指定時は threshold 到達時に open の代わりにこのコールバックを呼ぶ(例: 認証時はログアウトしてログインウィンドウ表示) */
11
+ onTrigger?: () => void;
12
+ };
13
+
14
+ export type UseLogWindowShortcutResult = {
15
+ /** ログウィンドウを表示するか */
16
+ isOpen: boolean;
17
+ /** ログウィンドウを開く */
18
+ open: () => void;
19
+ /** ログウィンドウを閉じる */
20
+ close: () => void;
21
+ /** トグル */
22
+ toggle: () => void;
23
+ /** 現在の Escape カウント(0 〜 escapeCount-1)。デバッグ・UI 表示用 */
24
+ escapeCount: number;
25
+ };
26
+
27
+ export function useLogWindowShortcut(
28
+ options: UseLogWindowShortcutOptions = {}
29
+ ): UseLogWindowShortcutResult {
30
+ const {
31
+ escapeCount: threshold = 5,
32
+ resetAfterMs = 1500,
33
+ closeOnEscape = true,
34
+ onTrigger,
35
+ } = options;
36
+
37
+ const [isOpen, setIsOpen] = useState(false);
38
+ const [escapeCount, setEscapeCount] = useState(0);
39
+ const lastEscapeAt = useRef<number>(0);
40
+ const resetTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
41
+
42
+ const open = useCallback(() => setIsOpen(true), []);
43
+ const close = useCallback(() => setIsOpen(false), []);
44
+ const toggle = useCallback(() => setIsOpen((prev) => !prev), []);
45
+
46
+ useEffect(() => {
47
+ const handleKeyDown = (e: KeyboardEvent) => {
48
+ if (e.key !== 'Escape') return;
49
+
50
+ if (isOpen) {
51
+ if (closeOnEscape) {
52
+ close();
53
+ }
54
+ return;
55
+ }
56
+
57
+ const now = Date.now();
58
+ if (now - lastEscapeAt.current > resetAfterMs) {
59
+ setEscapeCount(1);
60
+ } else {
61
+ setEscapeCount((c) => {
62
+ const next = c + 1;
63
+ if (next >= threshold) {
64
+ lastEscapeAt.current = 0;
65
+ if (resetTimer.current) {
66
+ clearTimeout(resetTimer.current);
67
+ resetTimer.current = null;
68
+ }
69
+ if (onTrigger) {
70
+ onTrigger();
71
+ } else {
72
+ open();
73
+ }
74
+ return 0;
75
+ }
76
+ return next;
77
+ });
78
+ }
79
+ lastEscapeAt.current = now;
80
+
81
+ if (resetTimer.current) clearTimeout(resetTimer.current);
82
+ resetTimer.current = setTimeout(() => {
83
+ setEscapeCount(0);
84
+ resetTimer.current = null;
85
+ }, resetAfterMs);
86
+ };
87
+
88
+ window.addEventListener('keydown', handleKeyDown);
89
+ return () => {
90
+ window.removeEventListener('keydown', handleKeyDown);
91
+ if (resetTimer.current) clearTimeout(resetTimer.current);
92
+ };
93
+ }, [isOpen, threshold, resetAfterMs, closeOnEscape, open, close, onTrigger]);
94
+
95
+ return { isOpen, open, close, toggle, escapeCount };
96
+ }
package/src/index.ts ADDED
@@ -0,0 +1,14 @@
1
+ // Api(provider: 'rest' | 'graphql' で REST: get/post/put/delete、GraphQL: ransack/handing)
2
+ export {
3
+ useApi,
4
+ type RestApiContextValue,
5
+ type GraphqlApiContextValue,
6
+ } from './contexts/ApiContext';
7
+
8
+ // Printer
9
+ export { usePrinter } from './contexts/PrinterContext';
10
+
11
+ // 統合 Context(Api + Printer + LogWindow)
12
+ export { YargramProvider, useYargram } from './contexts/YargramContext';
13
+ export type { YargramProviderProps } from './contexts/YargramContext';
14
+ export { gql } from '@apollo/client';
@@ -0,0 +1 @@
1
+ import '@testing-library/jest-dom';
package/tsconfig.json ADDED
@@ -0,0 +1,16 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "jsx": "react-jsx",
7
+ "strict": true,
8
+ "skipLibCheck": true,
9
+ "declaration": true,
10
+ "declarationMap": true,
11
+ "outDir": "./dist",
12
+ "rootDir": "./src"
13
+ },
14
+ "include": ["src/**/*"],
15
+ "exclude": ["node_modules", "dist"]
16
+ }
@@ -0,0 +1,18 @@
1
+ import { defineConfig } from 'vitest/config';
2
+ import react from '@vitejs/plugin-react';
3
+
4
+ export default defineConfig({
5
+ plugins: [react()],
6
+ test: {
7
+ environment: 'jsdom',
8
+ globals: true,
9
+ setupFiles: ['./src/test/setup.ts'],
10
+ include: ['src/**/*.{test,spec}.{ts,tsx}'],
11
+ coverage: {
12
+ provider: 'v8',
13
+ reporter: ['text', 'json', 'html'],
14
+ include: ['src/**/*.{ts,tsx}'],
15
+ exclude: ['src/**/*.stories.tsx', 'src/test/**', 'src/**/*.d.ts'],
16
+ },
17
+ },
18
+ });