feedtack 1.1.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +49 -40
- package/dist/{types-Cu4Oahg4.d.ts → adapter-Cn59URIG.d.ts} +68 -59
- package/dist/{chunk-3INDOI4N.js → chunk-GD2SY64K.js} +74 -1
- package/dist/feedtack.inject.js +199 -0
- package/dist/index.d.ts +30 -21
- package/dist/index.js +46 -83
- package/dist/node/index.d.ts +27 -0
- package/dist/node/index.js +169 -0
- package/dist/react/index.d.ts +61 -2
- package/dist/react/index.js +830 -232
- package/dist/types-CHrWe7xT.d.ts +61 -0
- package/package.json +5 -1
package/dist/index.js
CHANGED
|
@@ -8,8 +8,14 @@ import {
|
|
|
8
8
|
getPinCoords,
|
|
9
9
|
getTargetMeta,
|
|
10
10
|
getViewportMeta,
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
hashField,
|
|
12
|
+
isContentAdapter,
|
|
13
|
+
isContentEditAdapter,
|
|
14
|
+
scanFields,
|
|
15
|
+
themeToCSS,
|
|
16
|
+
warnIfNotContentAdapter,
|
|
17
|
+
warnIfNotContentEditAdapter
|
|
18
|
+
} from "./chunk-GD2SY64K.js";
|
|
13
19
|
|
|
14
20
|
// src/adapters/ConsoleAdapter.ts
|
|
15
21
|
var ConsoleAdapter = class {
|
|
@@ -31,85 +37,6 @@ var ConsoleAdapter = class {
|
|
|
31
37
|
}
|
|
32
38
|
};
|
|
33
39
|
|
|
34
|
-
// src/adapters/DiskAdapter.ts
|
|
35
|
-
import { readdir, readFile, writeFile, mkdir } from "fs/promises";
|
|
36
|
-
import { join } from "path";
|
|
37
|
-
var DiskAdapter = class {
|
|
38
|
-
constructor(config = {}) {
|
|
39
|
-
this.dir = config.directory ?? ".feedback";
|
|
40
|
-
}
|
|
41
|
-
async submit(payload) {
|
|
42
|
-
await mkdir(this.dir, { recursive: true });
|
|
43
|
-
const item = {
|
|
44
|
-
payload,
|
|
45
|
-
replies: [],
|
|
46
|
-
resolutions: [],
|
|
47
|
-
archives: []
|
|
48
|
-
};
|
|
49
|
-
await this.write(payload.id, item);
|
|
50
|
-
}
|
|
51
|
-
async reply(feedbackId, reply) {
|
|
52
|
-
const item = await this.read(feedbackId);
|
|
53
|
-
item.replies.push({
|
|
54
|
-
id: `r_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 7)}`,
|
|
55
|
-
feedbackId,
|
|
56
|
-
...reply
|
|
57
|
-
});
|
|
58
|
-
await this.write(feedbackId, item);
|
|
59
|
-
}
|
|
60
|
-
async resolve(feedbackId, resolution) {
|
|
61
|
-
const item = await this.read(feedbackId);
|
|
62
|
-
item.resolutions.push({ feedbackId, ...resolution });
|
|
63
|
-
await this.write(feedbackId, item);
|
|
64
|
-
}
|
|
65
|
-
async archive(feedbackId, userId) {
|
|
66
|
-
const item = await this.read(feedbackId);
|
|
67
|
-
item.archives.push({
|
|
68
|
-
feedbackId,
|
|
69
|
-
archivedBy: { id: userId, name: "", role: "" },
|
|
70
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
71
|
-
});
|
|
72
|
-
await this.write(feedbackId, item);
|
|
73
|
-
}
|
|
74
|
-
async loadFeedback(filter) {
|
|
75
|
-
let files;
|
|
76
|
-
try {
|
|
77
|
-
await mkdir(this.dir, { recursive: true });
|
|
78
|
-
files = (await readdir(this.dir)).filter((f) => f.endsWith(".json"));
|
|
79
|
-
} catch {
|
|
80
|
-
return [];
|
|
81
|
-
}
|
|
82
|
-
const items = await Promise.all(
|
|
83
|
-
files.map(
|
|
84
|
-
async (f) => JSON.parse(
|
|
85
|
-
await readFile(join(this.dir, f), "utf-8")
|
|
86
|
-
)
|
|
87
|
-
)
|
|
88
|
-
);
|
|
89
|
-
if (!filter) return items;
|
|
90
|
-
return items.filter((item) => {
|
|
91
|
-
if (filter.scope && item.payload.scope !== filter.scope) return false;
|
|
92
|
-
if (filter.pathname && item.payload.page.pathname !== filter.pathname)
|
|
93
|
-
return false;
|
|
94
|
-
if (filter.url && item.payload.page.url !== filter.url) return false;
|
|
95
|
-
if (filter.userId && item.payload.submittedBy.id !== filter.userId)
|
|
96
|
-
return false;
|
|
97
|
-
return true;
|
|
98
|
-
});
|
|
99
|
-
}
|
|
100
|
-
async read(id) {
|
|
101
|
-
return JSON.parse(
|
|
102
|
-
await readFile(join(this.dir, `${id}.json`), "utf-8")
|
|
103
|
-
);
|
|
104
|
-
}
|
|
105
|
-
async write(id, item) {
|
|
106
|
-
await writeFile(
|
|
107
|
-
join(this.dir, `${id}.json`),
|
|
108
|
-
JSON.stringify(item, null, 2)
|
|
109
|
-
);
|
|
110
|
-
}
|
|
111
|
-
};
|
|
112
|
-
|
|
113
40
|
// src/adapters/LocalStorageAdapter.ts
|
|
114
41
|
var LocalStorageAdapter = class {
|
|
115
42
|
constructor(config = {}) {
|
|
@@ -213,10 +140,40 @@ var WebhookAdapter = class {
|
|
|
213
140
|
async loadFeedback(filter) {
|
|
214
141
|
return this.config.loadFeedback(filter);
|
|
215
142
|
}
|
|
143
|
+
// ContentAdapter implementation
|
|
144
|
+
async approve(fieldPath, approval) {
|
|
145
|
+
const url = this.config.updateUrl ?? this.config.submitUrl;
|
|
146
|
+
await this.post(url, { type: "approve", fieldPath, ...approval });
|
|
147
|
+
}
|
|
148
|
+
async revokeApproval(fieldPath, userId) {
|
|
149
|
+
const url = this.config.updateUrl ?? this.config.submitUrl;
|
|
150
|
+
await this.post(url, { type: "revoke", fieldPath, userId });
|
|
151
|
+
}
|
|
152
|
+
async loadApprovals(filter) {
|
|
153
|
+
if (this.config.loadApprovals) {
|
|
154
|
+
return this.config.loadApprovals(filter);
|
|
155
|
+
}
|
|
156
|
+
return [];
|
|
157
|
+
}
|
|
158
|
+
// ContentEditAdapter implementation
|
|
159
|
+
async loadFields() {
|
|
160
|
+
if (this.config.loadFields) {
|
|
161
|
+
return this.config.loadFields();
|
|
162
|
+
}
|
|
163
|
+
return {};
|
|
164
|
+
}
|
|
165
|
+
async saveField(fieldPath, value) {
|
|
166
|
+
const url = this.config.updateUrl ?? this.config.submitUrl;
|
|
167
|
+
await this.post(url, {
|
|
168
|
+
type: "save-field",
|
|
169
|
+
fieldPath,
|
|
170
|
+
value,
|
|
171
|
+
clearApproval: true
|
|
172
|
+
});
|
|
173
|
+
}
|
|
216
174
|
};
|
|
217
175
|
export {
|
|
218
176
|
ConsoleAdapter,
|
|
219
|
-
DiskAdapter,
|
|
220
177
|
FeedtackEngine,
|
|
221
178
|
LocalStorageAdapter,
|
|
222
179
|
SCHEMA_VERSION,
|
|
@@ -228,5 +185,11 @@ export {
|
|
|
228
185
|
getPinCoords,
|
|
229
186
|
getTargetMeta,
|
|
230
187
|
getViewportMeta,
|
|
231
|
-
|
|
188
|
+
hashField,
|
|
189
|
+
isContentAdapter,
|
|
190
|
+
isContentEditAdapter,
|
|
191
|
+
scanFields,
|
|
192
|
+
themeToCSS,
|
|
193
|
+
warnIfNotContentAdapter,
|
|
194
|
+
warnIfNotContentEditAdapter
|
|
232
195
|
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { F as FeedtackAdapter, C as ContentAdapter, f as ContentEditAdapter, a as FeedtackPayload, b as FeedtackReply, c as FeedtackResolution, d as FeedtackFilter, e as FeedbackItem, i as FieldApproval, g as FieldFilter, h as FieldApprovalState } from '../adapter-Cn59URIG.js';
|
|
2
|
+
|
|
3
|
+
interface DiskAdapterConfig {
|
|
4
|
+
/** Directory to store JSON files in. Default: '.feedback' */
|
|
5
|
+
directory?: string;
|
|
6
|
+
}
|
|
7
|
+
/** Node.js adapter — persists each feedback item as a JSON file on disk */
|
|
8
|
+
declare class DiskAdapter implements FeedtackAdapter, ContentAdapter, ContentEditAdapter {
|
|
9
|
+
private dir;
|
|
10
|
+
private approvalsDir;
|
|
11
|
+
private fieldsDir;
|
|
12
|
+
constructor(config?: DiskAdapterConfig);
|
|
13
|
+
submit(payload: FeedtackPayload): Promise<void>;
|
|
14
|
+
reply(feedbackId: string, reply: Omit<FeedtackReply, 'id' | 'feedbackId'>): Promise<void>;
|
|
15
|
+
resolve(feedbackId: string, resolution: Omit<FeedtackResolution, 'feedbackId'>): Promise<void>;
|
|
16
|
+
archive(feedbackId: string, userId: string): Promise<void>;
|
|
17
|
+
loadFeedback(filter?: FeedtackFilter): Promise<FeedbackItem[]>;
|
|
18
|
+
approve(fieldPath: string, approval: FieldApproval): Promise<void>;
|
|
19
|
+
revokeApproval(fieldPath: string, userId: string): Promise<void>;
|
|
20
|
+
loadApprovals(filter?: FieldFilter): Promise<FieldApprovalState[]>;
|
|
21
|
+
loadFields(): Promise<Record<string, string>>;
|
|
22
|
+
saveField(fieldPath: string, value: string): Promise<void>;
|
|
23
|
+
private read;
|
|
24
|
+
private write;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export { DiskAdapter, type DiskAdapterConfig };
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
// src/adapters/DiskAdapter.ts
|
|
2
|
+
import { mkdir, readdir, readFile, writeFile } from "fs/promises";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
var DiskAdapter = class {
|
|
5
|
+
constructor(config = {}) {
|
|
6
|
+
this.dir = config.directory ?? ".feedback";
|
|
7
|
+
this.approvalsDir = join(this.dir, "approvals");
|
|
8
|
+
this.fieldsDir = join(this.dir, "fields");
|
|
9
|
+
}
|
|
10
|
+
async submit(payload) {
|
|
11
|
+
await mkdir(this.dir, { recursive: true });
|
|
12
|
+
const item = {
|
|
13
|
+
payload,
|
|
14
|
+
replies: [],
|
|
15
|
+
resolutions: [],
|
|
16
|
+
archives: []
|
|
17
|
+
};
|
|
18
|
+
await this.write(payload.id, item);
|
|
19
|
+
}
|
|
20
|
+
async reply(feedbackId, reply) {
|
|
21
|
+
const item = await this.read(feedbackId);
|
|
22
|
+
item.replies.push({
|
|
23
|
+
id: `r_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 7)}`,
|
|
24
|
+
feedbackId,
|
|
25
|
+
...reply
|
|
26
|
+
});
|
|
27
|
+
await this.write(feedbackId, item);
|
|
28
|
+
}
|
|
29
|
+
async resolve(feedbackId, resolution) {
|
|
30
|
+
const item = await this.read(feedbackId);
|
|
31
|
+
item.resolutions.push({ feedbackId, ...resolution });
|
|
32
|
+
await this.write(feedbackId, item);
|
|
33
|
+
}
|
|
34
|
+
async archive(feedbackId, userId) {
|
|
35
|
+
const item = await this.read(feedbackId);
|
|
36
|
+
item.archives.push({
|
|
37
|
+
feedbackId,
|
|
38
|
+
archivedBy: { id: userId, name: "", role: "" },
|
|
39
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
40
|
+
});
|
|
41
|
+
await this.write(feedbackId, item);
|
|
42
|
+
}
|
|
43
|
+
async loadFeedback(filter) {
|
|
44
|
+
let files;
|
|
45
|
+
try {
|
|
46
|
+
await mkdir(this.dir, { recursive: true });
|
|
47
|
+
files = (await readdir(this.dir)).filter((f) => f.endsWith(".json"));
|
|
48
|
+
} catch {
|
|
49
|
+
return [];
|
|
50
|
+
}
|
|
51
|
+
const items = await Promise.all(
|
|
52
|
+
files.map(
|
|
53
|
+
async (f) => JSON.parse(
|
|
54
|
+
await readFile(join(this.dir, f), "utf-8")
|
|
55
|
+
)
|
|
56
|
+
)
|
|
57
|
+
);
|
|
58
|
+
if (!filter) return items;
|
|
59
|
+
return items.filter((item) => {
|
|
60
|
+
if (filter.scope && item.payload.scope !== filter.scope) return false;
|
|
61
|
+
if (filter.pathname && item.payload.page.pathname !== filter.pathname)
|
|
62
|
+
return false;
|
|
63
|
+
if (filter.url && item.payload.page.url !== filter.url) return false;
|
|
64
|
+
if (filter.userId && item.payload.submittedBy.id !== filter.userId)
|
|
65
|
+
return false;
|
|
66
|
+
return true;
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
// ContentAdapter implementation
|
|
70
|
+
async approve(fieldPath, approval) {
|
|
71
|
+
await mkdir(this.approvalsDir, { recursive: true });
|
|
72
|
+
const safeName = fieldPath.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
73
|
+
await writeFile(
|
|
74
|
+
join(this.approvalsDir, `${safeName}.json`),
|
|
75
|
+
JSON.stringify(approval, null, 2)
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
async revokeApproval(fieldPath, userId) {
|
|
79
|
+
await mkdir(this.approvalsDir, { recursive: true });
|
|
80
|
+
const safeName = fieldPath.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
81
|
+
const filePath = join(this.approvalsDir, `${safeName}.json`);
|
|
82
|
+
let approval;
|
|
83
|
+
try {
|
|
84
|
+
approval = JSON.parse(await readFile(filePath, "utf-8"));
|
|
85
|
+
} catch {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
approval.by = approval.by.filter((id) => id !== userId);
|
|
89
|
+
if (approval.by.length === 0) {
|
|
90
|
+
const { unlink } = await import("fs/promises");
|
|
91
|
+
await unlink(filePath).catch(() => {
|
|
92
|
+
});
|
|
93
|
+
} else {
|
|
94
|
+
await writeFile(filePath, JSON.stringify(approval, null, 2));
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
async loadApprovals(filter) {
|
|
98
|
+
let files;
|
|
99
|
+
try {
|
|
100
|
+
await mkdir(this.approvalsDir, { recursive: true });
|
|
101
|
+
files = (await readdir(this.approvalsDir)).filter(
|
|
102
|
+
(f) => f.endsWith(".json")
|
|
103
|
+
);
|
|
104
|
+
} catch {
|
|
105
|
+
return [];
|
|
106
|
+
}
|
|
107
|
+
const states = await Promise.all(
|
|
108
|
+
files.map(async (f) => {
|
|
109
|
+
const fieldPath = f.replace(/\.json$/, "").replace(/_/g, ".");
|
|
110
|
+
const approval = JSON.parse(
|
|
111
|
+
await readFile(join(this.approvalsDir, f), "utf-8")
|
|
112
|
+
);
|
|
113
|
+
return {
|
|
114
|
+
fieldPath,
|
|
115
|
+
approval,
|
|
116
|
+
stale: false
|
|
117
|
+
};
|
|
118
|
+
})
|
|
119
|
+
);
|
|
120
|
+
if (!filter) return states;
|
|
121
|
+
return states.filter((s) => {
|
|
122
|
+
if (filter.fieldPath && s.fieldPath !== filter.fieldPath) return false;
|
|
123
|
+
return true;
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
// ContentEditAdapter implementation
|
|
127
|
+
async loadFields() {
|
|
128
|
+
let files;
|
|
129
|
+
try {
|
|
130
|
+
await mkdir(this.fieldsDir, { recursive: true });
|
|
131
|
+
files = (await readdir(this.fieldsDir)).filter((f) => f.endsWith(".json"));
|
|
132
|
+
} catch {
|
|
133
|
+
return {};
|
|
134
|
+
}
|
|
135
|
+
const entries = await Promise.all(
|
|
136
|
+
files.map(async (f) => {
|
|
137
|
+
const fieldPath = f.replace(/\.json$/, "").replace(/_/g, ".");
|
|
138
|
+
const value = JSON.parse(
|
|
139
|
+
await readFile(join(this.fieldsDir, f), "utf-8")
|
|
140
|
+
);
|
|
141
|
+
return [fieldPath, value];
|
|
142
|
+
})
|
|
143
|
+
);
|
|
144
|
+
return Object.fromEntries(entries);
|
|
145
|
+
}
|
|
146
|
+
async saveField(fieldPath, value) {
|
|
147
|
+
const safeName = fieldPath.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
148
|
+
await mkdir(this.fieldsDir, { recursive: true });
|
|
149
|
+
await writeFile(
|
|
150
|
+
join(this.fieldsDir, `${safeName}.json`),
|
|
151
|
+
JSON.stringify(value, null, 2)
|
|
152
|
+
);
|
|
153
|
+
const approvalPath = join(this.approvalsDir, `${safeName}.json`);
|
|
154
|
+
const { unlink } = await import("fs/promises");
|
|
155
|
+
await unlink(approvalPath).catch(() => {
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
async read(id) {
|
|
159
|
+
return JSON.parse(
|
|
160
|
+
await readFile(join(this.dir, `${id}.json`), "utf-8")
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
async write(id, item) {
|
|
164
|
+
await writeFile(join(this.dir, `${id}.json`), JSON.stringify(item, null, 2));
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
export {
|
|
168
|
+
DiskAdapter
|
|
169
|
+
};
|
package/dist/react/index.d.ts
CHANGED
|
@@ -1,11 +1,49 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { d as FeedtackTheme, c as FeedtackFlushEvent } from '../types-CHrWe7xT.js';
|
|
2
2
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
3
|
+
import { h as FieldApprovalState, F as FeedtackAdapter, u as FocusedFieldInfo, t as FieldChange, s as FeedtackUser, e as FeedbackItem } from '../adapter-Cn59URIG.js';
|
|
3
4
|
import React from 'react';
|
|
4
5
|
|
|
5
6
|
/** Fixed palette of 6 colors for pin markers */
|
|
6
7
|
declare const PIN_PALETTE: readonly ["#ef4444", "#3b82f6", "#22c55e", "#f59e0b", "#a855f7", "#ec4899"];
|
|
7
8
|
type PinColor = (typeof PIN_PALETTE)[number];
|
|
8
9
|
|
|
10
|
+
interface DeployCheckResult {
|
|
11
|
+
approved: boolean;
|
|
12
|
+
pending: string[];
|
|
13
|
+
}
|
|
14
|
+
interface UseContentApprovalOptions {
|
|
15
|
+
/**
|
|
16
|
+
* When provided, hash computation uses stored values from this map instead of
|
|
17
|
+
* reading element.textContent from the DOM. Use when static-build values may
|
|
18
|
+
* differ from live stored values (e.g. when used alongside useContentEdit).
|
|
19
|
+
*/
|
|
20
|
+
storedValues?: Map<string, string>;
|
|
21
|
+
}
|
|
22
|
+
interface UseContentApprovalResult {
|
|
23
|
+
fields: FieldApprovalState[];
|
|
24
|
+
approve: (fieldPath: string) => Promise<void>;
|
|
25
|
+
revoke: (fieldPath: string) => Promise<void>;
|
|
26
|
+
rescan: () => Promise<void>;
|
|
27
|
+
checkDeploy: () => Promise<DeployCheckResult>;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Hook for managing content field approvals.
|
|
31
|
+
* Requires an adapter that implements ContentAdapter.
|
|
32
|
+
*/
|
|
33
|
+
declare function useContentApproval(adapter: FeedtackAdapter, userId: string, options?: UseContentApprovalOptions): UseContentApprovalResult;
|
|
34
|
+
|
|
35
|
+
interface ContentEditToolbarProps {
|
|
36
|
+
focusedField: FocusedFieldInfo | null;
|
|
37
|
+
approvalState: FieldApprovalState | null;
|
|
38
|
+
changes: FieldChange[];
|
|
39
|
+
saving: string | null;
|
|
40
|
+
onApprove: (fieldPath: string) => Promise<void>;
|
|
41
|
+
onRevoke: (fieldPath: string) => Promise<void>;
|
|
42
|
+
onRevert: (fieldPath: string) => Promise<void>;
|
|
43
|
+
onCheckDeploy: () => Promise<DeployCheckResult>;
|
|
44
|
+
}
|
|
45
|
+
declare function ContentEditToolbar({ focusedField, approvalState, changes, saving, onApprove, onRevoke, onRevert, onCheckDeploy, }: ContentEditToolbarProps): react_jsx_runtime.JSX.Element;
|
|
46
|
+
|
|
9
47
|
interface FeedtackContextValue {
|
|
10
48
|
activatePinMode: () => void;
|
|
11
49
|
deactivatePinMode: () => void;
|
|
@@ -48,10 +86,31 @@ interface FeedtackProviderProps {
|
|
|
48
86
|
flushIdleMs?: number;
|
|
49
87
|
/** User roles that trigger re-scope on reply (default: any non-'agent' role) */
|
|
50
88
|
rescopeRoles?: string[];
|
|
89
|
+
/**
|
|
90
|
+
* Called by the consumer (e.g. on a Deploy button click) to check whether all
|
|
91
|
+
* content fields have current approvals. Feedtack surfaces the data; the consumer
|
|
92
|
+
* decides what to do with the result.
|
|
93
|
+
*/
|
|
94
|
+
onDeployCheck?: () => Promise<{
|
|
95
|
+
approved: boolean;
|
|
96
|
+
pending: string[];
|
|
97
|
+
}>;
|
|
51
98
|
}
|
|
52
99
|
declare function FeedtackProvider({ children, adapter, currentUser, hotkey, adminOnly, theme, classes, sentimentLabels, onError, disabled, renderPinIcon, onFlush, flushIdleMs, rescopeRoles, }: FeedtackProviderProps): react_jsx_runtime.JSX.Element;
|
|
53
100
|
|
|
101
|
+
interface UseContentEditResult extends UseContentApprovalResult {
|
|
102
|
+
active: boolean;
|
|
103
|
+
activate: () => Promise<void>;
|
|
104
|
+
deactivate: () => void;
|
|
105
|
+
changes: FieldChange[];
|
|
106
|
+
revert: (fieldPath: string) => Promise<void>;
|
|
107
|
+
saving: string | null;
|
|
108
|
+
focusedField: FocusedFieldInfo | null;
|
|
109
|
+
toolbarProps: ContentEditToolbarProps;
|
|
110
|
+
}
|
|
111
|
+
declare function useContentEdit(adapter: FeedtackAdapter, userId: string): UseContentEditResult;
|
|
112
|
+
|
|
54
113
|
/** Hook for host app to programmatically control feedtack */
|
|
55
114
|
declare function useFeedtack(): FeedtackContextValue;
|
|
56
115
|
|
|
57
|
-
export { type FeedtackClasses, type FeedtackContextValue, FeedtackFlushEvent, FeedtackProvider, type FeedtackProviderProps, type FeedtackSentimentLabels, PIN_PALETTE, type PinColor, useFeedtack };
|
|
116
|
+
export { ContentEditToolbar, type ContentEditToolbarProps, type DeployCheckResult, type FeedtackClasses, type FeedtackContextValue, FeedtackFlushEvent, FeedtackProvider, type FeedtackProviderProps, type FeedtackSentimentLabels, PIN_PALETTE, type PinColor, type UseContentApprovalOptions, type UseContentApprovalResult, type UseContentEditResult, useContentApproval, useContentEdit, useFeedtack };
|