@notionhq/custom-blocks 0.1.46 → 0.1.47
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/README.md +3 -4
- package/dist/customBlock.d.ts +76 -3
- package/dist/customBlock.d.ts.map +1 -1
- package/dist/customBlock.js +120 -2
- package/dist/react/useRuntimeState.d.ts +3 -5
- package/dist/react/useRuntimeState.d.ts.map +1 -1
- package/dist/react/useRuntimeState.js +10 -12
- package/dist/version.js +1 -1
- package/docs/block-location.md +51 -55
- package/docs/data-sources.md +3 -3
- package/docs/lifecycle.md +50 -10
- package/docs/users.md +4 -3
- package/package.json +1 -1
- package/src/customBlock.ts +139 -3
- package/src/react/useRuntimeState.ts +25 -12
package/README.md
CHANGED
|
@@ -113,10 +113,9 @@ Hosts that omit a contrast mode use standard contrast.
|
|
|
113
113
|
|
|
114
114
|
`NotionTokenScopeProps` has one required `children` prop.
|
|
115
115
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
See [Block location and appearance](./docs/block-location.md).
|
|
116
|
+
CSS variables apply only within `<NotionTokenScope>`. Place portal containers inside that scope, or wrap the portal content in another `<NotionTokenScope>`.
|
|
117
|
+
|
|
118
|
+
Outside React, use `customBlock.getTheme()` and `customBlock.getContrastMode()` to read the current appearance, or `customBlock.subscribeToTheme()` and `customBlock.subscribeToContrastMode()` to follow changes. See [Block location and appearance](./docs/block-location.md) for details.
|
|
120
119
|
|
|
121
120
|
## Reference
|
|
122
121
|
|
package/dist/customBlock.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ import type { NotionTheme } from "./protocol/theme.js";
|
|
|
8
8
|
import type { NotionUser } from "./protocol/users/user.js";
|
|
9
9
|
import { autoResize } from "./autoResize.js";
|
|
10
10
|
import { subscribeToDataSource } from "./bridge/dataSources/subscribe.js";
|
|
11
|
+
/** @deprecated Use individual context types and getters after `initCustomBlock` resolves. */
|
|
11
12
|
export type CustomBlockState = {
|
|
12
13
|
status: "uninitialized";
|
|
13
14
|
theme: NotionTheme;
|
|
@@ -22,20 +23,91 @@ export type CustomBlockState = {
|
|
|
22
23
|
currentUser: NotionUser;
|
|
23
24
|
dataSources: NotionDataSource[];
|
|
24
25
|
};
|
|
26
|
+
type Unsubscribe = () => void;
|
|
25
27
|
/**
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
+
* Read block and app context with getters, or follow changes with subscriptions.
|
|
29
|
+
* Getters throw if called before `initCustomBlock()` completes. Subscriptions
|
|
30
|
+
* can be registered before initialization and deliver the initial value once
|
|
31
|
+
* initialization completes, followed by changes to that value.
|
|
32
|
+
*
|
|
33
|
+
* Each subscription returns an unsubscribe function. Call it when the
|
|
34
|
+
* subscription is no longer needed.
|
|
28
35
|
*/
|
|
29
36
|
export declare const customBlock: {
|
|
30
|
-
|
|
37
|
+
/** @deprecated Use an individual subscription method, such as `subscribeToTheme()` or `subscribeToCurrentUser()`. */
|
|
38
|
+
subscribe(listener: () => void): Unsubscribe;
|
|
39
|
+
/** @deprecated Use an individual getter method, such as `getTheme()` or `getCurrentUser()`, after `initCustomBlock()` resolves. */
|
|
31
40
|
getState(): CustomBlockState;
|
|
41
|
+
/**
|
|
42
|
+
* Get the current user viewing the block.
|
|
43
|
+
* Throws if called before SDK initialization completes.
|
|
44
|
+
*/
|
|
32
45
|
getCurrentUser(): NotionUser;
|
|
46
|
+
/**
|
|
47
|
+
* Subscribes to the current `currentUser` and future changes.
|
|
48
|
+
* Waits for initialization before delivering the initial value.
|
|
49
|
+
*/
|
|
50
|
+
subscribeToCurrentUser(listener: (value: NotionUser) => void): Unsubscribe;
|
|
51
|
+
/**
|
|
52
|
+
* Get the host's current theme.
|
|
53
|
+
* Throws if called before SDK initialization completes.
|
|
54
|
+
*/
|
|
33
55
|
getTheme(): NotionTheme;
|
|
56
|
+
/**
|
|
57
|
+
* Subscribes to the current `theme` and future changes.
|
|
58
|
+
* Waits for initialization before delivering the initial value.
|
|
59
|
+
*/
|
|
60
|
+
subscribeToTheme(listener: (value: NotionTheme) => void): Unsubscribe;
|
|
61
|
+
/**
|
|
62
|
+
* Get the host's current contrast mode.
|
|
63
|
+
* Throws if called before SDK initialization completes.
|
|
64
|
+
*/
|
|
34
65
|
getContrastMode(): NotionContrastMode;
|
|
66
|
+
/**
|
|
67
|
+
* Subscribes to the current `contrastMode` and future changes.
|
|
68
|
+
* Waits for initialization before delivering the initial value.
|
|
69
|
+
*/
|
|
70
|
+
subscribeToContrastMode(listener: (value: NotionContrastMode) => void): Unsubscribe;
|
|
71
|
+
/**
|
|
72
|
+
* Get the custom block's ID. The ID is stable for the lifetime of the block.
|
|
73
|
+
* Throws if called before SDK initialization completes.
|
|
74
|
+
*/
|
|
35
75
|
getBlockId(): NotionBlockId;
|
|
76
|
+
/**
|
|
77
|
+
* Delivers the block ID once. The ID is stable for the lifetime of the block.
|
|
78
|
+
* Waits for initialization before delivering the initial value.
|
|
79
|
+
*/
|
|
80
|
+
subscribeToBlockId(listener: (value: NotionBlockId) => void): Unsubscribe;
|
|
81
|
+
/**
|
|
82
|
+
* Get the custom block's parent.
|
|
83
|
+
* Throws if called before SDK initialization completes.
|
|
84
|
+
*/
|
|
36
85
|
getParent(): NotionParent;
|
|
86
|
+
/**
|
|
87
|
+
* Subscribes to the current `parent` and future changes.
|
|
88
|
+
* Waits for initialization before delivering the initial value.
|
|
89
|
+
*/
|
|
90
|
+
subscribeToParent(listener: (value: NotionParent) => void): Unsubscribe;
|
|
91
|
+
/**
|
|
92
|
+
* Get the containing page and its parent.
|
|
93
|
+
* Throws if called before SDK initialization completes.
|
|
94
|
+
*/
|
|
37
95
|
getPage(): CustomBlockPage;
|
|
96
|
+
/**
|
|
97
|
+
* Subscribes to the current `page` and future changes.
|
|
98
|
+
* Waits for initialization before delivering the initial value.
|
|
99
|
+
*/
|
|
100
|
+
subscribeToPage(listener: (value: CustomBlockPage) => void): Unsubscribe;
|
|
101
|
+
/**
|
|
102
|
+
* Get the block's declared data source manifest.
|
|
103
|
+
* Throws if called before SDK initialization completes.
|
|
104
|
+
*/
|
|
38
105
|
getManifest(): CustomBlockManifest;
|
|
106
|
+
/**
|
|
107
|
+
* Subscribes to the current `manifest` and future changes.
|
|
108
|
+
* Waits for initialization before delivering the initial value.
|
|
109
|
+
*/
|
|
110
|
+
subscribeToManifest(listener: (value: CustomBlockManifest) => void): Unsubscribe;
|
|
39
111
|
autoResize: typeof autoResize;
|
|
40
112
|
/**
|
|
41
113
|
* Registers a listener that receives the current query snapshot and later updates.
|
|
@@ -43,4 +115,5 @@ export declare const customBlock: {
|
|
|
43
115
|
*/
|
|
44
116
|
subscribeToDataSource: typeof subscribeToDataSource;
|
|
45
117
|
};
|
|
118
|
+
export {};
|
|
46
119
|
//# sourceMappingURL=customBlock.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"customBlock.d.ts","sourceRoot":"","sources":["../src/customBlock.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,8CAA8C,CAAA;AACtF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,4DAA4D,CAAA;AAClG,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yCAAyC,CAAA;AAC5E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,8CAA8C,CAAA;AACvF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gDAAgD,CAAA;AACrF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4CAA4C,CAAA;AAC9E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2CAA2C,CAAA;AAC5E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gDAAgD,CAAA;AAChF,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAC5C,OAAO,EAAE,qBAAqB,EAAE,MAAM,mCAAmC,CAAA;
|
|
1
|
+
{"version":3,"file":"customBlock.d.ts","sourceRoot":"","sources":["../src/customBlock.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,8CAA8C,CAAA;AACtF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,4DAA4D,CAAA;AAClG,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yCAAyC,CAAA;AAC5E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,8CAA8C,CAAA;AACvF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gDAAgD,CAAA;AACrF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4CAA4C,CAAA;AAC9E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2CAA2C,CAAA;AAC5E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gDAAgD,CAAA;AAChF,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAC5C,OAAO,EAAE,qBAAqB,EAAE,MAAM,mCAAmC,CAAA;AAQzE,6FAA6F;AAC7F,MAAM,MAAM,gBAAgB,GACzB;IACA,MAAM,EAAE,eAAe,CAAA;IACvB,KAAK,EAAE,WAAW,CAAA;IAClB,YAAY,EAAE,kBAAkB,CAAA;CAC/B,GACD;IACA,MAAM,EAAE,aAAa,CAAA;IACrB,KAAK,EAAE,WAAW,CAAA;IAClB,YAAY,EAAE,kBAAkB,CAAA;IAChC,OAAO,EAAE,aAAa,CAAA;IACtB,MAAM,EAAE,YAAY,CAAA;IACpB,IAAI,EAAE,eAAe,CAAA;IACrB,WAAW,EAAE,UAAU,CAAA;IACvB,WAAW,EAAE,gBAAgB,EAAE,CAAA;CAC9B,CAAA;AAEJ,KAAK,WAAW,GAAG,MAAM,IAAI,CAAA;AAE7B;;;;;;;;GAQG;AACH,eAAO,MAAM,WAAW;IACvB,qHAAqH;wBACjG,MAAM,IAAI,GAAG,WAAW;IAI5C,mIAAmI;gBACvH,gBAAgB;IAI5B;;;OAGG;sBACe,UAAU;IAI5B;;;OAGG;qCAC8B,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,GAAG,WAAW;IAI1E;;;OAGG;gBACS,WAAW;IAIvB;;;OAGG;+BACwB,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,GAAG,WAAW;IAIrE;;;OAGG;uBACgB,kBAAkB;IAIrC;;;OAGG;sCAEQ,CAAC,KAAK,EAAE,kBAAkB,KAAK,IAAI,GAC3C,WAAW;IAId;;;OAGG;kBACW,aAAa;IAI3B;;;OAGG;iCAC0B,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,GAAG,WAAW;IAIzE;;;OAGG;iBACU,YAAY;IAIzB;;;OAGG;gCACyB,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,GAAG,WAAW;IAIvE;;;OAGG;eACQ,eAAe;IAI1B;;;OAGG;8BACuB,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,GAAG,WAAW;IAIxE;;;OAGG;mBACY,mBAAmB;IAIlC;;;OAGG;kCAEQ,CAAC,KAAK,EAAE,mBAAmB,KAAK,IAAI,GAC5C,WAAW;;IAMd;;;OAGG;;CAEH,CAAA"}
|
package/dist/customBlock.js
CHANGED
|
@@ -1,38 +1,123 @@
|
|
|
1
1
|
import { autoResize } from "./autoResize.js";
|
|
2
2
|
import { subscribeToDataSource } from "./bridge/dataSources/subscribe.js";
|
|
3
|
+
import { notifyListener } from "./bridge/notifyListener.js";
|
|
3
4
|
import { customBlockHost } from "./bridge/sandboxClient.js";
|
|
4
5
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
6
|
+
* Read block and app context with getters, or follow changes with subscriptions.
|
|
7
|
+
* Getters throw if called before `initCustomBlock()` completes. Subscriptions
|
|
8
|
+
* can be registered before initialization and deliver the initial value once
|
|
9
|
+
* initialization completes, followed by changes to that value.
|
|
10
|
+
*
|
|
11
|
+
* Each subscription returns an unsubscribe function. Call it when the
|
|
12
|
+
* subscription is no longer needed.
|
|
7
13
|
*/
|
|
8
14
|
export const customBlock = {
|
|
15
|
+
/** @deprecated Use an individual subscription method, such as `subscribeToTheme()` or `subscribeToCurrentUser()`. */
|
|
9
16
|
subscribe(listener) {
|
|
10
17
|
return customBlockHost.subscribe(listener);
|
|
11
18
|
},
|
|
19
|
+
/** @deprecated Use an individual getter method, such as `getTheme()` or `getCurrentUser()`, after `initCustomBlock()` resolves. */
|
|
12
20
|
getState() {
|
|
13
21
|
return toPublicState(customBlockHost.getState());
|
|
14
22
|
},
|
|
23
|
+
/**
|
|
24
|
+
* Get the current user viewing the block.
|
|
25
|
+
* Throws if called before SDK initialization completes.
|
|
26
|
+
*/
|
|
15
27
|
getCurrentUser() {
|
|
16
28
|
return getInitializedHostState("getCurrentUser").currentUser;
|
|
17
29
|
},
|
|
30
|
+
/**
|
|
31
|
+
* Subscribes to the current `currentUser` and future changes.
|
|
32
|
+
* Waits for initialization before delivering the initial value.
|
|
33
|
+
*/
|
|
34
|
+
subscribeToCurrentUser(listener) {
|
|
35
|
+
return subscribeToContext({ key: "currentUser", listener });
|
|
36
|
+
},
|
|
37
|
+
/**
|
|
38
|
+
* Get the host's current theme.
|
|
39
|
+
* Throws if called before SDK initialization completes.
|
|
40
|
+
*/
|
|
18
41
|
getTheme() {
|
|
19
42
|
return getInitializedHostState("getTheme").theme;
|
|
20
43
|
},
|
|
44
|
+
/**
|
|
45
|
+
* Subscribes to the current `theme` and future changes.
|
|
46
|
+
* Waits for initialization before delivering the initial value.
|
|
47
|
+
*/
|
|
48
|
+
subscribeToTheme(listener) {
|
|
49
|
+
return subscribeToContext({ key: "theme", listener });
|
|
50
|
+
},
|
|
51
|
+
/**
|
|
52
|
+
* Get the host's current contrast mode.
|
|
53
|
+
* Throws if called before SDK initialization completes.
|
|
54
|
+
*/
|
|
21
55
|
getContrastMode() {
|
|
22
56
|
return getInitializedHostState("getContrastMode").contrastMode;
|
|
23
57
|
},
|
|
58
|
+
/**
|
|
59
|
+
* Subscribes to the current `contrastMode` and future changes.
|
|
60
|
+
* Waits for initialization before delivering the initial value.
|
|
61
|
+
*/
|
|
62
|
+
subscribeToContrastMode(listener) {
|
|
63
|
+
return subscribeToContext({ key: "contrastMode", listener });
|
|
64
|
+
},
|
|
65
|
+
/**
|
|
66
|
+
* Get the custom block's ID. The ID is stable for the lifetime of the block.
|
|
67
|
+
* Throws if called before SDK initialization completes.
|
|
68
|
+
*/
|
|
24
69
|
getBlockId() {
|
|
25
70
|
return getInitializedHostState("getBlockId").blockId;
|
|
26
71
|
},
|
|
72
|
+
/**
|
|
73
|
+
* Delivers the block ID once. The ID is stable for the lifetime of the block.
|
|
74
|
+
* Waits for initialization before delivering the initial value.
|
|
75
|
+
*/
|
|
76
|
+
subscribeToBlockId(listener) {
|
|
77
|
+
return subscribeToContext({ key: "blockId", listener });
|
|
78
|
+
},
|
|
79
|
+
/**
|
|
80
|
+
* Get the custom block's parent.
|
|
81
|
+
* Throws if called before SDK initialization completes.
|
|
82
|
+
*/
|
|
27
83
|
getParent() {
|
|
28
84
|
return getInitializedHostState("getParent").parent;
|
|
29
85
|
},
|
|
86
|
+
/**
|
|
87
|
+
* Subscribes to the current `parent` and future changes.
|
|
88
|
+
* Waits for initialization before delivering the initial value.
|
|
89
|
+
*/
|
|
90
|
+
subscribeToParent(listener) {
|
|
91
|
+
return subscribeToContext({ key: "parent", listener });
|
|
92
|
+
},
|
|
93
|
+
/**
|
|
94
|
+
* Get the containing page and its parent.
|
|
95
|
+
* Throws if called before SDK initialization completes.
|
|
96
|
+
*/
|
|
30
97
|
getPage() {
|
|
31
98
|
return getInitializedHostState("getPage").page;
|
|
32
99
|
},
|
|
100
|
+
/**
|
|
101
|
+
* Subscribes to the current `page` and future changes.
|
|
102
|
+
* Waits for initialization before delivering the initial value.
|
|
103
|
+
*/
|
|
104
|
+
subscribeToPage(listener) {
|
|
105
|
+
return subscribeToContext({ key: "page", listener });
|
|
106
|
+
},
|
|
107
|
+
/**
|
|
108
|
+
* Get the block's declared data source manifest.
|
|
109
|
+
* Throws if called before SDK initialization completes.
|
|
110
|
+
*/
|
|
33
111
|
getManifest() {
|
|
34
112
|
return getInitializedHostState("getManifest").manifest;
|
|
35
113
|
},
|
|
114
|
+
/**
|
|
115
|
+
* Subscribes to the current `manifest` and future changes.
|
|
116
|
+
* Waits for initialization before delivering the initial value.
|
|
117
|
+
*/
|
|
118
|
+
subscribeToManifest(listener) {
|
|
119
|
+
return subscribeToContext({ key: "manifest", listener });
|
|
120
|
+
},
|
|
36
121
|
autoResize,
|
|
37
122
|
/**
|
|
38
123
|
* Registers a listener that receives the current query snapshot and later updates.
|
|
@@ -74,3 +159,36 @@ function getInitializedHostState(methodName) {
|
|
|
74
159
|
}
|
|
75
160
|
return hostState;
|
|
76
161
|
}
|
|
162
|
+
/**
|
|
163
|
+
* Subscribe to one context value. Wait for initialization before delivering
|
|
164
|
+
* the initial value, then deliver updates when that value changes.
|
|
165
|
+
* Return a function that stops delivery, including before initialization.
|
|
166
|
+
*/
|
|
167
|
+
function subscribeToContext(args) {
|
|
168
|
+
const { key, listener } = args;
|
|
169
|
+
let active = true;
|
|
170
|
+
let delivered = false;
|
|
171
|
+
let previous;
|
|
172
|
+
const deliverIfChanged = () => {
|
|
173
|
+
const state = customBlockHost.getState();
|
|
174
|
+
if (!active || state.status !== "initialized") {
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
const next = state[key];
|
|
178
|
+
// Use referential equality to keep the implementation simple.
|
|
179
|
+
// Callbacks can receive different objects with the same contents.
|
|
180
|
+
// We have not benchmarked this choice. Deep equality may also be suitable.
|
|
181
|
+
if (delivered && Object.is(previous, next)) {
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
previous = next;
|
|
185
|
+
delivered = true;
|
|
186
|
+
notifyListener(() => listener(next));
|
|
187
|
+
};
|
|
188
|
+
const unsubscribe = customBlockHost.subscribe(deliverIfChanged);
|
|
189
|
+
deliverIfChanged();
|
|
190
|
+
return () => {
|
|
191
|
+
active = false;
|
|
192
|
+
unsubscribe();
|
|
193
|
+
};
|
|
194
|
+
}
|
|
@@ -35,7 +35,7 @@ export declare function useParent(): NotionParent;
|
|
|
35
35
|
*/
|
|
36
36
|
export declare function usePage(): CustomBlockPage;
|
|
37
37
|
/**
|
|
38
|
-
* Returns the host's current theme. Re-renders
|
|
38
|
+
* Returns the host's current theme. Re-renders when the theme changes.
|
|
39
39
|
*
|
|
40
40
|
* Throws if called before `initCustomBlock` has resolved.
|
|
41
41
|
*
|
|
@@ -44,8 +44,7 @@ export declare function usePage(): CustomBlockPage;
|
|
|
44
44
|
*/
|
|
45
45
|
export declare function useTheme(): NotionTheme;
|
|
46
46
|
/**
|
|
47
|
-
* Returns the host's contrast preference. Re-renders
|
|
48
|
-
* `contrastModeChanged` message from the host.
|
|
47
|
+
* Returns the host's contrast preference. Re-renders when the contrast preference changes.
|
|
49
48
|
*
|
|
50
49
|
* Throws if called before `initCustomBlock` has resolved.
|
|
51
50
|
*
|
|
@@ -54,8 +53,7 @@ export declare function useTheme(): NotionTheme;
|
|
|
54
53
|
*/
|
|
55
54
|
export declare function useContrastMode(): NotionContrastMode;
|
|
56
55
|
/**
|
|
57
|
-
* Returns the viewing user's Notion profile. Re-renders
|
|
58
|
-
* `currentUserChanged` message.
|
|
56
|
+
* Returns the viewing user's Notion profile. Re-renders when the profile data changes.
|
|
59
57
|
*
|
|
60
58
|
* Throws if called before `initCustomBlock` has resolved. `await` it before mounting.
|
|
61
59
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useRuntimeState.d.ts","sourceRoot":"","sources":["../../src/react/useRuntimeState.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,8CAA8C,CAAA;AACtF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yCAAyC,CAAA;AAC5E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,8CAA8C,CAAA;AACvF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gDAAgD,CAAA;AACrF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4CAA4C,CAAA;AAC9E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2CAA2C,CAAA;AAC5E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gDAAgD,CAAA;AAIhF;;;;;;;GAOG;AACH,wBAAgB,UAAU,IAAI,aAAa,
|
|
1
|
+
{"version":3,"file":"useRuntimeState.d.ts","sourceRoot":"","sources":["../../src/react/useRuntimeState.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,8CAA8C,CAAA;AACtF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yCAAyC,CAAA;AAC5E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,8CAA8C,CAAA;AACvF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gDAAgD,CAAA;AACrF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4CAA4C,CAAA;AAC9E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2CAA2C,CAAA;AAC5E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gDAAgD,CAAA;AAIhF;;;;;;;GAOG;AACH,wBAAgB,UAAU,IAAI,aAAa,CAK1C;AAED;;;;;;;;GAQG;AACH,wBAAgB,SAAS,IAAI,YAAY,CAKxC;AAED;;;;;;;;GAQG;AACH,wBAAgB,OAAO,IAAI,eAAe,CAEzC;AAED;;;;;;;GAOG;AACH,wBAAgB,QAAQ,IAAI,WAAW,CAKtC;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,IAAI,kBAAkB,CAKpD;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,IAAI,UAAU,CAK3C;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,WAAW,IAAI,mBAAmB,CAKjD"}
|
|
@@ -9,7 +9,7 @@ import { customBlock } from "../customBlock.js";
|
|
|
9
9
|
* const blockId = useBlockId()
|
|
10
10
|
*/
|
|
11
11
|
export function useBlockId() {
|
|
12
|
-
return useSyncExternalStore(customBlock.
|
|
12
|
+
return useSyncExternalStore(customBlock.subscribeToBlockId, customBlock.getBlockId);
|
|
13
13
|
}
|
|
14
14
|
/**
|
|
15
15
|
* Returns the block's parent in the document tree. Re-renders when the host sends
|
|
@@ -21,7 +21,7 @@ export function useBlockId() {
|
|
|
21
21
|
* const parent = useParent()
|
|
22
22
|
*/
|
|
23
23
|
export function useParent() {
|
|
24
|
-
return useSyncExternalStore(customBlock.
|
|
24
|
+
return useSyncExternalStore(customBlock.subscribeToParent, customBlock.getParent);
|
|
25
25
|
}
|
|
26
26
|
/**
|
|
27
27
|
* Returns the nearest enclosing page ancestor. Re-renders when the host sends
|
|
@@ -33,10 +33,10 @@ export function useParent() {
|
|
|
33
33
|
* const page = usePage()
|
|
34
34
|
*/
|
|
35
35
|
export function usePage() {
|
|
36
|
-
return useSyncExternalStore(customBlock.
|
|
36
|
+
return useSyncExternalStore(customBlock.subscribeToPage, customBlock.getPage);
|
|
37
37
|
}
|
|
38
38
|
/**
|
|
39
|
-
* Returns the host's current theme. Re-renders
|
|
39
|
+
* Returns the host's current theme. Re-renders when the theme changes.
|
|
40
40
|
*
|
|
41
41
|
* Throws if called before `initCustomBlock` has resolved.
|
|
42
42
|
*
|
|
@@ -44,11 +44,10 @@ export function usePage() {
|
|
|
44
44
|
* const theme = useTheme()
|
|
45
45
|
*/
|
|
46
46
|
export function useTheme() {
|
|
47
|
-
return useSyncExternalStore(customBlock.
|
|
47
|
+
return useSyncExternalStore(customBlock.subscribeToTheme, customBlock.getTheme);
|
|
48
48
|
}
|
|
49
49
|
/**
|
|
50
|
-
* Returns the host's contrast preference. Re-renders
|
|
51
|
-
* `contrastModeChanged` message from the host.
|
|
50
|
+
* Returns the host's contrast preference. Re-renders when the contrast preference changes.
|
|
52
51
|
*
|
|
53
52
|
* Throws if called before `initCustomBlock` has resolved.
|
|
54
53
|
*
|
|
@@ -56,11 +55,10 @@ export function useTheme() {
|
|
|
56
55
|
* const contrastMode = useContrastMode()
|
|
57
56
|
*/
|
|
58
57
|
export function useContrastMode() {
|
|
59
|
-
return useSyncExternalStore(customBlock.
|
|
58
|
+
return useSyncExternalStore(customBlock.subscribeToContrastMode, customBlock.getContrastMode);
|
|
60
59
|
}
|
|
61
60
|
/**
|
|
62
|
-
* Returns the viewing user's Notion profile. Re-renders
|
|
63
|
-
* `currentUserChanged` message.
|
|
61
|
+
* Returns the viewing user's Notion profile. Re-renders when the profile data changes.
|
|
64
62
|
*
|
|
65
63
|
* Throws if called before `initCustomBlock` has resolved. `await` it before mounting.
|
|
66
64
|
*
|
|
@@ -69,7 +67,7 @@ export function useContrastMode() {
|
|
|
69
67
|
* console.log(me.id, me.name, me.person.email)
|
|
70
68
|
*/
|
|
71
69
|
export function useCurrentUser() {
|
|
72
|
-
return useSyncExternalStore(customBlock.
|
|
70
|
+
return useSyncExternalStore(customBlock.subscribeToCurrentUser, customBlock.getCurrentUser);
|
|
73
71
|
}
|
|
74
72
|
/**
|
|
75
73
|
* Returns the manifest that the block declared and the host provided during initialization.
|
|
@@ -84,5 +82,5 @@ export function useCurrentUser() {
|
|
|
84
82
|
* resolved property IDs and schemas.
|
|
85
83
|
*/
|
|
86
84
|
export function useManifest() {
|
|
87
|
-
return useSyncExternalStore(customBlock.
|
|
85
|
+
return useSyncExternalStore(customBlock.subscribeToManifest, customBlock.getManifest);
|
|
88
86
|
}
|
package/dist/version.js
CHANGED
package/docs/block-location.md
CHANGED
|
@@ -1,38 +1,59 @@
|
|
|
1
|
-
# Block
|
|
1
|
+
# Block location and appearance
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Block and app context describes where the block runs and the host's appearance. Use getters for current values, subscriptions for initial values and later changes, or React hooks in components.
|
|
4
4
|
|
|
5
|
-
## API
|
|
5
|
+
## API overview
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Import `customBlock` from `@notionhq/custom-blocks` for getters and subscriptions. Import React hooks from `@notionhq/custom-blocks/react`.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
The getter and subscription names below are methods on `customBlock`.
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
| Context | Getter | Subscription | React hook |
|
|
12
|
+
| --- | --- | --- | --- |
|
|
13
|
+
| Current user | `getCurrentUser()` | `subscribeToCurrentUser(callback)` | `useCurrentUser()` |
|
|
14
|
+
| Custom block ID | `getBlockId()` | `subscribeToBlockId(callback)` | `useBlockId()` |
|
|
15
|
+
| Parent | `getParent()` | `subscribeToParent(callback)` | `useParent()` |
|
|
16
|
+
| Containing page | `getPage()` | `subscribeToPage(callback)` | `usePage()` |
|
|
17
|
+
| Theme | `getTheme()` | `subscribeToTheme(callback)` | `useTheme()` |
|
|
18
|
+
| Contrast mode | `getContrastMode()` | `subscribeToContrastMode(callback)` | `useContrastMode()` |
|
|
19
|
+
| Manifest | `getManifest()` | `subscribeToManifest(callback)` | `useManifest()` |
|
|
12
20
|
|
|
13
|
-
|
|
21
|
+
## Reading values and following changes
|
|
14
22
|
|
|
15
|
-
|
|
16
|
-
function useBlockId(): NotionBlockId;
|
|
17
|
-
```
|
|
23
|
+
Getters return the current value and throw before initialization completes. In React, call context hooks inside `NotionCustomBlock`, which waits for initialization before rendering its children.
|
|
18
24
|
|
|
19
|
-
|
|
25
|
+
Subscriptions can register before initialization. They wait for initialization before delivering the initial value, or deliver immediately if initialization is complete. Start initialization with `initCustomBlock()` or `NotionCustomBlock`. Subscriptions do not start it themselves. These initialization APIs also report failures. Each subscription returns an unsubscribe function. Repeated cleanup calls have no effect.
|
|
26
|
+
|
|
27
|
+
After initial delivery, subscriptions compare values with `Object.is`. Objects use referential equality: a new reference triggers a callback even if its contents are unchanged. Updates to other context values do not trigger the callback. React hooks follow the same comparison rules.
|
|
20
28
|
|
|
21
29
|
```ts
|
|
22
|
-
|
|
30
|
+
import { customBlock, initCustomBlock } from "@notionhq/custom-blocks"
|
|
31
|
+
|
|
32
|
+
const unsubscribe = customBlock.subscribeToTheme(theme => {
|
|
33
|
+
console.log(theme) // Initial theme, then later changes.
|
|
34
|
+
})
|
|
23
35
|
|
|
24
|
-
|
|
36
|
+
await initCustomBlock()
|
|
37
|
+
console.log(customBlock.getTheme()) // Read the current theme once.
|
|
38
|
+
|
|
39
|
+
// Call unsubscribe() when the view is removed.
|
|
25
40
|
```
|
|
26
41
|
|
|
27
|
-
|
|
42
|
+
## Current user
|
|
28
43
|
|
|
29
|
-
|
|
44
|
+
Use `getCurrentUser()`, `subscribeToCurrentUser()`, and `useCurrentUser()` to access the viewing user's `NotionUser`. See [users](./users.md#reading-the-current-user) for profile fields and user APIs.
|
|
30
45
|
|
|
31
|
-
|
|
46
|
+
## Custom block ID
|
|
32
47
|
|
|
33
|
-
|
|
34
|
-
|
|
48
|
+
Use `getBlockId()`, `subscribeToBlockId()`, and `useBlockId()` to access the custom block's own `NotionBlockId`.
|
|
49
|
+
|
|
50
|
+
The ID is stable for the lifetime of the block. Each subscription delivers once, unless cancelled before initialization. You can register before initialization without an error.
|
|
35
51
|
|
|
52
|
+
## Parent
|
|
53
|
+
|
|
54
|
+
Use `getParent()`, `subscribeToParent()`, and `useParent()` to access the block's `NotionParent` in the document tree. For a custom collection view, the parent identifies the data source associated with the view.
|
|
55
|
+
|
|
56
|
+
```ts
|
|
36
57
|
type NotionParent =
|
|
37
58
|
| { type: "page_id"; page_id: string } // inline custom block under a page
|
|
38
59
|
| { type: "block_id"; block_id: NotionBlockId } // inline custom block under a toggle/column/callout/...
|
|
@@ -42,8 +63,6 @@ type NotionParent =
|
|
|
42
63
|
| { type: "unavailable" }; // access denied; no parent ID or kind
|
|
43
64
|
```
|
|
44
65
|
|
|
45
|
-
For non-React renderers, use `customBlock.getParent()` after `initCustomBlock()` resolves.
|
|
46
|
-
|
|
47
66
|
To identify custom blocks in agent-owned instruction content:
|
|
48
67
|
|
|
49
68
|
```tsx
|
|
@@ -60,15 +79,11 @@ export function AgentInstructionBadge() {
|
|
|
60
79
|
}
|
|
61
80
|
```
|
|
62
81
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
Returns the nearest enclosing `page` / `collection_view_page` ancestor.
|
|
82
|
+
## Containing page
|
|
66
83
|
|
|
67
|
-
|
|
84
|
+
Use `getPage()`, `subscribeToPage()`, and `usePage()` to access the nearest enclosing page or collection view page. The page's parent can change even when its ID stays the same.
|
|
68
85
|
|
|
69
86
|
```ts
|
|
70
|
-
function usePage(): CustomBlockPage;
|
|
71
|
-
|
|
72
87
|
type CustomBlockPage = {
|
|
73
88
|
id: NotionPageId;
|
|
74
89
|
parent: NotionParent; // the containing page's own parent
|
|
@@ -77,15 +92,10 @@ type CustomBlockPage = {
|
|
|
77
92
|
|
|
78
93
|
`page.parent` allows determining if the custom block is in a freestanding page or part of a database. Don't confuse it with `useParent()`, which is the custom block's own parent.
|
|
79
94
|
|
|
80
|
-
|
|
81
|
-
const page = usePage();
|
|
82
|
-
const isInsideDatabaseRow = page.parent.type === "data_source_id";
|
|
83
|
-
```
|
|
95
|
+
A containing page whose parent has `type: "data_source_id"` belongs to a data source.
|
|
84
96
|
|
|
85
97
|
For anything beyond location — title, icon, properties — fetch the page through the pages API: `pages.get(page.id)`.
|
|
86
98
|
|
|
87
|
-
For non-React renderers, use `customBlock.getPage()` after `initCustomBlock()` resolves.
|
|
88
|
-
|
|
89
99
|
When the viewer cannot access the parent resource, the host returns
|
|
90
100
|
`{ type: "unavailable" }`, including on `usePage().parent`. This applies to every
|
|
91
101
|
parent kind, including data sources, even when the readable child identifies
|
|
@@ -97,25 +107,13 @@ Treat `unavailable` as unknown when branching on location: it does not establish
|
|
|
97
107
|
whether the containing page is a database row. A parent that is still loading
|
|
98
108
|
is not reported as access denied.
|
|
99
109
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
Returns the host's current theme.
|
|
110
|
+
## Theme and contrast
|
|
103
111
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
```ts
|
|
107
|
-
function useTheme(): NotionTheme; // "light" | "dark"
|
|
108
|
-
```
|
|
112
|
+
Use design tokens to style the block. When your code needs the current appearance, use a getter, subscription, or React hook from the [API overview](#api-overview).
|
|
109
113
|
|
|
110
|
-
|
|
114
|
+
`NotionTheme` is `"light"` or `"dark"`. `NotionContrastMode` is `"standard"` or `"high"`. Hosts that omit a contrast mode use `"standard"`.
|
|
111
115
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
```ts
|
|
115
|
-
await initCustomBlock();
|
|
116
|
-
|
|
117
|
-
const theme = customBlock.getTheme();
|
|
118
|
-
```
|
|
116
|
+
Getters read the current value after initialization. Subscriptions can register earlier. They deliver the initial value once initialized, then later changes. React hooks keep components current when called inside `NotionCustomBlock`.
|
|
119
117
|
|
|
120
118
|
```tsx
|
|
121
119
|
import { usePage, useTheme } from "@notionhq/custom-blocks/react";
|
|
@@ -127,14 +125,12 @@ export function Header() {
|
|
|
127
125
|
}
|
|
128
126
|
```
|
|
129
127
|
|
|
130
|
-
|
|
128
|
+
For styling, `NotionTokenScope` applies theme and contrast mode to the bundled Notion CSS variables automatically. The stylesheet uses `data-display-mode` for light and dark mode. The separate `data-theme` attribute selects content color palettes such as `blue`, `red`, and `gray`.
|
|
131
129
|
|
|
132
|
-
|
|
130
|
+
See [Reading values and following changes](#reading-values-and-following-changes) for a getter and subscription example, including cleanup.
|
|
133
131
|
|
|
134
|
-
|
|
135
|
-
function useContrastMode(): NotionContrastMode; // "standard" | "high"
|
|
136
|
-
```
|
|
132
|
+
## Manifest
|
|
137
133
|
|
|
138
|
-
|
|
134
|
+
Use `getManifest()`, `subscribeToManifest()`, and `useManifest()` to access the declared `CustomBlockManifest`. The manifest stays fixed for the lifetime of the running block, so each subscription delivers it once. You can register before initialization without an error.
|
|
139
135
|
|
|
140
|
-
|
|
136
|
+
See [data sources](./data-sources.md) for manifest details and `subscribeToDataSource()` for query snapshots.
|
package/docs/data-sources.md
CHANGED
|
@@ -60,8 +60,6 @@ const unsubscribe = customBlock.subscribeToDataSource({
|
|
|
60
60
|
},
|
|
61
61
|
});
|
|
62
62
|
|
|
63
|
-
// When the renderer unmounts:
|
|
64
|
-
unsubscribe();
|
|
65
63
|
```
|
|
66
64
|
|
|
67
65
|
To change the query or request more rows, unsubscribe and create a new subscription with different options.
|
|
@@ -235,7 +233,9 @@ Returns the block manifest received from the host during initialization. The man
|
|
|
235
233
|
function customBlock.getManifest(): CustomBlockManifest;
|
|
236
234
|
```
|
|
237
235
|
|
|
238
|
-
Framework-neutral getter for the same manifest returned by `useManifest()`.
|
|
236
|
+
Framework-neutral getter for the same manifest returned by `useManifest()`.
|
|
237
|
+
`customBlock.subscribeToManifest(callback)` follows the [context subscription contract](./block-location.md#reading-values-and-following-changes).
|
|
238
|
+
The manifest stays fixed for the lifetime of the running block, so each subscription delivers it once. You can register before initialization without an error.
|
|
239
239
|
|
|
240
240
|
## Example: querying a data source
|
|
241
241
|
|
package/docs/lifecycle.md
CHANGED
|
@@ -143,20 +143,61 @@ All code types are open string unions, so keep a default branch. Use `isRetryabl
|
|
|
143
143
|
|
|
144
144
|
### `customBlock`
|
|
145
145
|
|
|
146
|
-
|
|
146
|
+
The SDK provides getters, subscriptions, and React hooks for block and app context. Use `customBlock` for getters and subscriptions.
|
|
147
147
|
|
|
148
|
-
|
|
148
|
+
#### Getter
|
|
149
|
+
|
|
150
|
+
Getters return the current value, such as the theme, current user, or containing page. They throw if called before initialization completes.
|
|
149
151
|
|
|
150
152
|
```ts
|
|
151
|
-
|
|
153
|
+
import { customBlock, initCustomBlock } from "@notionhq/custom-blocks"
|
|
152
154
|
|
|
153
|
-
|
|
154
|
-
const
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
155
|
+
await initCustomBlock()
|
|
156
|
+
const theme = customBlock.getTheme()
|
|
157
|
+
console.log(theme)
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
#### Subscription
|
|
161
|
+
|
|
162
|
+
Subscriptions deliver the initial value and later changes. You can register them before initialization. They wait for initialization before delivering the initial value, or deliver immediately if initialization is complete.
|
|
163
|
+
|
|
164
|
+
```ts
|
|
165
|
+
import { customBlock, initCustomBlock } from "@notionhq/custom-blocks"
|
|
166
|
+
|
|
167
|
+
const unsubscribe = customBlock.subscribeToTheme(theme => {
|
|
168
|
+
console.log(theme)
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
await initCustomBlock()
|
|
172
|
+
|
|
173
|
+
// Call unsubscribe() when the view is removed to stop future callbacks.
|
|
158
174
|
```
|
|
159
175
|
|
|
176
|
+
#### React hook
|
|
177
|
+
|
|
178
|
+
In React, use context hooks inside `NotionCustomBlock`. The wrapper waits for initialization, and the hooks keep components current as values change. React handles subscription cleanup when the component unmounts.
|
|
179
|
+
|
|
180
|
+
```tsx
|
|
181
|
+
import { NotionCustomBlock, useTheme } from "@notionhq/custom-blocks/react"
|
|
182
|
+
|
|
183
|
+
export function App() {
|
|
184
|
+
return (
|
|
185
|
+
<NotionCustomBlock>
|
|
186
|
+
<ThemeLabel />
|
|
187
|
+
</NotionCustomBlock>
|
|
188
|
+
)
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function ThemeLabel() {
|
|
192
|
+
const theme = useTheme()
|
|
193
|
+
return <p>Current theme: {theme}</p>
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
See [Block location and appearance](./block-location.md#reading-values-and-following-changes) for the available getters, subscriptions, and React hooks.
|
|
198
|
+
|
|
199
|
+
### `customBlock.autoResize()`
|
|
200
|
+
|
|
160
201
|
For non-React auto-resize, pass the element whose content height should drive the host iframe. The helper posts one initial measurement, observes later size changes when `ResizeObserver` is available, dedupes unchanged heights, and returns a cleanup function:
|
|
161
202
|
|
|
162
203
|
```ts
|
|
@@ -164,8 +205,7 @@ const stopAutoResize = customBlock.autoResize({
|
|
|
164
205
|
target: document.getElementById("root"),
|
|
165
206
|
});
|
|
166
207
|
|
|
167
|
-
//
|
|
168
|
-
stopAutoResize();
|
|
208
|
+
// Call stopAutoResize() when your renderer is removed.
|
|
169
209
|
```
|
|
170
210
|
|
|
171
211
|
### `NotInIframeError`
|
package/docs/users.md
CHANGED
|
@@ -29,14 +29,15 @@ For non-React renderers, read the same state through `customBlock` after initial
|
|
|
29
29
|
|
|
30
30
|
```ts
|
|
31
31
|
await initCustomBlock()
|
|
32
|
-
const me = customBlock.getCurrentUser()
|
|
33
32
|
|
|
34
|
-
const unsubscribe = customBlock.
|
|
35
|
-
const nextMe = customBlock.getCurrentUser()
|
|
33
|
+
const unsubscribe = customBlock.subscribeToCurrentUser(nextMe => {
|
|
36
34
|
// Update your renderer with nextMe.
|
|
37
35
|
})
|
|
38
36
|
```
|
|
39
37
|
|
|
38
|
+
Call `unsubscribe()` when your renderer is removed. The callback receives the initial profile and later changes.
|
|
39
|
+
See [context subscriptions](./block-location.md#reading-values-and-following-changes) for initialization and equality rules.
|
|
40
|
+
|
|
40
41
|
## Listing users
|
|
41
42
|
|
|
42
43
|
`users.list(input?)` returns workspace users visible to the current custom block, mirroring Notion's [`GET /v1/users`](https://developers.notion.com/reference/get-users) shape.
|
package/package.json
CHANGED
package/src/customBlock.ts
CHANGED
|
@@ -12,8 +12,10 @@ import type {
|
|
|
12
12
|
CustomBlockHostState,
|
|
13
13
|
InitializedHostState,
|
|
14
14
|
} from "./bridge/hostState.js"
|
|
15
|
+
import { notifyListener } from "./bridge/notifyListener.js"
|
|
15
16
|
import { customBlockHost } from "./bridge/sandboxClient.js"
|
|
16
17
|
|
|
18
|
+
/** @deprecated Use individual context types and getters after `initCustomBlock` resolves. */
|
|
17
19
|
export type CustomBlockState =
|
|
18
20
|
| {
|
|
19
21
|
status: "uninitialized"
|
|
@@ -31,47 +33,144 @@ export type CustomBlockState =
|
|
|
31
33
|
dataSources: NotionDataSource[]
|
|
32
34
|
}
|
|
33
35
|
|
|
36
|
+
type Unsubscribe = () => void
|
|
37
|
+
|
|
34
38
|
/**
|
|
35
|
-
*
|
|
36
|
-
*
|
|
39
|
+
* Read block and app context with getters, or follow changes with subscriptions.
|
|
40
|
+
* Getters throw if called before `initCustomBlock()` completes. Subscriptions
|
|
41
|
+
* can be registered before initialization and deliver the initial value once
|
|
42
|
+
* initialization completes, followed by changes to that value.
|
|
43
|
+
*
|
|
44
|
+
* Each subscription returns an unsubscribe function. Call it when the
|
|
45
|
+
* subscription is no longer needed.
|
|
37
46
|
*/
|
|
38
47
|
export const customBlock = {
|
|
39
|
-
|
|
48
|
+
/** @deprecated Use an individual subscription method, such as `subscribeToTheme()` or `subscribeToCurrentUser()`. */
|
|
49
|
+
subscribe(listener: () => void): Unsubscribe {
|
|
40
50
|
return customBlockHost.subscribe(listener)
|
|
41
51
|
},
|
|
42
52
|
|
|
53
|
+
/** @deprecated Use an individual getter method, such as `getTheme()` or `getCurrentUser()`, after `initCustomBlock()` resolves. */
|
|
43
54
|
getState(): CustomBlockState {
|
|
44
55
|
return toPublicState(customBlockHost.getState())
|
|
45
56
|
},
|
|
46
57
|
|
|
58
|
+
/**
|
|
59
|
+
* Get the current user viewing the block.
|
|
60
|
+
* Throws if called before SDK initialization completes.
|
|
61
|
+
*/
|
|
47
62
|
getCurrentUser(): NotionUser {
|
|
48
63
|
return getInitializedHostState("getCurrentUser").currentUser
|
|
49
64
|
},
|
|
50
65
|
|
|
66
|
+
/**
|
|
67
|
+
* Subscribes to the current `currentUser` and future changes.
|
|
68
|
+
* Waits for initialization before delivering the initial value.
|
|
69
|
+
*/
|
|
70
|
+
subscribeToCurrentUser(listener: (value: NotionUser) => void): Unsubscribe {
|
|
71
|
+
return subscribeToContext({ key: "currentUser", listener })
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Get the host's current theme.
|
|
76
|
+
* Throws if called before SDK initialization completes.
|
|
77
|
+
*/
|
|
51
78
|
getTheme(): NotionTheme {
|
|
52
79
|
return getInitializedHostState("getTheme").theme
|
|
53
80
|
},
|
|
54
81
|
|
|
82
|
+
/**
|
|
83
|
+
* Subscribes to the current `theme` and future changes.
|
|
84
|
+
* Waits for initialization before delivering the initial value.
|
|
85
|
+
*/
|
|
86
|
+
subscribeToTheme(listener: (value: NotionTheme) => void): Unsubscribe {
|
|
87
|
+
return subscribeToContext({ key: "theme", listener })
|
|
88
|
+
},
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Get the host's current contrast mode.
|
|
92
|
+
* Throws if called before SDK initialization completes.
|
|
93
|
+
*/
|
|
55
94
|
getContrastMode(): NotionContrastMode {
|
|
56
95
|
return getInitializedHostState("getContrastMode").contrastMode
|
|
57
96
|
},
|
|
58
97
|
|
|
98
|
+
/**
|
|
99
|
+
* Subscribes to the current `contrastMode` and future changes.
|
|
100
|
+
* Waits for initialization before delivering the initial value.
|
|
101
|
+
*/
|
|
102
|
+
subscribeToContrastMode(
|
|
103
|
+
listener: (value: NotionContrastMode) => void,
|
|
104
|
+
): Unsubscribe {
|
|
105
|
+
return subscribeToContext({ key: "contrastMode", listener })
|
|
106
|
+
},
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Get the custom block's ID. The ID is stable for the lifetime of the block.
|
|
110
|
+
* Throws if called before SDK initialization completes.
|
|
111
|
+
*/
|
|
59
112
|
getBlockId(): NotionBlockId {
|
|
60
113
|
return getInitializedHostState("getBlockId").blockId
|
|
61
114
|
},
|
|
62
115
|
|
|
116
|
+
/**
|
|
117
|
+
* Delivers the block ID once. The ID is stable for the lifetime of the block.
|
|
118
|
+
* Waits for initialization before delivering the initial value.
|
|
119
|
+
*/
|
|
120
|
+
subscribeToBlockId(listener: (value: NotionBlockId) => void): Unsubscribe {
|
|
121
|
+
return subscribeToContext({ key: "blockId", listener })
|
|
122
|
+
},
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Get the custom block's parent.
|
|
126
|
+
* Throws if called before SDK initialization completes.
|
|
127
|
+
*/
|
|
63
128
|
getParent(): NotionParent {
|
|
64
129
|
return getInitializedHostState("getParent").parent
|
|
65
130
|
},
|
|
66
131
|
|
|
132
|
+
/**
|
|
133
|
+
* Subscribes to the current `parent` and future changes.
|
|
134
|
+
* Waits for initialization before delivering the initial value.
|
|
135
|
+
*/
|
|
136
|
+
subscribeToParent(listener: (value: NotionParent) => void): Unsubscribe {
|
|
137
|
+
return subscribeToContext({ key: "parent", listener })
|
|
138
|
+
},
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Get the containing page and its parent.
|
|
142
|
+
* Throws if called before SDK initialization completes.
|
|
143
|
+
*/
|
|
67
144
|
getPage(): CustomBlockPage {
|
|
68
145
|
return getInitializedHostState("getPage").page
|
|
69
146
|
},
|
|
70
147
|
|
|
148
|
+
/**
|
|
149
|
+
* Subscribes to the current `page` and future changes.
|
|
150
|
+
* Waits for initialization before delivering the initial value.
|
|
151
|
+
*/
|
|
152
|
+
subscribeToPage(listener: (value: CustomBlockPage) => void): Unsubscribe {
|
|
153
|
+
return subscribeToContext({ key: "page", listener })
|
|
154
|
+
},
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Get the block's declared data source manifest.
|
|
158
|
+
* Throws if called before SDK initialization completes.
|
|
159
|
+
*/
|
|
71
160
|
getManifest(): CustomBlockManifest {
|
|
72
161
|
return getInitializedHostState("getManifest").manifest
|
|
73
162
|
},
|
|
74
163
|
|
|
164
|
+
/**
|
|
165
|
+
* Subscribes to the current `manifest` and future changes.
|
|
166
|
+
* Waits for initialization before delivering the initial value.
|
|
167
|
+
*/
|
|
168
|
+
subscribeToManifest(
|
|
169
|
+
listener: (value: CustomBlockManifest) => void,
|
|
170
|
+
): Unsubscribe {
|
|
171
|
+
return subscribeToContext({ key: "manifest", listener })
|
|
172
|
+
},
|
|
173
|
+
|
|
75
174
|
autoResize,
|
|
76
175
|
|
|
77
176
|
/**
|
|
@@ -119,3 +218,40 @@ function getInitializedHostState(methodName: string): InitializedHostState {
|
|
|
119
218
|
}
|
|
120
219
|
return hostState
|
|
121
220
|
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Subscribe to one context value. Wait for initialization before delivering
|
|
224
|
+
* the initial value, then deliver updates when that value changes.
|
|
225
|
+
* Return a function that stops delivery, including before initialization.
|
|
226
|
+
*/
|
|
227
|
+
function subscribeToContext<K extends keyof InitializedHostState>(args: {
|
|
228
|
+
key: K
|
|
229
|
+
listener: (value: InitializedHostState[K]) => void
|
|
230
|
+
}): Unsubscribe {
|
|
231
|
+
const { key, listener } = args
|
|
232
|
+
let active = true
|
|
233
|
+
let delivered = false
|
|
234
|
+
let previous: InitializedHostState[K] | undefined
|
|
235
|
+
const deliverIfChanged = () => {
|
|
236
|
+
const state = customBlockHost.getState()
|
|
237
|
+
if (!active || state.status !== "initialized") {
|
|
238
|
+
return
|
|
239
|
+
}
|
|
240
|
+
const next = state[key]
|
|
241
|
+
// Use referential equality to keep the implementation simple.
|
|
242
|
+
// Callbacks can receive different objects with the same contents.
|
|
243
|
+
// We have not benchmarked this choice. Deep equality may also be suitable.
|
|
244
|
+
if (delivered && Object.is(previous, next)) {
|
|
245
|
+
return
|
|
246
|
+
}
|
|
247
|
+
previous = next
|
|
248
|
+
delivered = true
|
|
249
|
+
notifyListener(() => listener(next))
|
|
250
|
+
}
|
|
251
|
+
const unsubscribe = customBlockHost.subscribe(deliverIfChanged)
|
|
252
|
+
deliverIfChanged()
|
|
253
|
+
return () => {
|
|
254
|
+
active = false
|
|
255
|
+
unsubscribe()
|
|
256
|
+
}
|
|
257
|
+
}
|
|
@@ -17,7 +17,10 @@ import { customBlock } from "../customBlock.js"
|
|
|
17
17
|
* const blockId = useBlockId()
|
|
18
18
|
*/
|
|
19
19
|
export function useBlockId(): NotionBlockId {
|
|
20
|
-
return useSyncExternalStore(
|
|
20
|
+
return useSyncExternalStore(
|
|
21
|
+
customBlock.subscribeToBlockId,
|
|
22
|
+
customBlock.getBlockId,
|
|
23
|
+
)
|
|
21
24
|
}
|
|
22
25
|
|
|
23
26
|
/**
|
|
@@ -30,7 +33,10 @@ export function useBlockId(): NotionBlockId {
|
|
|
30
33
|
* const parent = useParent()
|
|
31
34
|
*/
|
|
32
35
|
export function useParent(): NotionParent {
|
|
33
|
-
return useSyncExternalStore(
|
|
36
|
+
return useSyncExternalStore(
|
|
37
|
+
customBlock.subscribeToParent,
|
|
38
|
+
customBlock.getParent,
|
|
39
|
+
)
|
|
34
40
|
}
|
|
35
41
|
|
|
36
42
|
/**
|
|
@@ -43,11 +49,11 @@ export function useParent(): NotionParent {
|
|
|
43
49
|
* const page = usePage()
|
|
44
50
|
*/
|
|
45
51
|
export function usePage(): CustomBlockPage {
|
|
46
|
-
return useSyncExternalStore(customBlock.
|
|
52
|
+
return useSyncExternalStore(customBlock.subscribeToPage, customBlock.getPage)
|
|
47
53
|
}
|
|
48
54
|
|
|
49
55
|
/**
|
|
50
|
-
* Returns the host's current theme. Re-renders
|
|
56
|
+
* Returns the host's current theme. Re-renders when the theme changes.
|
|
51
57
|
*
|
|
52
58
|
* Throws if called before `initCustomBlock` has resolved.
|
|
53
59
|
*
|
|
@@ -55,12 +61,14 @@ export function usePage(): CustomBlockPage {
|
|
|
55
61
|
* const theme = useTheme()
|
|
56
62
|
*/
|
|
57
63
|
export function useTheme(): NotionTheme {
|
|
58
|
-
return useSyncExternalStore(
|
|
64
|
+
return useSyncExternalStore(
|
|
65
|
+
customBlock.subscribeToTheme,
|
|
66
|
+
customBlock.getTheme,
|
|
67
|
+
)
|
|
59
68
|
}
|
|
60
69
|
|
|
61
70
|
/**
|
|
62
|
-
* Returns the host's contrast preference. Re-renders
|
|
63
|
-
* `contrastModeChanged` message from the host.
|
|
71
|
+
* Returns the host's contrast preference. Re-renders when the contrast preference changes.
|
|
64
72
|
*
|
|
65
73
|
* Throws if called before `initCustomBlock` has resolved.
|
|
66
74
|
*
|
|
@@ -69,14 +77,13 @@ export function useTheme(): NotionTheme {
|
|
|
69
77
|
*/
|
|
70
78
|
export function useContrastMode(): NotionContrastMode {
|
|
71
79
|
return useSyncExternalStore(
|
|
72
|
-
customBlock.
|
|
80
|
+
customBlock.subscribeToContrastMode,
|
|
73
81
|
customBlock.getContrastMode,
|
|
74
82
|
)
|
|
75
83
|
}
|
|
76
84
|
|
|
77
85
|
/**
|
|
78
|
-
* Returns the viewing user's Notion profile. Re-renders
|
|
79
|
-
* `currentUserChanged` message.
|
|
86
|
+
* Returns the viewing user's Notion profile. Re-renders when the profile data changes.
|
|
80
87
|
*
|
|
81
88
|
* Throws if called before `initCustomBlock` has resolved. `await` it before mounting.
|
|
82
89
|
*
|
|
@@ -85,7 +92,10 @@ export function useContrastMode(): NotionContrastMode {
|
|
|
85
92
|
* console.log(me.id, me.name, me.person.email)
|
|
86
93
|
*/
|
|
87
94
|
export function useCurrentUser(): NotionUser {
|
|
88
|
-
return useSyncExternalStore(
|
|
95
|
+
return useSyncExternalStore(
|
|
96
|
+
customBlock.subscribeToCurrentUser,
|
|
97
|
+
customBlock.getCurrentUser,
|
|
98
|
+
)
|
|
89
99
|
}
|
|
90
100
|
|
|
91
101
|
/**
|
|
@@ -101,5 +111,8 @@ export function useCurrentUser(): NotionUser {
|
|
|
101
111
|
* resolved property IDs and schemas.
|
|
102
112
|
*/
|
|
103
113
|
export function useManifest(): CustomBlockManifest {
|
|
104
|
-
return useSyncExternalStore(
|
|
114
|
+
return useSyncExternalStore(
|
|
115
|
+
customBlock.subscribeToManifest,
|
|
116
|
+
customBlock.getManifest,
|
|
117
|
+
)
|
|
105
118
|
}
|