react-iframe-bridge 0.9.0 → 2.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 (140) hide show
  1. package/README.md +0 -10
  2. package/{lib-cjs/components/ErrorPage.d.ts → lib/components/error_page.d.ts} +2 -1
  3. package/lib/components/error_page.d.ts.map +1 -0
  4. package/lib/components/error_page.js +15 -0
  5. package/lib/components/error_page.js.map +1 -0
  6. package/lib/components/home/{Home.d.ts → home.d.ts} +1 -0
  7. package/lib/components/home/home.d.ts.map +1 -0
  8. package/lib/components/home/home.js +44 -0
  9. package/lib/components/home/home.js.map +1 -0
  10. package/{lib-cjs/components/home/HomeContext.d.ts → lib/components/home/home_context.d.ts} +3 -2
  11. package/lib/components/home/home_context.d.ts.map +1 -0
  12. package/lib/components/home/{HomeContext.js → home_context.js} +5 -4
  13. package/lib/components/home/home_context.js.map +1 -0
  14. package/{lib-cjs/components/home/HomeHeader.d.ts → lib/components/home/home_header.d.ts} +1 -0
  15. package/lib/components/home/home_header.d.ts.map +1 -0
  16. package/lib/components/home/home_header.js +16 -0
  17. package/lib/components/home/home_header.js.map +1 -0
  18. package/lib/components/home/{HomeIframe.d.ts → home_iframe.d.ts} +1 -0
  19. package/lib/components/home/home_iframe.d.ts.map +1 -0
  20. package/lib/components/home/{HomeIframe.js → home_iframe.js} +8 -2
  21. package/lib/components/home/home_iframe.js.map +1 -0
  22. package/{lib-cjs/components/home/HomeNoSample.d.ts → lib/components/home/home_no_sample.d.ts} +1 -0
  23. package/lib/components/home/home_no_sample.d.ts.map +1 -0
  24. package/lib/components/home/{HomeNoSample.js → home_no_sample.js} +3 -2
  25. package/lib/components/home/home_no_sample.js.map +1 -0
  26. package/{lib-cjs/components/home/HomeSamples.d.ts → lib/components/home/home_samples.d.ts} +1 -0
  27. package/lib/components/home/home_samples.d.ts.map +1 -0
  28. package/lib/components/home/home_samples.js +29 -0
  29. package/lib/components/home/home_samples.js.map +1 -0
  30. package/{lib-cjs/components/home/HomeSelector.d.ts → lib/components/home/home_selector.d.ts} +1 -0
  31. package/lib/components/home/home_selector.d.ts.map +1 -0
  32. package/lib/components/home/home_selector.js +15 -0
  33. package/lib/components/home/home_selector.js.map +1 -0
  34. package/lib/components/input.d.ts +11 -0
  35. package/lib/components/input.d.ts.map +1 -0
  36. package/lib/components/input.js +14 -0
  37. package/lib/components/input.js.map +1 -0
  38. package/lib/components/{LoadingFull.d.ts → loading_full.d.ts} +1 -0
  39. package/lib/components/loading_full.d.ts.map +1 -0
  40. package/lib/components/loading_full.js +12 -0
  41. package/lib/components/loading_full.js.map +1 -0
  42. package/lib/components/{Spinner.d.ts → spinner.d.ts} +3 -1
  43. package/lib/components/spinner.d.ts.map +1 -0
  44. package/lib/components/spinner.js +18 -0
  45. package/lib/components/spinner.js.map +1 -0
  46. package/lib/contexts/{iframeBridge.d.ts → iframe_bridge.d.ts} +5 -3
  47. package/lib/contexts/iframe_bridge.d.ts.map +1 -0
  48. package/lib/contexts/{iframeBridge.js → iframe_bridge.js} +6 -7
  49. package/lib/contexts/iframe_bridge.js.map +1 -0
  50. package/lib/contexts/roc.d.ts +2 -1
  51. package/lib/contexts/roc.d.ts.map +1 -0
  52. package/lib/contexts/roc.js +1 -0
  53. package/lib/contexts/roc.js.map +1 -0
  54. package/{lib-cjs/hooks/localStorage.d.ts → lib/hooks/local_storage.d.ts} +2 -1
  55. package/lib/hooks/local_storage.d.ts.map +1 -0
  56. package/lib/hooks/{localStorage.js → local_storage.js} +2 -1
  57. package/lib/hooks/local_storage.js.map +1 -0
  58. package/lib/hooks/{useRocQuery.d.ts → use_roc_query.d.ts} +2 -1
  59. package/lib/hooks/use_roc_query.d.ts.map +1 -0
  60. package/lib/hooks/{useRocQuery.js → use_roc_query.js} +2 -1
  61. package/lib/hooks/use_roc_query.js.map +1 -0
  62. package/lib/index.d.ts +4 -3
  63. package/lib/index.d.ts.map +1 -0
  64. package/lib/index.js +4 -3
  65. package/lib/index.js.map +1 -0
  66. package/lib/types/db.d.ts +5 -0
  67. package/lib/types/db.d.ts.map +1 -0
  68. package/lib/types/db.js +1 -0
  69. package/lib/types/db.js.map +1 -0
  70. package/lib/types/util.d.ts +1 -0
  71. package/lib/types/util.d.ts.map +1 -0
  72. package/lib/types/util.js +1 -0
  73. package/lib/types/util.js.map +1 -0
  74. package/{lib-cjs/utils/localStorage.d.ts → lib/utils/local_storage.d.ts} +1 -0
  75. package/lib/utils/local_storage.d.ts.map +1 -0
  76. package/lib/utils/{localStorage.js → local_storage.js} +1 -0
  77. package/lib/utils/local_storage.js.map +1 -0
  78. package/package.json +11 -11
  79. package/src/components/error_page.tsx +39 -0
  80. package/src/components/home/home.tsx +98 -0
  81. package/src/components/home/home_context.tsx +101 -0
  82. package/src/components/home/home_header.tsx +29 -0
  83. package/src/components/home/home_iframe.tsx +73 -0
  84. package/src/components/home/home_no_sample.tsx +16 -0
  85. package/src/components/home/home_samples.tsx +62 -0
  86. package/src/components/home/home_selector.tsx +23 -0
  87. package/src/components/input.tsx +31 -0
  88. package/src/components/loading_full.tsx +19 -0
  89. package/src/components/spinner.tsx +44 -0
  90. package/src/contexts/iframe_bridge.tsx +234 -0
  91. package/src/contexts/roc.tsx +23 -0
  92. package/src/hooks/local_storage.ts +45 -0
  93. package/src/hooks/use_roc_query.ts +75 -0
  94. package/src/index.ts +7 -0
  95. package/src/types/db.ts +81 -0
  96. package/src/types/util.ts +3 -0
  97. package/src/utils/local_storage.ts +35 -0
  98. package/lib/components/ErrorPage.d.ts +0 -8
  99. package/lib/components/ErrorPage.js +0 -4
  100. package/lib/components/LoadingFull.js +0 -5
  101. package/lib/components/Spinner.js +0 -5
  102. package/lib/components/home/Home.js +0 -22
  103. package/lib/components/home/HomeContext.d.ts +0 -20
  104. package/lib/components/home/HomeHeader.d.ts +0 -1
  105. package/lib/components/home/HomeHeader.js +0 -9
  106. package/lib/components/home/HomeNoSample.d.ts +0 -1
  107. package/lib/components/home/HomeSamples.d.ts +0 -1
  108. package/lib/components/home/HomeSamples.js +0 -23
  109. package/lib/components/home/HomeSelector.d.ts +0 -5
  110. package/lib/components/home/HomeSelector.js +0 -5
  111. package/lib/hooks/localStorage.d.ts +0 -13
  112. package/lib/utils/localStorage.d.ts +0 -3
  113. package/lib-cjs/components/ErrorPage.js +0 -7
  114. package/lib-cjs/components/LoadingFull.d.ts +0 -1
  115. package/lib-cjs/components/LoadingFull.js +0 -11
  116. package/lib-cjs/components/Spinner.d.ts +0 -4
  117. package/lib-cjs/components/Spinner.js +0 -11
  118. package/lib-cjs/components/home/Home.d.ts +0 -26
  119. package/lib-cjs/components/home/Home.js +0 -29
  120. package/lib-cjs/components/home/HomeContext.js +0 -55
  121. package/lib-cjs/components/home/HomeHeader.js +0 -12
  122. package/lib-cjs/components/home/HomeIframe.d.ts +0 -5
  123. package/lib-cjs/components/home/HomeIframe.js +0 -37
  124. package/lib-cjs/components/home/HomeNoSample.js +0 -14
  125. package/lib-cjs/components/home/HomeSamples.js +0 -29
  126. package/lib-cjs/components/home/HomeSelector.js +0 -11
  127. package/lib-cjs/contexts/iframeBridge.d.ts +0 -32
  128. package/lib-cjs/contexts/iframeBridge.js +0 -131
  129. package/lib-cjs/contexts/roc.d.ts +0 -8
  130. package/lib-cjs/contexts/roc.js +0 -21
  131. package/lib-cjs/hooks/localStorage.js +0 -39
  132. package/lib-cjs/hooks/useRocQuery.d.ts +0 -13
  133. package/lib-cjs/hooks/useRocQuery.js +0 -46
  134. package/lib-cjs/index.d.ts +0 -3
  135. package/lib-cjs/index.js +0 -12
  136. package/lib-cjs/types/db.d.ts +0 -78
  137. package/lib-cjs/types/db.js +0 -2
  138. package/lib-cjs/types/util.d.ts +0 -6
  139. package/lib-cjs/types/util.js +0 -2
  140. package/lib-cjs/utils/localStorage.js +0 -34
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,2BAA2B,CAAC;AACjD,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EACL,sBAAsB,EACtB,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,6BAA6B,CAAC"}
package/lib/index.js CHANGED
@@ -1,3 +1,4 @@
1
- export { Home } from './components/home/Home';
2
- export { useRoc, RocProvider } from './contexts/roc';
3
- export { useIframeBridgeContext, useIframeBridgeSample, IframeBridgeProvider, } from './contexts/iframeBridge';
1
+ export { Home } from './components/home/home.js';
2
+ export { useRoc, RocProvider } from './contexts/roc.js';
3
+ export { useIframeBridgeContext, useIframeBridgeSample, IframeBridgeProvider, } from './contexts/iframe_bridge.js';
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,2BAA2B,CAAC;AACjD,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EACL,sBAAsB,EACtB,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,6BAA6B,CAAC"}
package/lib/types/db.d.ts CHANGED
@@ -39,6 +39,10 @@ export interface SampleEntry {
39
39
  }
