create-ncblock 0.0.1

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 (238) hide show
  1. package/README.md +49 -0
  2. package/bin/cli.js +33 -0
  3. package/package.json +25 -0
  4. package/scripts/init.ts +527 -0
  5. package/scripts/scaffold-assets/AGENTS.md +65 -0
  6. package/scripts/utils/templates.ts +293 -0
  7. package/sdk-version.json +1 -0
  8. package/templates/debug/README.md +36 -0
  9. package/templates/debug/_gitignore +2 -0
  10. package/templates/debug/custom_blocks.json +9 -0
  11. package/templates/debug/dist/assets/index-Cet2SsjS.css +2 -0
  12. package/templates/debug/dist/assets/index-DAzv_fuh.js +9 -0
  13. package/templates/debug/dist/custom_blocks.json +9 -0
  14. package/templates/debug/dist/index.html +16 -0
  15. package/templates/debug/index.html +15 -0
  16. package/templates/debug/node_modules/.bin/browserslist +21 -0
  17. package/templates/debug/node_modules/.bin/esbuild +21 -0
  18. package/templates/debug/node_modules/.bin/jiti +21 -0
  19. package/templates/debug/node_modules/.bin/rollup +21 -0
  20. package/templates/debug/node_modules/.bin/tsc +21 -0
  21. package/templates/debug/node_modules/.bin/tsserver +21 -0
  22. package/templates/debug/node_modules/.bin/tsx +21 -0
  23. package/templates/debug/node_modules/.bin/vite +21 -0
  24. package/templates/debug/node_modules/.vite/deps/_metadata.json +50 -0
  25. package/templates/debug/node_modules/.vite/deps/package.json +3 -0
  26. package/templates/debug/node_modules/.vite/deps/react-CsV5wVHy.js +770 -0
  27. package/templates/debug/node_modules/.vite/deps/react-CsV5wVHy.js.map +1 -0
  28. package/templates/debug/node_modules/.vite/deps/react-dom.js +185 -0
  29. package/templates/debug/node_modules/.vite/deps/react-dom.js.map +1 -0
  30. package/templates/debug/node_modules/.vite/deps/react-dom_client.js +14384 -0
  31. package/templates/debug/node_modules/.vite/deps/react-dom_client.js.map +1 -0
  32. package/templates/debug/node_modules/.vite/deps/react.js +2 -0
  33. package/templates/debug/node_modules/.vite/deps/react_jsx-dev-runtime.js +204 -0
  34. package/templates/debug/node_modules/.vite/deps/react_jsx-dev-runtime.js.map +1 -0
  35. package/templates/debug/node_modules/.vite/deps/react_jsx-runtime.js +208 -0
  36. package/templates/debug/node_modules/.vite/deps/react_jsx-runtime.js.map +1 -0
  37. package/templates/debug/node_modules/.vite/deps/valibot.js +6623 -0
  38. package/templates/debug/node_modules/.vite/deps/valibot.js.map +1 -0
  39. package/templates/debug/node_modules/.vite-temp/vite.config.ts.timestamp-1778623720803-0bcf523a67aa8.mjs +15 -0
  40. package/templates/debug/package.json +30 -0
  41. package/templates/debug/src/index.css +62 -0
  42. package/templates/debug/src/index.tsx +1963 -0
  43. package/templates/debug/tsconfig.json +17 -0
  44. package/templates/debug/vite.config.ts +8 -0
  45. package/templates/empty/README.md +10 -0
  46. package/templates/empty/_gitignore +2 -0
  47. package/templates/empty/custom_blocks.json +12 -0
  48. package/templates/empty/dist/assets/index-CodJADav.js +9 -0
  49. package/templates/empty/dist/custom_blocks.json +12 -0
  50. package/templates/empty/dist/index.html +15 -0
  51. package/templates/empty/index.html +15 -0
  52. package/templates/empty/node_modules/.bin/esbuild +21 -0
  53. package/templates/empty/node_modules/.bin/jiti +21 -0
  54. package/templates/empty/node_modules/.bin/tsc +21 -0
  55. package/templates/empty/node_modules/.bin/tsserver +21 -0
  56. package/templates/empty/node_modules/.bin/tsx +21 -0
  57. package/templates/empty/node_modules/.bin/vite +21 -0
  58. package/templates/empty/node_modules/.vite/deps/_metadata.json +50 -0
  59. package/templates/empty/node_modules/.vite/deps/package.json +3 -0
  60. package/templates/empty/node_modules/.vite/deps/react-CsV5wVHy.js +770 -0
  61. package/templates/empty/node_modules/.vite/deps/react-CsV5wVHy.js.map +1 -0
  62. package/templates/empty/node_modules/.vite/deps/react-dom.js +185 -0
  63. package/templates/empty/node_modules/.vite/deps/react-dom.js.map +1 -0
  64. package/templates/empty/node_modules/.vite/deps/react-dom_client.js +14384 -0
  65. package/templates/empty/node_modules/.vite/deps/react-dom_client.js.map +1 -0
  66. package/templates/empty/node_modules/.vite/deps/react.js +2 -0
  67. package/templates/empty/node_modules/.vite/deps/react_jsx-dev-runtime.js +204 -0
  68. package/templates/empty/node_modules/.vite/deps/react_jsx-dev-runtime.js.map +1 -0
  69. package/templates/empty/node_modules/.vite/deps/react_jsx-runtime.js +208 -0
  70. package/templates/empty/node_modules/.vite/deps/react_jsx-runtime.js.map +1 -0
  71. package/templates/empty/node_modules/.vite/deps/valibot.js +6623 -0
  72. package/templates/empty/node_modules/.vite/deps/valibot.js.map +1 -0
  73. package/templates/empty/package.json +28 -0
  74. package/templates/empty/src/index.tsx +12 -0
  75. package/templates/empty/tsconfig.json +17 -0
  76. package/templates/empty/vite.config.ts +7 -0
  77. package/templates/gantt-chart/node_modules/.bin/tsc +21 -0
  78. package/templates/gantt-chart/node_modules/.bin/tsserver +21 -0
  79. package/templates/gantt-chart/node_modules/.bin/vite +21 -0
  80. package/templates/gantt-chart/node_modules/.vite/deps/_metadata.json +50 -0
  81. package/templates/gantt-chart/node_modules/.vite/deps/package.json +3 -0
  82. package/templates/gantt-chart/node_modules/.vite/deps/react-CsV5wVHy.js +770 -0
  83. package/templates/gantt-chart/node_modules/.vite/deps/react-CsV5wVHy.js.map +1 -0
  84. package/templates/gantt-chart/node_modules/.vite/deps/react-dom.js +185 -0
  85. package/templates/gantt-chart/node_modules/.vite/deps/react-dom.js.map +1 -0
  86. package/templates/gantt-chart/node_modules/.vite/deps/react-dom_client.js +14384 -0
  87. package/templates/gantt-chart/node_modules/.vite/deps/react-dom_client.js.map +1 -0
  88. package/templates/gantt-chart/node_modules/.vite/deps/react.js +2 -0
  89. package/templates/gantt-chart/node_modules/.vite/deps/react_jsx-dev-runtime.js +204 -0
  90. package/templates/gantt-chart/node_modules/.vite/deps/react_jsx-dev-runtime.js.map +1 -0
  91. package/templates/gantt-chart/node_modules/.vite/deps/react_jsx-runtime.js +208 -0
  92. package/templates/gantt-chart/node_modules/.vite/deps/react_jsx-runtime.js.map +1 -0
  93. package/templates/gantt-chart/node_modules/.vite/deps/valibot.js +6623 -0
  94. package/templates/gantt-chart/node_modules/.vite/deps/valibot.js.map +1 -0
  95. package/templates/hello-world/node_modules/.bin/tsc +21 -0
  96. package/templates/hello-world/node_modules/.bin/tsserver +21 -0
  97. package/templates/hello-world/node_modules/.bin/vite +21 -0
  98. package/templates/hello-world/node_modules/.vite/deps/_metadata.json +50 -0
  99. package/templates/hello-world/node_modules/.vite/deps/package.json +3 -0
  100. package/templates/hello-world/node_modules/.vite/deps/react-CsV5wVHy.js +770 -0
  101. package/templates/hello-world/node_modules/.vite/deps/react-CsV5wVHy.js.map +1 -0
  102. package/templates/hello-world/node_modules/.vite/deps/react-dom.js +185 -0
  103. package/templates/hello-world/node_modules/.vite/deps/react-dom.js.map +1 -0
  104. package/templates/hello-world/node_modules/.vite/deps/react-dom_client.js +14384 -0
  105. package/templates/hello-world/node_modules/.vite/deps/react-dom_client.js.map +1 -0
  106. package/templates/hello-world/node_modules/.vite/deps/react.js +2 -0
  107. package/templates/hello-world/node_modules/.vite/deps/react_jsx-dev-runtime.js +204 -0
  108. package/templates/hello-world/node_modules/.vite/deps/react_jsx-dev-runtime.js.map +1 -0
  109. package/templates/hello-world/node_modules/.vite/deps/react_jsx-runtime.js +208 -0
  110. package/templates/hello-world/node_modules/.vite/deps/react_jsx-runtime.js.map +1 -0
  111. package/templates/hello-world/node_modules/.vite/deps/valibot.js +6623 -0
  112. package/templates/hello-world/node_modules/.vite/deps/valibot.js.map +1 -0
  113. package/templates/interactive-resize/node_modules/.bin/tsc +21 -0
  114. package/templates/interactive-resize/node_modules/.bin/tsserver +21 -0
  115. package/templates/interactive-resize/node_modules/.bin/vite +21 -0
  116. package/templates/interactive-resize/node_modules/.vite/deps/_metadata.json +50 -0
  117. package/templates/interactive-resize/node_modules/.vite/deps/package.json +3 -0
  118. package/templates/interactive-resize/node_modules/.vite/deps/react-CsV5wVHy.js +770 -0
  119. package/templates/interactive-resize/node_modules/.vite/deps/react-CsV5wVHy.js.map +1 -0
  120. package/templates/interactive-resize/node_modules/.vite/deps/react-dom.js +185 -0
  121. package/templates/interactive-resize/node_modules/.vite/deps/react-dom.js.map +1 -0
  122. package/templates/interactive-resize/node_modules/.vite/deps/react-dom_client.js +14384 -0
  123. package/templates/interactive-resize/node_modules/.vite/deps/react-dom_client.js.map +1 -0
  124. package/templates/interactive-resize/node_modules/.vite/deps/react.js +2 -0
  125. package/templates/interactive-resize/node_modules/.vite/deps/react_jsx-dev-runtime.js +204 -0
  126. package/templates/interactive-resize/node_modules/.vite/deps/react_jsx-dev-runtime.js.map +1 -0
  127. package/templates/interactive-resize/node_modules/.vite/deps/react_jsx-runtime.js +208 -0
  128. package/templates/interactive-resize/node_modules/.vite/deps/react_jsx-runtime.js.map +1 -0
  129. package/templates/interactive-resize/node_modules/.vite/deps/valibot.js +6623 -0
  130. package/templates/interactive-resize/node_modules/.vite/deps/valibot.js.map +1 -0
  131. package/templates/org-chart/node_modules/.bin/tsc +21 -0
  132. package/templates/org-chart/node_modules/.bin/tsserver +21 -0
  133. package/templates/org-chart/node_modules/.bin/vite +21 -0
  134. package/templates/org-chart/node_modules/.vite/deps/_metadata.json +50 -0
  135. package/templates/org-chart/node_modules/.vite/deps/package.json +3 -0
  136. package/templates/org-chart/node_modules/.vite/deps/react-CsV5wVHy.js +770 -0
  137. package/templates/org-chart/node_modules/.vite/deps/react-CsV5wVHy.js.map +1 -0
  138. package/templates/org-chart/node_modules/.vite/deps/react-dom.js +185 -0
  139. package/templates/org-chart/node_modules/.vite/deps/react-dom.js.map +1 -0
  140. package/templates/org-chart/node_modules/.vite/deps/react-dom_client.js +14384 -0
  141. package/templates/org-chart/node_modules/.vite/deps/react-dom_client.js.map +1 -0
  142. package/templates/org-chart/node_modules/.vite/deps/react.js +2 -0
  143. package/templates/org-chart/node_modules/.vite/deps/react_jsx-dev-runtime.js +204 -0
  144. package/templates/org-chart/node_modules/.vite/deps/react_jsx-dev-runtime.js.map +1 -0
  145. package/templates/org-chart/node_modules/.vite/deps/react_jsx-runtime.js +208 -0
  146. package/templates/org-chart/node_modules/.vite/deps/react_jsx-runtime.js.map +1 -0
  147. package/templates/org-chart/node_modules/.vite/deps/valibot.js +6623 -0
  148. package/templates/org-chart/node_modules/.vite/deps/valibot.js.map +1 -0
  149. package/templates/radar-chart/README.md +55 -0
  150. package/templates/radar-chart/_gitignore +2 -0
  151. package/templates/radar-chart/custom_blocks.json +34 -0
  152. package/templates/radar-chart/dist/assets/index-DOf05oXg.css +2 -0
  153. package/templates/radar-chart/dist/assets/index-DWpNd1qt.js +9 -0
  154. package/templates/radar-chart/dist/custom_blocks.json +34 -0
  155. package/templates/radar-chart/dist/index.html +16 -0
  156. package/templates/radar-chart/index.html +15 -0
  157. package/templates/radar-chart/node_modules/.bin/esbuild +21 -0
  158. package/templates/radar-chart/node_modules/.bin/jiti +21 -0
  159. package/templates/radar-chart/node_modules/.bin/tsc +21 -0
  160. package/templates/radar-chart/node_modules/.bin/tsserver +21 -0
  161. package/templates/radar-chart/node_modules/.bin/tsx +21 -0
  162. package/templates/radar-chart/node_modules/.bin/vite +21 -0
  163. package/templates/radar-chart/node_modules/.vite/deps/_metadata.json +50 -0
  164. package/templates/radar-chart/node_modules/.vite/deps/package.json +3 -0
  165. package/templates/radar-chart/node_modules/.vite/deps/react-CsV5wVHy.js +770 -0
  166. package/templates/radar-chart/node_modules/.vite/deps/react-CsV5wVHy.js.map +1 -0
  167. package/templates/radar-chart/node_modules/.vite/deps/react-dom.js +185 -0
  168. package/templates/radar-chart/node_modules/.vite/deps/react-dom.js.map +1 -0
  169. package/templates/radar-chart/node_modules/.vite/deps/react-dom_client.js +14384 -0
  170. package/templates/radar-chart/node_modules/.vite/deps/react-dom_client.js.map +1 -0
  171. package/templates/radar-chart/node_modules/.vite/deps/react.js +2 -0
  172. package/templates/radar-chart/node_modules/.vite/deps/react_jsx-dev-runtime.js +204 -0
  173. package/templates/radar-chart/node_modules/.vite/deps/react_jsx-dev-runtime.js.map +1 -0
  174. package/templates/radar-chart/node_modules/.vite/deps/react_jsx-runtime.js +208 -0
  175. package/templates/radar-chart/node_modules/.vite/deps/react_jsx-runtime.js.map +1 -0
  176. package/templates/radar-chart/node_modules/.vite/deps/valibot.js +6623 -0
  177. package/templates/radar-chart/node_modules/.vite/deps/valibot.js.map +1 -0
  178. package/templates/radar-chart/package.json +30 -0
  179. package/templates/radar-chart/src/index.css +44 -0
  180. package/templates/radar-chart/src/index.tsx +531 -0
  181. package/templates/radar-chart/tsconfig.json +17 -0
  182. package/templates/radar-chart/vite.config.ts +8 -0
  183. package/templates/table-view/README.md +43 -0
  184. package/templates/table-view/_gitignore +2 -0
  185. package/templates/table-view/custom_blocks.json +9 -0
  186. package/templates/table-view/dist/assets/index-Bd8u_e4X.js +12 -0
  187. package/templates/table-view/dist/assets/index-BkZn3aQZ.css +1 -0
  188. package/templates/table-view/dist/custom_blocks.json +9 -0
  189. package/templates/table-view/dist/index.html +16 -0
  190. package/templates/table-view/index.html +15 -0
  191. package/templates/table-view/node_modules/.bin/esbuild +21 -0
  192. package/templates/table-view/node_modules/.bin/jiti +21 -0
  193. package/templates/table-view/node_modules/.bin/rollup +21 -0
  194. package/templates/table-view/node_modules/.bin/tsc +21 -0
  195. package/templates/table-view/node_modules/.bin/tsserver +21 -0
  196. package/templates/table-view/node_modules/.bin/tsx +21 -0
  197. package/templates/table-view/node_modules/.bin/vite +21 -0
  198. package/templates/table-view/node_modules/.vite/deps/@tanstack_react-table.js +2809 -0
  199. package/templates/table-view/node_modules/.vite/deps/@tanstack_react-table.js.map +1 -0
  200. package/templates/table-view/node_modules/.vite/deps/_metadata.json +56 -0
  201. package/templates/table-view/node_modules/.vite/deps/package.json +3 -0
  202. package/templates/table-view/node_modules/.vite/deps/react-D5jdVkJj.js +790 -0
  203. package/templates/table-view/node_modules/.vite/deps/react-D5jdVkJj.js.map +1 -0
  204. package/templates/table-view/node_modules/.vite/deps/react-dom.js +185 -0
  205. package/templates/table-view/node_modules/.vite/deps/react-dom.js.map +1 -0
  206. package/templates/table-view/node_modules/.vite/deps/react-dom_client.js +14384 -0
  207. package/templates/table-view/node_modules/.vite/deps/react-dom_client.js.map +1 -0
  208. package/templates/table-view/node_modules/.vite/deps/react.js +2 -0
  209. package/templates/table-view/node_modules/.vite/deps/react_jsx-dev-runtime.js +204 -0
  210. package/templates/table-view/node_modules/.vite/deps/react_jsx-dev-runtime.js.map +1 -0
  211. package/templates/table-view/node_modules/.vite/deps/react_jsx-runtime.js +208 -0
  212. package/templates/table-view/node_modules/.vite/deps/react_jsx-runtime.js.map +1 -0
  213. package/templates/table-view/node_modules/.vite/deps/valibot.js +6623 -0
  214. package/templates/table-view/node_modules/.vite/deps/valibot.js.map +1 -0
  215. package/templates/table-view/package.json +31 -0
  216. package/templates/table-view/src/index.css +256 -0
  217. package/templates/table-view/src/index.tsx +1814 -0
  218. package/templates/table-view/src/table-model.ts +663 -0
  219. package/templates/table-view/tsconfig.json +17 -0
  220. package/templates/table-view/vite.config.ts +8 -0
  221. package/templates/us-heatmap/node_modules/.bin/tsc +21 -0
  222. package/templates/us-heatmap/node_modules/.bin/tsserver +21 -0
  223. package/templates/us-heatmap/node_modules/.bin/vite +21 -0
  224. package/templates/us-heatmap/node_modules/.vite/deps/_metadata.json +50 -0
  225. package/templates/us-heatmap/node_modules/.vite/deps/package.json +3 -0
  226. package/templates/us-heatmap/node_modules/.vite/deps/react-CsV5wVHy.js +770 -0
  227. package/templates/us-heatmap/node_modules/.vite/deps/react-CsV5wVHy.js.map +1 -0
  228. package/templates/us-heatmap/node_modules/.vite/deps/react-dom.js +185 -0
  229. package/templates/us-heatmap/node_modules/.vite/deps/react-dom.js.map +1 -0
  230. package/templates/us-heatmap/node_modules/.vite/deps/react-dom_client.js +14384 -0
  231. package/templates/us-heatmap/node_modules/.vite/deps/react-dom_client.js.map +1 -0
  232. package/templates/us-heatmap/node_modules/.vite/deps/react.js +2 -0
  233. package/templates/us-heatmap/node_modules/.vite/deps/react_jsx-dev-runtime.js +204 -0
  234. package/templates/us-heatmap/node_modules/.vite/deps/react_jsx-dev-runtime.js.map +1 -0
  235. package/templates/us-heatmap/node_modules/.vite/deps/react_jsx-runtime.js +208 -0
  236. package/templates/us-heatmap/node_modules/.vite/deps/react_jsx-runtime.js.map +1 -0
  237. package/templates/us-heatmap/node_modules/.vite/deps/valibot.js +6623 -0
  238. package/templates/us-heatmap/node_modules/.vite/deps/valibot.js.map +1 -0
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "radar-chart",
3
+ "private": true,
4
+ "version": "0.0.1",
5
+ "type": "module",
6
+ "notionCustomTemplate": {
7
+ "title": "Radar Chart",
8
+ "description": "Multi-series chart demo with a fixed numeric schema for mapped collection data.",
9
+ "recommended": false
10
+ },
11
+ "scripts": {
12
+ "dev": "vite",
13
+ "build": "vite build",
14
+ "typecheck": "tsc --noEmit"
15
+ },
16
+ "dependencies": {
17
+ "ncblock": "workspace:*",
18
+ "react": "^19.2.5",
19
+ "react-dom": "^19.2.5"
20
+ },
21
+ "devDependencies": {
22
+ "@tailwindcss/vite": "^4.2.4",
23
+ "@types/react": "^19.2.14",
24
+ "@types/react-dom": "^19.2.3",
25
+ "@vitejs/plugin-react": "^6.0.1",
26
+ "tailwindcss": "^4.2.4",
27
+ "typescript": "^6.0.3",
28
+ "vite": "^8.0.10"
29
+ }
30
+ }
@@ -0,0 +1,44 @@
1
+ @import "tailwindcss";
2
+
3
+ :root {
4
+ font-family:
5
+ ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica,
6
+ Arial, sans-serif;
7
+ }
8
+
9
+ :root,
10
+ [data-theme="light"] {
11
+ --app-bg: var(--color-white);
12
+ --card-bg: var(--color-stone-100);
13
+ --surface-bg: var(--color-white);
14
+ --foreground: var(--color-stone-800);
15
+ --muted: color-mix(in oklab, var(--color-stone-800) 45%, transparent);
16
+ --border: color-mix(in oklab, var(--color-stone-800) 12%, transparent);
17
+ --grid-stroke: color-mix(in oklab, var(--color-stone-800) 12%, transparent);
18
+ --series-1: var(--color-blue-600);
19
+ --series-2: var(--color-red-600);
20
+ --series-3: var(--color-green-600);
21
+ --series-4: var(--color-violet-700);
22
+ --series-5: var(--color-amber-600);
23
+ --series-6: var(--color-cyan-600);
24
+ }
25
+
26
+ [data-theme="dark"] {
27
+ --app-bg: var(--color-zinc-900);
28
+ --card-bg: var(--color-zinc-800);
29
+ --surface-bg: var(--color-zinc-900);
30
+ --foreground: var(--color-zinc-200);
31
+ --muted: color-mix(in oklab, var(--color-white) 45%, transparent);
32
+ --border: color-mix(in oklab, var(--color-white) 12%, transparent);
33
+ --grid-stroke: color-mix(in oklab, var(--color-white) 16%, transparent);
34
+ --series-1: var(--color-blue-400);
35
+ --series-2: var(--color-red-400);
36
+ --series-3: var(--color-green-400);
37
+ --series-4: var(--color-violet-400);
38
+ --series-5: var(--color-amber-400);
39
+ --series-6: var(--color-cyan-400);
40
+ }
41
+
42
+ body {
43
+ margin: 0;
44
+ }
@@ -0,0 +1,531 @@
1
+ import {
2
+ NotionCustomBlock,
3
+ type NotionDataSourcePage,
4
+ type NotionPageId,
5
+ useDataSource,
6
+ useDataSourceDefinitions,
7
+ useTheme,
8
+ } from "ncblock"
9
+ import React from "react"
10
+ import ReactDOM from "react-dom/client"
11
+
12
+ import "./index.css"
13
+
14
+ type RadarSeries = {
15
+ id: string
16
+ label: string
17
+ color: string
18
+ values: number[]
19
+ }
20
+
21
+ type RadarSchemaAnalysis = {
22
+ labelKey?: string
23
+ missingFields: string[]
24
+ isReady: boolean
25
+ }
26
+
27
+ function makeFakeItem(
28
+ id: string,
29
+ properties: NotionDataSourcePage["propertiesByKey"],
30
+ ): NotionDataSourcePage {
31
+ return {
32
+ id: id as NotionPageId,
33
+ propertiesById: {},
34
+ propertiesByKey: properties,
35
+ update: async () => ({
36
+ status: "error",
37
+ error: "Sample rows are read-only",
38
+ }),
39
+ }
40
+ }
41
+
42
+ const FAKE_ITEMS: NotionDataSourcePage[] = [
43
+ makeFakeItem("engineering", {
44
+ name: "Engineering",
45
+ speed: 82,
46
+ quality: 91,
47
+ focus: 74,
48
+ collaboration: 88,
49
+ execution: 79,
50
+ }),
51
+ makeFakeItem("design", {
52
+ name: "Design",
53
+ speed: 68,
54
+ quality: 84,
55
+ focus: 90,
56
+ collaboration: 77,
57
+ execution: 70,
58
+ }),
59
+ makeFakeItem("product", {
60
+ name: "Product",
61
+ speed: 75,
62
+ quality: 79,
63
+ focus: 86,
64
+ collaboration: 81,
65
+ execution: 83,
66
+ }),
67
+ ]
68
+
69
+ const EXPECTED_LABEL_KEYS = ["name", "title"]
70
+ const EXPECTED_DIMENSION_KEYS = [
71
+ "speed",
72
+ "quality",
73
+ "focus",
74
+ "collaboration",
75
+ "execution",
76
+ ]
77
+ const SERIES_COLORS = [
78
+ "var(--series-1)",
79
+ "var(--series-2)",
80
+ "var(--series-3)",
81
+ "var(--series-4)",
82
+ "var(--series-5)",
83
+ "var(--series-6)",
84
+ ]
85
+
86
+ function isFiniteNumber(value: unknown): value is number {
87
+ return typeof value === "number" && Number.isFinite(value)
88
+ }
89
+
90
+ function formatDimensionLabel(key: string): string {
91
+ return key.replace(/([a-z])([A-Z])/g, "$1 $2")
92
+ }
93
+
94
+ function getLabelKey(items: NotionDataSourcePage[]): string | undefined {
95
+ for (const key of EXPECTED_LABEL_KEYS) {
96
+ if (items.some(item => typeof item.propertiesByKey[key] === "string")) {
97
+ return key
98
+ }
99
+ }
100
+ return undefined
101
+ }
102
+
103
+ function analyzeRadarSchema(
104
+ items: NotionDataSourcePage[],
105
+ ): RadarSchemaAnalysis {
106
+ const labelKey = getLabelKey(items)
107
+ const missingFields: string[] = []
108
+
109
+ if (!labelKey) {
110
+ missingFields.push("name (or title)")
111
+ }
112
+
113
+ for (const key of EXPECTED_DIMENSION_KEYS) {
114
+ if (!items.some(item => isFiniteNumber(item.propertiesByKey[key]))) {
115
+ missingFields.push(key)
116
+ }
117
+ }
118
+
119
+ return {
120
+ labelKey,
121
+ missingFields,
122
+ isReady: missingFields.length === 0,
123
+ }
124
+ }
125
+
126
+ function getRoundedMaxValue(
127
+ items: NotionDataSourcePage[],
128
+ keys: string[],
129
+ ): number {
130
+ const maxValue = items.reduce((currentMax, item) => {
131
+ return keys.reduce((rowMax, key) => {
132
+ const value = item.propertiesByKey[key]
133
+ return isFiniteNumber(value) ? Math.max(rowMax, value) : rowMax
134
+ }, currentMax)
135
+ }, 0)
136
+
137
+ if (maxValue <= 10) {
138
+ return 10
139
+ }
140
+
141
+ if (maxValue <= 100) {
142
+ return Math.ceil(maxValue / 10) * 10
143
+ }
144
+
145
+ return Math.ceil(maxValue / 25) * 25
146
+ }
147
+
148
+ function RadarChart(props: {
149
+ items: NotionDataSourcePage[]
150
+ isUsingFallbackData: boolean
151
+ analysis: RadarSchemaAnalysis
152
+ queryError: string | undefined
153
+ hasMore: boolean
154
+ isLoading: boolean
155
+ onFetchMore: () => void
156
+ }) {
157
+ const {
158
+ items,
159
+ isUsingFallbackData,
160
+ analysis,
161
+ queryError,
162
+ hasMore,
163
+ isLoading,
164
+ onFetchMore,
165
+ } = props
166
+
167
+ const { labelKey } = analysis
168
+ const dimensionKeys = EXPECTED_DIMENSION_KEYS
169
+ const maxValue = React.useMemo(
170
+ () => getRoundedMaxValue(items, dimensionKeys),
171
+ [dimensionKeys, items],
172
+ )
173
+ const chartSize = 520
174
+ const radius = 170
175
+ const levels = 4
176
+ const labelRadius = radius + 34
177
+ const angleStep =
178
+ dimensionKeys.length > 0 ? (Math.PI * 2) / dimensionKeys.length : 0
179
+
180
+ // Center the chart's bounding box (including labels) within the viewBox.
181
+ // The pentagon is asymmetric vertically: with a vertex pointing up, the
182
+ // top vertex sits at -radius from center, but the bottom vertices only
183
+ // reach +radius * sin(54°) ≈ +0.809 * radius. Account for label sizing
184
+ // so the chart looks visually centered.
185
+ const verticalExtents = React.useMemo(() => {
186
+ let topMost = 0
187
+ let bottomMost = 0
188
+ for (let i = 0; i < dimensionKeys.length; i += 1) {
189
+ const angle = -Math.PI / 2 + angleStep * i
190
+ const labelY = Math.sin(angle) * labelRadius
191
+ const isAbove = labelY < -10
192
+ const isBelow = labelY > 10
193
+ const textHalfHeight = 6
194
+ const top = isAbove
195
+ ? labelY - textHalfHeight * 2
196
+ : labelY - textHalfHeight
197
+ const bottom = isBelow
198
+ ? labelY + textHalfHeight * 2
199
+ : labelY + textHalfHeight
200
+ if (top < topMost) {
201
+ topMost = top
202
+ }
203
+ if (bottom > bottomMost) {
204
+ bottomMost = bottom
205
+ }
206
+ }
207
+ return { topMost, bottomMost }
208
+ }, [angleStep, dimensionKeys.length, labelRadius])
209
+
210
+ const cx = chartSize / 2
211
+ const cy =
212
+ chartSize / 2 - (verticalExtents.topMost + verticalExtents.bottomMost) / 2
213
+
214
+ const series = React.useMemo<RadarSeries[]>(() => {
215
+ return items.map((item, index) => {
216
+ const labelValue = labelKey ? item.propertiesByKey[labelKey] : undefined
217
+ return {
218
+ id: item.id,
219
+ label:
220
+ (typeof labelValue === "string" && labelValue) ||
221
+ `Series ${index + 1}`,
222
+ color: SERIES_COLORS[index % SERIES_COLORS.length],
223
+ values: dimensionKeys.map(key => {
224
+ const value = item.propertiesByKey[key]
225
+ return isFiniteNumber(value) ? value : 0
226
+ }),
227
+ }
228
+ })
229
+ }, [dimensionKeys, items, labelKey])
230
+
231
+ function getPoint(value: number, index: number) {
232
+ const ratio = Math.max(0, Math.min(1, value / maxValue))
233
+ const angle = -Math.PI / 2 + angleStep * index
234
+ return {
235
+ x: cx + Math.cos(angle) * radius * ratio,
236
+ y: cy + Math.sin(angle) * radius * ratio,
237
+ }
238
+ }
239
+
240
+ function getPath(values: number[]) {
241
+ return (
242
+ values
243
+ .map((value, index) => {
244
+ const point = getPoint(value, index)
245
+ return `${index === 0 ? "M" : "L"} ${point.x} ${point.y}`
246
+ })
247
+ .join(" ") + " Z"
248
+ )
249
+ }
250
+
251
+ return (
252
+ <div className="grid gap-5">
253
+ <div className="rounded-xl border border-(--border) bg-(--card-bg) px-4 py-4 text-sm leading-6 text-(--muted)">
254
+ {isUsingFallbackData
255
+ ? "Showing fake sample data until a collection is mapped."
256
+ : `Showing ${items.length} row${items.length === 1 ? "" : "s"} from the mapped collection.`}
257
+ {queryError ? ` Query note: ${queryError}` : ""}
258
+ </div>
259
+
260
+ <div className="grid grid-cols-[minmax(0,1fr)_220px] items-start gap-6">
261
+ <div className="overflow-x-auto rounded-2xl border border-(--border) bg-(--surface-bg) p-5">
262
+ <svg
263
+ className="block h-auto min-w-[360px] w-full"
264
+ viewBox={`0 0 ${chartSize} ${chartSize}`}
265
+ >
266
+ {Array.from({ length: levels }, (_, levelIndex) => {
267
+ const level = (levelIndex + 1) / levels
268
+ const points = dimensionKeys
269
+ .map((_, index) => {
270
+ const angle = -Math.PI / 2 + angleStep * index
271
+ const x = cx + Math.cos(angle) * radius * level
272
+ const y = cy + Math.sin(angle) * radius * level
273
+ return `${x},${y}`
274
+ })
275
+ .join(" ")
276
+
277
+ return (
278
+ <polygon
279
+ key={level}
280
+ points={points}
281
+ fill="none"
282
+ stroke="var(--grid-stroke)"
283
+ strokeWidth={1}
284
+ />
285
+ )
286
+ })}
287
+
288
+ {dimensionKeys.map((key, index) => {
289
+ const angle = -Math.PI / 2 + angleStep * index
290
+ const cosA = Math.cos(angle)
291
+ const sinA = Math.sin(angle)
292
+ const axisX = cx + cosA * radius
293
+ const axisY = cy + sinA * radius
294
+ const labelX = cx + cosA * labelRadius
295
+ const labelY = cy + sinA * labelRadius
296
+ const horizontalThreshold = 0.15
297
+
298
+ return (
299
+ <g key={key}>
300
+ <line
301
+ x1={cx}
302
+ y1={cy}
303
+ x2={axisX}
304
+ y2={axisY}
305
+ stroke="var(--grid-stroke)"
306
+ strokeWidth={1}
307
+ />
308
+ <text
309
+ x={labelX}
310
+ y={labelY}
311
+ fill="var(--foreground)"
312
+ fontSize={12}
313
+ textAnchor={
314
+ Math.abs(cosA) < horizontalThreshold
315
+ ? "middle"
316
+ : cosA < 0
317
+ ? "end"
318
+ : "start"
319
+ }
320
+ dominantBaseline={
321
+ Math.abs(sinA) < horizontalThreshold
322
+ ? "middle"
323
+ : sinA < 0
324
+ ? "auto"
325
+ : "hanging"
326
+ }
327
+ >
328
+ {formatDimensionLabel(key)}
329
+ </text>
330
+ </g>
331
+ )
332
+ })}
333
+
334
+ {series.map(seriesItem => (
335
+ <path
336
+ key={seriesItem.id}
337
+ d={getPath(seriesItem.values)}
338
+ fill={seriesItem.color}
339
+ fillOpacity={0.14}
340
+ stroke={seriesItem.color}
341
+ strokeWidth={2}
342
+ />
343
+ ))}
344
+
345
+ {series.flatMap(seriesItem =>
346
+ seriesItem.values.map((value, index) => {
347
+ const point = getPoint(value, index)
348
+ return (
349
+ <circle
350
+ key={`${seriesItem.id}-${dimensionKeys[index]}`}
351
+ cx={point.x}
352
+ cy={point.y}
353
+ r={4}
354
+ fill={seriesItem.color}
355
+ />
356
+ )
357
+ }),
358
+ )}
359
+ </svg>
360
+ </div>
361
+
362
+ <div className="rounded-2xl border border-(--border) bg-(--card-bg) p-5">
363
+ <div className="mb-3 text-base font-semibold text-(--foreground)">
364
+ Legend
365
+ </div>
366
+ <div className="mb-5 grid gap-2.5">
367
+ {series.map(seriesItem => (
368
+ <div
369
+ key={seriesItem.id}
370
+ className="grid grid-cols-[12px_minmax(0,1fr)] items-center gap-2.5"
371
+ >
372
+ <div
373
+ className="h-3 w-3 rounded-full"
374
+ style={{
375
+ background: seriesItem.color,
376
+ }}
377
+ />
378
+ <div className="text-sm text-(--foreground)">
379
+ {seriesItem.label}
380
+ </div>
381
+ </div>
382
+ ))}
383
+ </div>
384
+
385
+ <div className="text-[13px] leading-[1.7] text-(--muted)">
386
+ <div>Max scale: {maxValue}</div>
387
+ <div>Label field: {labelKey ?? "name"}</div>
388
+ <div>Dimensions: {dimensionKeys.join(", ")}</div>
389
+ </div>
390
+
391
+ {hasMore ? (
392
+ <button
393
+ onClick={onFetchMore}
394
+ disabled={isLoading}
395
+ className="mt-4 w-full rounded-lg border border-(--border) bg-(--surface-bg) px-3 py-2.5 text-(--foreground)"
396
+ style={{ cursor: isLoading ? "progress" : "pointer" }}
397
+ >
398
+ {isLoading ? "Loading..." : "Fetch more"}
399
+ </button>
400
+ ) : null}
401
+ </div>
402
+ </div>
403
+ </div>
404
+ )
405
+ }
406
+
407
+ function SetupHint(props: {
408
+ missingFields: string[]
409
+ dataSourceKey: string
410
+ isUsingFallbackData: boolean
411
+ }) {
412
+ const { missingFields, dataSourceKey, isUsingFallbackData } = props
413
+
414
+ return (
415
+ <div className="mb-6 rounded-[14px] border border-(--border) bg-(--card-bg) p-5">
416
+ <div className="mb-2 text-lg font-semibold text-(--foreground)">
417
+ Set up the source database
418
+ </div>
419
+ <div className="mb-3 text-sm leading-[1.7] text-(--muted)">
420
+ This demo is hardcoded to one radar schema. Map a data source with key{" "}
421
+ <code>{dataSourceKey}</code> and create these exact semantic fields:
422
+ </div>
423
+ <div className="mb-3 text-sm leading-[1.8] text-(--foreground)">
424
+ <div>
425
+ <code>name</code> or <code>title</code> as the row label
426
+ </div>
427
+ <div>
428
+ <code>speed</code>, <code>quality</code>, <code>focus</code>,{" "}
429
+ <code>collaboration</code>, <code>execution</code> as number columns
430
+ </div>
431
+ </div>
432
+ {missingFields.length > 0 ? (
433
+ <div className="text-sm leading-[1.7] text-(--muted)">
434
+ Missing right now: <code>{missingFields.join(", ")}</code>
435
+ </div>
436
+ ) : null}
437
+ <div className="mt-3 text-sm leading-[1.7] text-(--muted)">
438
+ {isUsingFallbackData
439
+ ? "Showing built-in sample data below until the mapped collection matches this schema."
440
+ : "Showing your mapped collection below."}
441
+ </div>
442
+ </div>
443
+ )
444
+ }
445
+
446
+ function App() {
447
+ const theme = useTheme()
448
+ const dataSources = useDataSourceDefinitions()
449
+ const activeDataSourceKey = dataSources[0]?.key ?? "default"
450
+ const query = useDataSource(activeDataSourceKey)
451
+ const colorTheme = theme === "dark" ? "dark" : "light"
452
+ const mappedAnalysis = analyzeRadarSchema(query.items)
453
+ const isUsingFallbackData = !mappedAnalysis.isReady
454
+ const items = isUsingFallbackData ? FAKE_ITEMS : query.items
455
+ const analysis = isUsingFallbackData
456
+ ? analyzeRadarSchema(FAKE_ITEMS)
457
+ : mappedAnalysis
458
+
459
+ return (
460
+ <div
461
+ data-theme={colorTheme}
462
+ className="bg-(--app-bg) p-8 text-(--foreground)"
463
+ >
464
+ <div className="mb-6">
465
+ <h1 className="mb-2 text-[32px] font-bold leading-tight tracking-[-0.03em]">
466
+ Radar Chart
467
+ </h1>
468
+ <p className="m-0 max-w-[760px] text-[15px] leading-6 text-(--muted)">
469
+ Each row in the mapped collection becomes one spider-chart polygon.
470
+ One text field is used for the legend, and this demo expects one fixed
471
+ set of score columns.
472
+ </p>
473
+ </div>
474
+
475
+ <div className="mb-6 grid grid-cols-[repeat(auto-fit,minmax(220px,1fr))] gap-5">
476
+ {[
477
+ {
478
+ label: "Active data source",
479
+ value: activeDataSourceKey,
480
+ },
481
+ {
482
+ label: "Rows",
483
+ value: String(items.length),
484
+ },
485
+ {
486
+ label: "Mode",
487
+ value: isUsingFallbackData
488
+ ? "Sample preview data"
489
+ : "Mapped collection",
490
+ },
491
+ ].map(card => (
492
+ <div
493
+ key={card.label}
494
+ className="rounded-[14px] border border-(--border) bg-(--card-bg) p-[18px]"
495
+ >
496
+ <div className="mb-2 text-xs font-semibold uppercase tracking-[0.04em] text-(--muted)">
497
+ {card.label}
498
+ </div>
499
+ <div className="text-lg font-semibold text-(--foreground)">
500
+ {card.value}
501
+ </div>
502
+ </div>
503
+ ))}
504
+ </div>
505
+
506
+ {isUsingFallbackData ? (
507
+ <SetupHint
508
+ missingFields={mappedAnalysis.missingFields}
509
+ dataSourceKey={activeDataSourceKey}
510
+ isUsingFallbackData={isUsingFallbackData}
511
+ />
512
+ ) : null}
513
+
514
+ <RadarChart
515
+ items={items}
516
+ isUsingFallbackData={isUsingFallbackData}
517
+ analysis={analysis}
518
+ queryError={query.error}
519
+ hasMore={query.hasMore}
520
+ isLoading={query.isLoading}
521
+ onFetchMore={query.fetchMore}
522
+ />
523
+ </div>
524
+ )
525
+ }
526
+
527
+ ReactDOM.createRoot(document.getElementById("root")!).render(
528
+ <NotionCustomBlock>
529
+ <App />
530
+ </NotionCustomBlock>,
531
+ )
@@ -0,0 +1,17 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "strict": true,
7
+ "noUnusedLocals": true,
8
+ "noUnusedParameters": true,
9
+ "types": ["vite/client"],
10
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
11
+ "skipLibCheck": true,
12
+ "esModuleInterop": true,
13
+ "isolatedModules": true,
14
+ "jsx": "react-jsx"
15
+ },
16
+ "include": ["src", "vite.config.ts"]
17
+ }
@@ -0,0 +1,8 @@
1
+ import tailwindcss from "@tailwindcss/vite"
2
+ import react from "@vitejs/plugin-react"
3
+ import { notionCustomBlock } from "ncblock/vite"
4
+ import { defineConfig } from "vite"
5
+
6
+ export default defineConfig({
7
+ plugins: [react(), tailwindcss(), notionCustomBlock()],
8
+ })
@@ -0,0 +1,43 @@
1
+ # {{name}}
2
+
3
+ A [Notion custom view](https://developers.notion.com) block.
4
+
5
+ ## Development
6
+
7
+ ```bash
8
+ npm run dev # Start Vite dev server
9
+ npm run build # Build to dist/
10
+ ```
11
+
12
+ From the repo root you can also run just this template with:
13
+
14
+ ```bash
15
+ pnpm --filter table-view dev
16
+ ```
17
+
18
+ If you run `pnpm run dev` at the repo root, every template starts at once and
19
+ `table-view` is available at `http://localhost:9882`.
20
+
21
+ ## Project structure
22
+
23
+ ```text
24
+ src/
25
+ index.tsx # TanStack Table-powered custom view
26
+ index.css # Tailwind CSS entrypoint
27
+ index.html # Vite entry HTML
28
+ custom_blocks.json # Manifest declaring required data sources (served at runtime)
29
+ dist/ # Built output: index.html + assets/ + custom_blocks.json
30
+ ```
31
+
32
+ This template includes Tailwind by default and renders every supported
33
+ `ncblock` value shape: text, numbers, booleans, dates,
34
+ string arrays, and relations. It also includes search, column visibility
35
+ controls, density switching, and relation pills that render `-> title` when a
36
+ related page is loaded or `-> uuid` when it is not.
37
+
38
+ ## SDK hooks
39
+
40
+ - **`useCustomBlockContext()`** -- returns `{ customBlockId, parent, page }` describing the block's location in the document tree
41
+ - **`useTheme()`** -- returns the host's current theme (`"light"` or `"dark"`)
42
+ - **`useDataSourceDefinitions()`** -- returns resolved data-source definitions
43
+ - **`useDataSource(key)`** -- returns `{ items, collectionSchema, propertySchemasById, propertySchemasByKey, isLoading, hasMore, fetchMore, error }`. Each `item` has `{ id, propertiesById, propertiesByKey }`. The four built-ins (`created_time`, `last_edited_time`, `created_by`, `last_edited_by`) appear in `propertiesById` / `propertySchemasById`, never in the `*ByKey` views.
@@ -0,0 +1,2 @@
1
+ node_modules
2
+ dist
@@ -0,0 +1,9 @@
1
+ {
2
+ "version": 1,
3
+ "dataSources": {
4
+ "default": {
5
+ "name": "Table data",
6
+ "description": "Rows to render in the tabular view."
7
+ }
8
+ }
9
+ }