40
40
  export interface SampleEntryContent {
41
41
  general: {
42
+ title?: string;
43
+ name?: Array<{
44
+ value: string;
45
+ }>;
42
46
  mf: string;
43
47
  mw: number;
44
48
  em: number;
@@ -76,3 +80,4 @@ export interface SampleEntrySpectraNmr {
76
80
  filename: string;
77
81
  };
78
82
  }
83
+ //# sourceMappingURL=db.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../../src/types/db.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,EAAE,MAAM,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,OAAO,CAAC;IAChB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,MAAM,aAAa,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAC7C,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,OAAO,CAAC;IACf,GAAG,EAAE,aAAa,CAAC;IACnB,KAAK,EAAE,QAAQ,CAAC;IAChB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,EAAE,kBAAkB,CAAC;CAC9B;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE;QACP,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,IAAI,CAAC,EAAE,KAAK,CAAC;YAAE,KAAK,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QAChC,EAAE,EAAE,MAAM,CAAC;QACX,EAAE,EAAE,MAAM,CAAC;QACX,EAAE,EAAE,MAAM,CAAC;QACX,OAAO,EAAE,MAAM,CAAC;QAChB,GAAG,EAAE;YACH,KAAK,EAAE,MAAM,CAAC;YACd,WAAW,EAAE,MAAM,CAAC;YACpB,KAAK,EAAE,MAAM,EAAE,CAAC;SACjB,CAAC;KACH,CAAC;IACF,UAAU,EAAE;QACV,GAAG,EAAE,KAAK,CAAC;YAAE,KAAK,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;KAC/B,CAAC;IACF,OAAO,EAAE;QACP,GAAG,EAAE,qBAAqB,EAAE,CAAC;KAC9B,CAAC;CACH;AAED,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,KAAK,EAAE,OAAO,CAAC;IACf,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,OAAO,EAAE,CAAC;IACjB,KAAK,EAAE;QACL,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC;CACH"}
package/lib/types/db.js CHANGED
@@ -1 +1,2 @@
1
1
  export {};
2
+ //# sourceMappingURL=db.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"db.js","sourceRoot":"","sources":["../../src/types/db.ts"],"names":[],"mappings":""}
@@ -4,3 +4,4 @@ export type ActionType<Action, Payload = void> = Payload extends void ? {
4
4
  type: Action;
5
5
  payload: Payload;
6
6
  };
7
+ //# sourceMappingURL=util.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../../src/types/util.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,UAAU,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,IAAI,OAAO,SAAS,IAAI,GACjE;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC"}
package/lib/types/util.js CHANGED
@@ -1 +1,2 @@
1
1
  export {};
2
+ //# sourceMappingURL=util.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"util.js","sourceRoot":"","sources":["../../src/types/util.ts"],"names":[],"mappings":""}
@@ -1,3 +1,4 @@
1
1
  declare let getItem: (key: string) => unknown | null;
2
2
  declare let setItem: (key: string, value: unknown) => void;
3
3
  export { getItem, setItem };
4
+ //# sourceMappingURL=local_storage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local_storage.d.ts","sourceRoot":"","sources":["../../src/utils/local_storage.ts"],"names":[],"mappings":"AAcA,QAAA,IAAI,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,GAAG,IAAI,CAAC;AAC7C,QAAA,IAAI,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;AAmBnD,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC"}
@@ -30,3 +30,4 @@ else {
30
30
  };
31
31
  }
32
32
  export { getItem, setItem };
33
+ //# sourceMappingURL=local_storage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local_storage.js","sourceRoot":"","sources":["../../src/utils/local_storage.ts"],"names":[],"mappings":"AAAA,mEAAmE;AAEnE;;;;GAIG;AACH,SAAS,SAAS,CAAC,GAAW;IAC5B,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACrB,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;IAC1D,CAAC;IACD,OAAO,uBAAuB,GAAG,EAAE,CAAC;AACtC,CAAC;AAED,IAAI,OAAwC,CAAC;AAC7C,IAAI,OAA8C,CAAC;AAEnD,IAAI,OAAO,YAAY,KAAK,WAAW,EAAE,CAAC;IACxC,OAAO,GAAG,SAAS,OAAO,CAAC,GAAW;QACpC,MAAM,KAAK,GAAG,YAAY,CAAC,OAAO,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC;QACnD,IAAI,CAAC,KAAK;YAAE,OAAO,IAAI,CAAC;QACxB,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAC3B,CAAC,CAAC;IACF,OAAO,GAAG,CAAC,GAAW,EAAE,KAAc,EAAE,EAAE,CACxC,YAAY,CAAC,OAAO,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;AAChE,CAAC;KAAM,CAAC;IACN,6DAA6D;IAC7D,OAAO,GAAG,CAAC,GAAW,EAAE,EAAE,CAAC,IAAI,CAAC;IAChC,6DAA6D;IAC7D,OAAO,GAAG,CAAC,GAAW,EAAE,KAAc,EAAQ,EAAE;QAC9C,OAAO;IACT,CAAC,CAAC;AACJ,CAAC;AAED,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC"}
package/package.json CHANGED
@@ -1,16 +1,16 @@
1
1
  {
2
2
  "name": "react-iframe-bridge",
3
- "version": "0.9.0",
3
+ "version": "2.0.0",
4
4
  "description": "React hooks and components to work with iframe-bridge.",
5
- "main": "lib-cjs/index.js",
6
- "module": "lib/index.js",
5
+ "type": "module",
6
+ "exports": "./lib/index.js",
7
7
  "files": [
8
8
  "lib",
9
- "lib-cjs"
9
+ "src"
10
10
  ],
11
11
  "scripts": {
12
12
  "check-types": "tsc --noEmit",
13
- "compile": "tsc && tsc -p tsconfig.cjs.json",
13
+ "compile": "tsc",
14
14
  "eslint": "eslint .",
15
15
  "eslint-fix": "npm run eslint -- --fix",
16
16
  "prepack": "npm run compile",
@@ -28,19 +28,19 @@
28
28
  },
29
29
  "homepage": "https://github.com/zakodium-oss/react-iframe-bridge#readme",
30
30
  "dependencies": {
31
- "clsx": "^2.1.1",
32
- "iframe-bridge": "^2.0.0",
31
+ "iframe-bridge": "^3.0.1",
33
32
  "immer": "^10.1.1",
34
33
  "rest-on-couch-client": "^5.3.1"
35
34
  },
36
35
  "devDependencies": {
36
+ "@types/node": "^22.15.21",
37
37
  "@types/react": "^18.3.1",
38
38
  "@types/react-dom": "^18.3.0",
39
- "eslint": "^8.57.0",
40
- "eslint-config-zakodium": "^9.0.1",
41
- "prettier": "^3.2.5",
39
+ "eslint": "^9.27.0",
40
+ "eslint-config-zakodium": "^15.0.1",
41
+ "prettier": "^3.5.3",
42
42
  "react": "^18.3.1",
43
43
  "react-dom": "^18.3.1",
44
- "typescript": "^5.4.5"
44
+ "typescript": "^5.8.3"
45
45
  }
46
46
  }
@@ -0,0 +1,39 @@
1
+ import type { ReactNode } from 'react';
2
+
3
+ interface ErrorPageProps {
4
+ title: string;
5
+ subtitle: string;
6
+ children?: ReactNode;
7
+ }
8
+
9
+ export default function ErrorPage(props: ErrorPageProps) {
10
+ return (
11
+ <div style={{ maxWidth: '56rem', margin: 'auto' }}>
12
+ <div
13
+ style={{
14
+ display: 'flex',
15
+ justifyContent: 'space-between',
16
+ paddingInline: '0.5rem',
17
+ paddingTop: '1rem',
18
+ }}
19
+ >
20
+ <div style={{ minWidth: 0 }}>
21
+ <h1
22
+ style={{
23
+ fontSize: '3rem',
24
+ fontWeight: 'bold',
25
+ marginTop: '4rem',
26
+ color: '#1c398e',
27
+ }}
28
+ >
29
+ {props.title}
30
+ </h1>
31
+ <h2 style={{ marginTop: '4rem', fontSize: '1.125rem' }}>
32
+ {props.subtitle}
33
+ </h2>
34
+ <div style={{ marginTop: '1rem' }}>{props.children}</div>
35
+ </div>
36
+ </div>
37
+ </div>
38
+ );
39
+ }
@@ -0,0 +1,98 @@
1
+ import { useEffect } from 'react';
2
+
3
+ import { HomeContextProvider, useHomeDispatchContext } from './home_context.js';
4
+ import HomeHeader from './home_header.js';
5
+ import HomeIframe from './home_iframe.js';
6
+ import HomeNoSample from './home_no_sample.js';
7
+ import HomeSamples from './home_samples.js';
8
+
9
+ export interface HomeProps {
10
+ /**
11
+ * URL of the rest-on-couch instance.
12
+ * @default 'http://localhost:3000/api/fake-roc'
13
+ */
14
+ rocUrl?: string;
15
+ /**
16
+ * Name of the rest-on-couch database.
17
+ * @default 'eln'
18
+ */
19
+ database?: string;
20
+ /**
21
+ * Base url loaded by the iframe
22
+ */
23
+ baseUrl?: string;
24
+ /**
25
+ * Default path the iframe should load, if no path is found in local storage
26
+ */
27
+ defaultPath?: string;
28
+ /**
29
+ * Opt out of selecting a sample / selecting no sample before loading the iframe.
30
+ * The sample selection UI will be hidden and the iframe will automatically load without a selected sample.
31
+ */
32
+ noSampleSelection?: boolean;
33
+ }
34
+
35
+ export function Home(props: HomeProps) {
36
+ const { baseUrl, noSampleSelection, rocUrl, database, defaultPath } = props;
37
+ return (
38
+ <HomeContextProvider
39
+ rocUrl={rocUrl}
40
+ database={database}
41
+ defaultPath={defaultPath}
42
+ >
43
+ <HomeInternal noSampleSelection={noSampleSelection} baseUrl={baseUrl} />
44
+ </HomeContextProvider>
45
+ );
46
+ }
47
+
48
+ function HomeInternal(props: Pick<HomeProps, 'baseUrl' | 'noSampleSelection'>) {
49
+ const homeDispatch = useHomeDispatchContext();
50
+ useEffect(() => {
51
+ if (props.noSampleSelection) {
52
+ homeDispatch({
53
+ type: 'OPEN_NO_SAMPLE',
54
+ });
55
+ }
56
+ }, [props.noSampleSelection, homeDispatch]);
57
+ return (
58
+ <div
59
+ style={{
60
+ display: 'flex',
61
+ flexDirection: 'column',
62
+ width: '100vw',
63
+ height: '100vh',
64
+ }}
65
+ >
66
+ <HomeHeader />
67
+ <div
68
+ style={{
69
+ marginTop: '0.5rem',
70
+ display: 'flex',
71
+ minHeight: 0,
72
+ flex: 1,
73
+ flexDirection: 'row',
74
+ borderTop: '1px solid #d1d5dc',
75
+ }}
76
+ >
77
+ {!props.noSampleSelection && (
78
+ <div
79
+ style={{
80
+ display: 'flex',
81
+ width: '12rem',
82
+ flexDirection: 'column',
83
+ gap: '0.75rem',
84
+ overflow: 'auto',
85
+ borderRight: '1px solid #d1d5dc',
86
+ paddingInline: '0.5rem',
87
+ paddingTop: '1rem',
88
+ }}
89
+ >
90
+ <HomeNoSample />
91
+ <HomeSamples />
92
+ </div>
93
+ )}
94
+ <HomeIframe baseUrl={props.baseUrl} />
95
+ </div>
96
+ </div>
97
+ );
98
+ }
@@ -0,0 +1,101 @@
1
+ import { produce } from 'immer';
2
+ import type { Dispatch, ReactNode, Reducer } from 'react';
3
+ import { createContext, useContext, useReducer } from 'react';
4
+
5
+ import { RocProvider } from '../../contexts/roc.js';
6
+ import { useSaveToLocalStorage } from '../../hooks/local_storage.js';
7
+ import type { ActionType } from '../../types/util.js';
8
+ import { getItem } from '../../utils/local_storage.js';
9
+
10
+ interface HomeContextType {
11
+ rocUrl: string;
12
+ database: string;
13
+ iframePath: string;
14
+ iframeMode: 'closed' | 'sample' | 'no-sample';
15
+ selectedSample: string | null;
16
+ }
17
+
18
+ function getInitialHomeContext(
19
+ config: Pick<
20
+ HomeContextProviderProps,
21
+ 'rocUrl' | 'database' | 'defaultPath'
22
+ > = {},
23
+ ): HomeContextType {
24
+ const {
25
+ rocUrl = 'http://localhost:3000/api/fake-roc',
26
+ database = 'eln',
27
+ defaultPath = '/dev/base-page',
28
+ } = config;
29
+ return {
30
+ rocUrl,
31
+ database,
32
+ iframePath: (getItem('dev-home-iframe-path') as string) || defaultPath,
33
+ iframeMode: 'closed',
34
+ selectedSample: null,
35
+ };
36
+ }
37
+
38
+ type HomeContextAction =
39
+ | ActionType<'SELECT_SAMPLE', string>
40
+ | ActionType<'OPEN_NO_SAMPLE'>
41
+ | ActionType<'SET_IFRAME_PAGE', string>;
42
+
43
+ const homeReducer: Reducer<HomeContextType, HomeContextAction> = produce(
44
+ (state: HomeContextType, action: HomeContextAction) => {
45
+ switch (action.type) {
46
+ case 'OPEN_NO_SAMPLE':
47
+ state.iframeMode = 'no-sample';
48
+ state.selectedSample = null;
49
+ break;
50
+ case 'SELECT_SAMPLE':
51
+ state.iframeMode = 'sample';
52
+ state.selectedSample = action.payload;
53
+ break;
54
+ case 'SET_IFRAME_PAGE':
55
+ state.iframePath = action.payload;
56
+ break;
57
+ default:
58
+ throw new Error('unreachable');
59
+ }
60
+ },
61
+ );
62
+
63
+ const homeContext = createContext(getInitialHomeContext());
64
+ const homeDispatchContext = createContext<Dispatch<HomeContextAction>>(() => {
65
+ // noop
66
+ });
67
+
68
+ interface HomeContextProviderProps {
69
+ children: ReactNode;
70
+ rocUrl?: string;
71
+ database?: string;
72
+ defaultPath?: string;
73
+ }
74
+
75
+ export function HomeContextProvider(props: HomeContextProviderProps) {
76
+ const { children, ...initial } = props;
77
+ const [homeState, dispatch] = useReducer(
78
+ homeReducer,
79
+ initial,
80
+ getInitialHomeContext,
81
+ );
82
+ useSaveToLocalStorage('dev-home-iframe-path', homeState.iframePath);
83
+
84
+ return (
85
+ <homeContext.Provider value={homeState}>
86
+ <homeDispatchContext.Provider value={dispatch}>
87
+ <RocProvider url={homeState.rocUrl} database={homeState.database}>
88
+ {children}
89
+ </RocProvider>
90
+ </homeDispatchContext.Provider>
91
+ </homeContext.Provider>
92
+ );
93
+ }
94
+
95
+ export function useHomeContext() {
96
+ return useContext(homeContext);
97
+ }
98
+
99
+ export function useHomeDispatchContext() {
100
+ return useContext(homeDispatchContext);
101
+ }
@@ -0,0 +1,29 @@
1
+ import Input from '../input.js';
2
+
3
+ import { useHomeContext, useHomeDispatchContext } from './home_context.js';
4
+
5
+ export default function HomeHeader() {
6
+ const { rocUrl, database, iframePath } = useHomeContext();
7
+ const dispatch = useHomeDispatchContext();
8
+ return (
9
+ <header
10
+ style={{
11
+ display: 'flex',
12
+ flexDirection: 'row',
13
+ gap: '1rem',
14
+ padding: '0.5rem',
15
+ }}
16
+ >
17
+ <Input name="rocUrl" style={{ flex: 1 }} value={rocUrl} readOnly />
18
+ <Input name="database" value={database} readOnly />
19
+ <Input
20
+ name="iframe-page"
21
+ value={iframePath}
22
+ style={{ flex: 1 }}
23
+ onChange={(event) => {
24
+ dispatch({ type: 'SET_IFRAME_PAGE', payload: event.target.value });
25
+ }}
26
+ />
27
+ </header>
28
+ );
29
+ }
@@ -0,0 +1,73 @@
1
+ // https://github.com/import-js/eslint-plugin-import/issues/1810
2
+
3
+ import { postMessage, registerHandler } from 'iframe-bridge/main';
4
+ import { useEffect, useState } from 'react';
5
+
6
+ import { useHomeContext } from './home_context.js';
7
+
8
+ interface AdminMessage {
9
+ type: 'admin.connect';
10
+ windowID: number;
11
+ }
12
+
13
+ interface HomeIframeProps {
14
+ baseUrl?: string;
15
+ }
16
+
17
+ export default function HomeIframe(props: HomeIframeProps) {
18
+ const { baseUrl } = props;
19
+ const { database, iframePath, rocUrl, selectedSample, iframeMode } =
20
+ useHomeContext();
21
+
22
+ const [windowId, setWindowId] = useState<number>();
23
+
24
+ useEffect(() => {
25
+ registerHandler('admin', (message: AdminMessage) => {
26
+ switch (message.type) {
27
+ case 'admin.connect': {
28
+ setWindowId(message.windowID);
29
+ break;
30
+ }
31
+ default:
32
+ throw new Error('unreachable');
33
+ }
34
+ });
35
+ }, []);
36
+
37
+ useEffect(() => {
38
+ if (windowId === undefined) return;
39
+ postMessage(
40
+ 'tab.data',
41
+ {
42
+ couchDB: {
43
+ url: rocUrl,
44
+ database,
45
+ },
46
+ uuid: selectedSample,
47
+ },
48
+ windowId,
49
+ );
50
+ }, [windowId, database, rocUrl, selectedSample]);
51
+
52
+ return (
53
+ <div
54
+ style={{
55
+ display: 'flex',
56
+ flex: 1,
57
+ alignItems: 'center',
58
+ justifyContent: 'center',
59
+ }}
60
+ >
61
+ {iframeMode !== 'closed' ? (
62
+ <iframe
63
+ key={selectedSample}
64
+ allowFullScreen
65
+ src={`${baseUrl || ''}${iframePath}`}
66
+ style={{ width: '100%', height: '100%' }}
67
+ />
68
+ ) : (
69
+ <div>Please select something</div>
70
+ )}
71
+ </div>
72
+ );
73
+ }
@@ -0,0 +1,16 @@
1
+ import { useHomeContext, useHomeDispatchContext } from './home_context.js';
2
+ import HomeSelector from './home_selector.js';
3
+
4
+ export default function HomeNoSample() {
5
+ const { iframeMode } = useHomeContext();
6
+ const dispatch = useHomeDispatchContext();
7
+ return (
8
+ <div>
9
+ <HomeSelector
10
+ onClick={() => dispatch({ type: 'OPEN_NO_SAMPLE' })}
11
+ selected={iframeMode === 'no-sample'}
12
+ text="No sample"
13
+ />
14
+ </div>
15
+ );
16
+ }
@@ -0,0 +1,62 @@
1
+ import type { RocQueryResult } from '../../hooks/use_roc_query.js';
2
+ import { useRocQuery } from '../../hooks/use_roc_query.js';
3
+ import type { TocEntry } from '../../types/db.js';
4
+ import Spinner from '../spinner.js';
5
+
6
+ import { useHomeContext, useHomeDispatchContext } from './home_context.js';
7
+ import HomeSelector from './home_selector.js';
8
+
9
+ export default function HomeSamples() {
10
+ const { loading, error, result } = useRocQuery<TocEntry>('sample_toc');
11
+ if (error) {
12
+ throw error;
13
+ }
14
+ return (
15
+ <>
16
+ <h1
17
+ style={{
18
+ marginBottom: '1rem',
19
+ textAlign: 'center',
20
+ fontSize: '1.125rem',
21
+ fontWeight: 'bold',
22
+ }}
23
+ >
24
+ Sample TOC
25
+ </h1>
26
+ <div style={{ flex: 1 }}>
27
+ {loading || !result ? <Loading /> : <SampleToc samples={result} />}
28
+ </div>
29
+ </>
30
+ );
31
+ }
32
+
33
+ function SampleToc(props: { samples: Array<RocQueryResult<TocEntry>> }) {
34
+ const { selectedSample } = useHomeContext();
35
+ const dispatch = useHomeDispatchContext();
36
+ function selectSample(id: string) {
37
+ dispatch({ type: 'SELECT_SAMPLE', payload: id });
38
+ }
39
+
40
+ return (
41
+ <div style={{ display: 'flex', flexDirection: 'column', gap: '0.5rem' }}>
42
+ {props.samples.map((sample) => (
43
+ <HomeSelector
44
+ key={sample.id}
45
+ selected={sample.id === selectedSample}
46
+ text={sample.value.reference}
47
+ onClick={() => selectSample(sample.id)}
48
+ />
49
+ ))}
50
+ </div>
51
+ );
52
+ }
53
+
54
+ function Loading() {
55
+ return (
56
+ <div
57
+ style={{ marginTop: '2rem', display: 'flex', justifyContent: 'center' }}
58
+ >
59
+ <Spinner style={{ width: '2rem', height: '2rem', color: '#6a7282' }} />
60
+ </div>
61
+ );
62
+ }
@@ -0,0 +1,23 @@
1
+ import type { CSSProperties } from 'react';
2
+
3
+ export default function HomeSelector(props: {
4
+ selected: boolean;
5
+ onClick: () => void;
6
+ text: string;
7
+ }) {
8
+ const style: CSSProperties = {
9
+ padding: '0.25rem',
10
+ border: '1px solid #99a1af',
11
+ borderRadius: '0.25rem',
12
+ cursor: 'pointer',
13
+ };
14
+ if (props.selected) {
15
+ style.backgroundColor = '#f3f4f6';
16
+ style.boxShadow = 'inset 0 2px 4px 0 rgb(0 0 0 / 0.05)';
17
+ }
18
+ return (
19
+ <div style={style} onClick={props.onClick}>
20
+ {props.text}
21
+ </div>
22
+ );
23
+ }
@@ -0,0 +1,31 @@
1
+ import type { ChangeEvent, CSSProperties } from 'react';
2
+
3
+ interface InputProps {
4
+ name: string;
5
+ style?: CSSProperties;
6
+ value: string;
7
+ readOnly?: boolean;
8
+ onChange?: (event: ChangeEvent<HTMLInputElement>) => void;
9
+ }
10
+
11
+ export default function Input(props: InputProps) {
12
+ return (
13
+ <input
14
+ name={props.name}
15
+ type="text"
16
+ style={{
17
+ appearance: 'none',
18
+ border: '1px solid #4a5565',
19
+ backgroundColor: 'white',
20
+ paddingInline: '0.75rem',
21
+ paddingBlock: '0.5rem',
22
+ fontSize: '1rem',
23
+ lineHeight: 1,
24
+ ...props.style,
25
+ }}
26
+ value={props.value}
27
+ readOnly={props.readOnly}
28
+ onChange={props.onChange}
29
+ />
30
+ );
31
+ }
@@ -0,0 +1,19 @@
1
+ import Spinner from './spinner.js';
2
+
3
+ export default function LoadingFull() {
4
+ return (
5
+ <div
6
+ style={{
7
+ display: 'flex',
8
+ width: '100%',
9
+ height: '100%',
10
+ alignItems: 'center',
11
+ justifyContent: 'center',
12
+ }}
13
+ >
14
+ <Spinner
15
+ style={{ width: '2.5rem', height: '2.5rem', color: '#6a7282' }}
16
+ />
17
+ </div>
18
+ );
19
+ }
@@ -0,0 +1,44 @@
1
+ import type { CSSProperties } from 'react';
2
+ import { useEffect, useRef } from 'react';
3
+
4
+ export interface SpinnerProps {
5
+ style?: CSSProperties;
6
+ }
7
+
8
+ export default function Spinner(props: SpinnerProps) {
9
+ // Trick to spin the element without defining keyframes.
10
+ const style: CSSProperties = {
11
+ transform: 'rotate(0deg)',
12
+ transition: 'transform 60s linear',
13
+ ...props.style,
14
+ };
15
+ const ref = useRef<SVGSVGElement>(null);
16
+ useEffect(() => {
17
+ if (!ref.current) return;
18
+ ref.current.style.transform = 'rotate(36000deg)';
19
+ }, []);
20
+
21
+ return (
22
+ <svg
23
+ ref={ref}
24
+ style={style}
25
+ xmlns="http://www.w3.org/2000/svg"
26
+ fill="none"
27
+ viewBox="0 0 24 24"
28
+ >
29
+ <circle
30
+ opacity="25%"
31
+ cx="12"
32
+ cy="12"
33
+ r="10"
34
+ stroke="currentColor"
35
+ strokeWidth="4"
36
+ />
37
+ <path
38
+ opacity="75%"
39
+ fill="currentColor"
40
+ d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
41
+ />
42
+ </svg>
43
+ );
44
+ }