sanity-plugin-sgntech-interview 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +173 -0
- package/dist/_chunks-es/index.js +17543 -0
- package/dist/_chunks-es/index.js.map +1 -0
- package/dist/_chunks-es/refractor.js +4653 -0
- package/dist/_chunks-es/refractor.js.map +1 -0
- package/dist/_chunks-es/speakers.js +36 -0
- package/dist/_chunks-es/speakers.js.map +1 -0
- package/dist/index.d.ts +517 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -0
- package/dist/render.d.ts +382 -0
- package/dist/render.js +165 -0
- package/dist/render.js.map +1 -0
- package/package.json +97 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
function findSpeaker(speakers, id) {
|
|
2
|
+
if (Array.isArray(speakers) && id)
|
|
3
|
+
return speakers.find((speaker) => speaker.id === id);
|
|
4
|
+
}
|
|
5
|
+
function askingSpeaker(speakers, value) {
|
|
6
|
+
return findSpeaker(speakers, value?.askedBy) || (speakers ?? []).find((speaker) => speaker.interviewer);
|
|
7
|
+
}
|
|
8
|
+
function speakerLabel(speaker, fallback = "\u2014") {
|
|
9
|
+
return speaker?.name?.trim() || speaker?.id?.trim() || fallback;
|
|
10
|
+
}
|
|
11
|
+
function timestampSeconds(input) {
|
|
12
|
+
const raw = (input ?? "").trim();
|
|
13
|
+
if (!raw) return;
|
|
14
|
+
const parts = raw.split(":");
|
|
15
|
+
if (parts.length < 2 || parts.length > 3 || !parts.every((part) => /^\d{1,3}$/.test(part))) return;
|
|
16
|
+
const numbers = parts.map(Number), [hours, minutes, seconds] = numbers.length === 3 ? numbers : [0, numbers[0] ?? 0, numbers[1] ?? 0];
|
|
17
|
+
if (!(minutes > 59 || seconds > 59))
|
|
18
|
+
return hours * 3600 + minutes * 60 + seconds;
|
|
19
|
+
}
|
|
20
|
+
function timestampUrl(source, seconds) {
|
|
21
|
+
if (!source || seconds === void 0) return source;
|
|
22
|
+
const separator = source.includes("?") ? "&" : "?";
|
|
23
|
+
return /youtu\.?be/.test(source) ? `${source}${separator}t=${seconds}` : /vimeo\./.test(source) ? `${source}#t=${seconds}s` : `${source}${separator}t=${seconds}`;
|
|
24
|
+
}
|
|
25
|
+
function usableSpeakers(speakers) {
|
|
26
|
+
return Array.isArray(speakers) ? speakers.filter((speaker) => !!speaker?.id?.trim()) : [];
|
|
27
|
+
}
|
|
28
|
+
export {
|
|
29
|
+
askingSpeaker,
|
|
30
|
+
findSpeaker,
|
|
31
|
+
speakerLabel,
|
|
32
|
+
timestampSeconds,
|
|
33
|
+
timestampUrl,
|
|
34
|
+
usableSpeakers
|
|
35
|
+
};
|
|
36
|
+
//# sourceMappingURL=speakers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"speakers.js","sources":["../../src/lib/speakers.ts"],"sourcesContent":["import type {InterviewValue, Speaker} from './types'\n\n/**\n * The speaker a block refers to, or the first interviewer when it asks for one\n * without naming it.\n *\n * @public\n */\nexport function findSpeaker(\n speakers: Speaker[] | undefined,\n id: string | undefined,\n): Speaker | undefined {\n if (!Array.isArray(speakers)) return undefined\n if (id) return speakers.find((speaker) => speaker.id === id)\n return undefined\n}\n\n/**\n * Who is asking: the block's own choice, otherwise the first speaker marked as\n * an interviewer.\n *\n * @public\n */\nexport function askingSpeaker(\n speakers: Speaker[] | undefined,\n value: InterviewValue | undefined,\n): Speaker | undefined {\n const named = findSpeaker(speakers, value?.askedBy)\n if (named) return named\n return (speakers ?? []).find((speaker) => speaker.interviewer)\n}\n\n/**\n * What to print for a speaker, falling back through name, id and a dash so a\n * half-filled interview still renders.\n *\n * @public\n */\nexport function speakerLabel(speaker: Speaker | undefined, fallback = '—'): string {\n return speaker?.name?.trim() || speaker?.id?.trim() || fallback\n}\n\n/**\n * Turns `12:34` or `1:02:33` into seconds, for links into a recording.\n *\n * Returns `undefined` for anything it cannot read, so a typo becomes a plain\n * label rather than a link to second zero.\n *\n * @public\n */\nexport function timestampSeconds(input: string | undefined): number | undefined {\n const raw = (input ?? '').trim()\n if (!raw) return undefined\n\n const parts = raw.split(':')\n if (parts.length < 2 || parts.length > 3) return undefined\n if (!parts.every((part) => /^\\d{1,3}$/.test(part))) return undefined\n\n const numbers = parts.map(Number)\n const [hours, minutes, seconds] =\n numbers.length === 3 ? numbers : [0, numbers[0] ?? 0, numbers[1] ?? 0]\n\n if (minutes > 59 || seconds > 59) return undefined\n return hours * 3600 + minutes * 60 + seconds\n}\n\n/**\n * Adds a start time to a media URL, in the form each service expects.\n *\n * @public\n */\nexport function timestampUrl(source: string | undefined, seconds: number | undefined): string | undefined {\n if (!source || seconds === undefined) return source\n\n const separator = source.includes('?') ? '&' : '?'\n if (/youtu\\.?be/.test(source)) return `${source}${separator}t=${seconds}`\n if (/vimeo\\./.test(source)) return `${source}#t=${seconds}s`\n return `${source}${separator}t=${seconds}`\n}\n\n/**\n * Speakers that can actually be offered in a picker.\n *\n * @public\n */\nexport function usableSpeakers(speakers: Speaker[] | undefined): Speaker[] {\n if (!Array.isArray(speakers)) return []\n return speakers.filter((speaker) => Boolean(speaker?.id?.trim()))\n}\n"],"names":[],"mappings":"AAQO,SAAS,YACd,UACA,IACqB;AACrB,MAAK,MAAM,QAAQ,QAAQ,KACvB;AAAI,WAAO,SAAS,KAAK,CAAC,YAAY,QAAQ,OAAO,EAAE;AAE7D;AAQO,SAAS,cACd,UACA,OACqB;AAErB,SADc,YAAY,UAAU,OAAO,OAAO,MAE1C,YAAY,CAAA,GAAI,KAAK,CAAC,YAAY,QAAQ,WAAW;AAC/D;AAQO,SAAS,aAAa,SAA8B,WAAW,UAAa;AACjF,SAAO,SAAS,MAAM,KAAA,KAAU,SAAS,IAAI,UAAU;AACzD;AAUO,SAAS,iBAAiB,OAA+C;AAC9E,QAAM,OAAO,SAAS,IAAI,KAAA;AAC1B,MAAI,CAAC,IAAK;AAEV,QAAM,QAAQ,IAAI,MAAM,GAAG;AAE3B,MADI,MAAM,SAAS,KAAK,MAAM,SAAS,KACnC,CAAC,MAAM,MAAM,CAAC,SAAS,YAAY,KAAK,IAAI,CAAC,EAAG;AAEpD,QAAM,UAAU,MAAM,IAAI,MAAM,GAC1B,CAAC,OAAO,SAAS,OAAO,IAC5B,QAAQ,WAAW,IAAI,UAAU,CAAC,GAAG,QAAQ,CAAC,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC;AAEvE,MAAI,EAAA,UAAU,MAAM,UAAU;AAC9B,WAAO,QAAQ,OAAO,UAAU,KAAK;AACvC;AAOO,SAAS,aAAa,QAA4B,SAAiD;AACxG,MAAI,CAAC,UAAU,YAAY,OAAW,QAAO;AAE7C,QAAM,YAAY,OAAO,SAAS,GAAG,IAAI,MAAM;AAC/C,SAAI,aAAa,KAAK,MAAM,IAAU,GAAG,MAAM,GAAG,SAAS,KAAK,OAAO,KACnE,UAAU,KAAK,MAAM,IAAU,GAAG,MAAM,MAAM,OAAO,MAClD,GAAG,MAAM,GAAG,SAAS,KAAK,OAAO;AAC1C;AAOO,SAAS,eAAe,UAA4C;AACzE,SAAK,MAAM,QAAQ,QAAQ,IACpB,SAAS,OAAO,CAAC,YAAY,CAAA,CAAQ,SAAS,IAAI,KAAA,CAAO,IAD3B,CAAA;AAEvC;"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,517 @@
|
|
|
1
|
+
import { ArrayRule } from "sanity";
|
|
2
|
+
import { ConditionalPropertyCallbackContext } from "sanity";
|
|
3
|
+
import { Plugin as Plugin_2 } from "sanity";
|
|
4
|
+
import { StringInputProps } from "sanity";
|
|
5
|
+
import { StringRule } from "sanity";
|
|
6
|
+
import { UrlRule } from "sanity";
|
|
7
|
+
import { WidenInitialValue } from "sanity";
|
|
8
|
+
import { WidenValidation } from "sanity";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Any object with an `_type` that is a string. Can hold any other properties.
|
|
12
|
+
* @public
|
|
13
|
+
*/
|
|
14
|
+
declare type ArbitraryTypedObject = TypedObject & {
|
|
15
|
+
[key: string]: any;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Who is asking: the block's own choice, otherwise the first speaker marked as
|
|
20
|
+
* an interviewer.
|
|
21
|
+
*
|
|
22
|
+
* @public
|
|
23
|
+
*/
|
|
24
|
+
export declare function askingSpeaker(
|
|
25
|
+
speakers: Speaker[] | undefined,
|
|
26
|
+
value: InterviewValue | undefined,
|
|
27
|
+
): Speaker | undefined;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* The question-and-answer block.
|
|
31
|
+
*
|
|
32
|
+
* @public
|
|
33
|
+
*/
|
|
34
|
+
export declare function createInterviewBlockType(config?: InterviewConfig): {
|
|
35
|
+
name: string;
|
|
36
|
+
title: string;
|
|
37
|
+
type: "object";
|
|
38
|
+
icon: typeof InterviewIcon;
|
|
39
|
+
fields: (
|
|
40
|
+
| ({
|
|
41
|
+
name: "speaker";
|
|
42
|
+
title: string;
|
|
43
|
+
type: "string";
|
|
44
|
+
components: {
|
|
45
|
+
input: typeof SpeakerInput;
|
|
46
|
+
};
|
|
47
|
+
validation: (rule: StringRule) => StringRule;
|
|
48
|
+
} & WidenValidation)
|
|
49
|
+
| ({
|
|
50
|
+
name: "question";
|
|
51
|
+
type: "string";
|
|
52
|
+
validation: (rule: StringRule) => StringRule;
|
|
53
|
+
} & WidenValidation)
|
|
54
|
+
| ({
|
|
55
|
+
name: "answer";
|
|
56
|
+
type: "array";
|
|
57
|
+
of: {
|
|
58
|
+
type: "block";
|
|
59
|
+
styles: {
|
|
60
|
+
title: string;
|
|
61
|
+
value: string;
|
|
62
|
+
}[];
|
|
63
|
+
lists: never[];
|
|
64
|
+
marks: {
|
|
65
|
+
decorators: {
|
|
66
|
+
title: string;
|
|
67
|
+
value: string;
|
|
68
|
+
}[];
|
|
69
|
+
annotations: {
|
|
70
|
+
type: "object";
|
|
71
|
+
name: "link";
|
|
72
|
+
title: string;
|
|
73
|
+
fields: ({
|
|
74
|
+
name: "href";
|
|
75
|
+
type: "url";
|
|
76
|
+
validation: (r: UrlRule) => UrlRule;
|
|
77
|
+
} & WidenValidation)[];
|
|
78
|
+
}[];
|
|
79
|
+
};
|
|
80
|
+
}[];
|
|
81
|
+
validation: (rule: ArrayRule<unknown[]>) => ArrayRule<unknown[]>;
|
|
82
|
+
} & WidenValidation)
|
|
83
|
+
| {
|
|
84
|
+
name: "askedBy";
|
|
85
|
+
title: string;
|
|
86
|
+
description: string;
|
|
87
|
+
type: "string";
|
|
88
|
+
components: {
|
|
89
|
+
input: typeof SpeakerInput;
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
| ({
|
|
93
|
+
name: "timestamp";
|
|
94
|
+
description: string;
|
|
95
|
+
type: "string";
|
|
96
|
+
validation: (rule: StringRule) => StringRule;
|
|
97
|
+
} & WidenValidation)
|
|
98
|
+
| ({
|
|
99
|
+
name: "translated";
|
|
100
|
+
title: string;
|
|
101
|
+
description: string;
|
|
102
|
+
type: "boolean";
|
|
103
|
+
initialValue: false;
|
|
104
|
+
} & WidenInitialValue)
|
|
105
|
+
| {
|
|
106
|
+
name: "originalAnswer";
|
|
107
|
+
title: string;
|
|
108
|
+
type: "text";
|
|
109
|
+
rows: number;
|
|
110
|
+
hidden: ({ parent }: ConditionalPropertyCallbackContext) => boolean;
|
|
111
|
+
}
|
|
112
|
+
)[];
|
|
113
|
+
preview: {
|
|
114
|
+
select: {
|
|
115
|
+
question: string;
|
|
116
|
+
speaker: string;
|
|
117
|
+
timestamp: string;
|
|
118
|
+
};
|
|
119
|
+
prepare: (select: Record<"speaker" | "question" | "timestamp", any>) => {
|
|
120
|
+
title: string;
|
|
121
|
+
subtitle: string;
|
|
122
|
+
};
|
|
123
|
+
};
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* The document field the blocks read their speakers from.
|
|
128
|
+
*
|
|
129
|
+
* Add it to any type that carries interviews:
|
|
130
|
+
*
|
|
131
|
+
* ```ts
|
|
132
|
+
* defineType({name: 'post', fields: [createSpeakersField(), /* … *\/]})
|
|
133
|
+
* ```
|
|
134
|
+
*
|
|
135
|
+
* @public
|
|
136
|
+
*/
|
|
137
|
+
export declare function createSpeakersField(config?: InterviewConfig): {
|
|
138
|
+
name: string;
|
|
139
|
+
title: string;
|
|
140
|
+
description: string;
|
|
141
|
+
type: "array";
|
|
142
|
+
of: {
|
|
143
|
+
type: "object";
|
|
144
|
+
name: "speaker";
|
|
145
|
+
fields: (
|
|
146
|
+
| ({
|
|
147
|
+
name: "id";
|
|
148
|
+
title: string;
|
|
149
|
+
description: string;
|
|
150
|
+
type: "string";
|
|
151
|
+
validation: (rule: StringRule) => StringRule;
|
|
152
|
+
} & WidenValidation)
|
|
153
|
+
| ({
|
|
154
|
+
name: "name";
|
|
155
|
+
type: "string";
|
|
156
|
+
validation: (rule: StringRule) => StringRule;
|
|
157
|
+
} & WidenValidation)
|
|
158
|
+
| {
|
|
159
|
+
name: "role";
|
|
160
|
+
description: string;
|
|
161
|
+
type: "string";
|
|
162
|
+
}
|
|
163
|
+
| ({
|
|
164
|
+
name: "interviewer";
|
|
165
|
+
title: string;
|
|
166
|
+
description: string;
|
|
167
|
+
type: "boolean";
|
|
168
|
+
initialValue: false;
|
|
169
|
+
} & WidenInitialValue)
|
|
170
|
+
| {
|
|
171
|
+
name: "person";
|
|
172
|
+
title: string;
|
|
173
|
+
description: string;
|
|
174
|
+
type: "reference";
|
|
175
|
+
to: {
|
|
176
|
+
type: string;
|
|
177
|
+
}[];
|
|
178
|
+
}
|
|
179
|
+
)[];
|
|
180
|
+
preview: {
|
|
181
|
+
select: {
|
|
182
|
+
title: string;
|
|
183
|
+
role: string;
|
|
184
|
+
id: string;
|
|
185
|
+
interviewer: string;
|
|
186
|
+
};
|
|
187
|
+
prepare: (
|
|
188
|
+
select: Record<"id" | "role" | "title" | "interviewer", any>,
|
|
189
|
+
) => {
|
|
190
|
+
title: string;
|
|
191
|
+
subtitle: string;
|
|
192
|
+
};
|
|
193
|
+
};
|
|
194
|
+
}[];
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* The speaker a block refers to, or the first interviewer when it asks for one
|
|
199
|
+
* without naming it.
|
|
200
|
+
*
|
|
201
|
+
* @public
|
|
202
|
+
*/
|
|
203
|
+
export declare function findSpeaker(
|
|
204
|
+
speakers: Speaker[] | undefined,
|
|
205
|
+
id: string | undefined,
|
|
206
|
+
): Speaker | undefined;
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Adds an `interviewBlock` to Sanity Studio: one question and its answer, as a
|
|
210
|
+
* block in the article body, so intros, photos and section headings can sit
|
|
211
|
+
* between the turns the way a printed interview does.
|
|
212
|
+
*
|
|
213
|
+
* Speakers are defined once on the document with `createSpeakersField`, and
|
|
214
|
+
* each block picks from that list — renaming somebody fixes every turn at once.
|
|
215
|
+
* An answer takes bold, italic and links, and can carry a timestamp into the
|
|
216
|
+
* recording plus a note when it was translated rather than quoted.
|
|
217
|
+
*
|
|
218
|
+
* ```ts
|
|
219
|
+
* import {defineArrayMember, defineConfig, defineField} from 'sanity'
|
|
220
|
+
* import {createSpeakersField, interview} from 'sanity-plugin-sgntech-interview'
|
|
221
|
+
*
|
|
222
|
+
* export default defineConfig({
|
|
223
|
+
* plugins: [interview()],
|
|
224
|
+
* schema: {
|
|
225
|
+
* types: [
|
|
226
|
+
* {
|
|
227
|
+
* type: 'document',
|
|
228
|
+
* name: 'post',
|
|
229
|
+
* fields: [
|
|
230
|
+
* createSpeakersField(),
|
|
231
|
+
* defineField({
|
|
232
|
+
* type: 'array',
|
|
233
|
+
* name: 'body',
|
|
234
|
+
* of: [defineArrayMember({type: 'block'}), defineArrayMember({type: 'interviewBlock'})],
|
|
235
|
+
* }),
|
|
236
|
+
* ],
|
|
237
|
+
* },
|
|
238
|
+
* ],
|
|
239
|
+
* },
|
|
240
|
+
* })
|
|
241
|
+
* ```
|
|
242
|
+
*
|
|
243
|
+
* The React renderer lives under `sanity-plugin-sgntech-interview/render` and
|
|
244
|
+
* imports neither `sanity` nor `@sanity/ui`.
|
|
245
|
+
*
|
|
246
|
+
* @public
|
|
247
|
+
*/
|
|
248
|
+
export declare const interview: Plugin_2<void | InterviewConfig>;
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Configuration for the plugin.
|
|
252
|
+
*
|
|
253
|
+
* @public
|
|
254
|
+
*/
|
|
255
|
+
export declare interface InterviewConfig {
|
|
256
|
+
/** Block type name. Defaults to `interviewBlock`. */
|
|
257
|
+
name?: string;
|
|
258
|
+
/** Label in the editor. Defaults to `Interview`. */
|
|
259
|
+
title?: string;
|
|
260
|
+
/** Field name the speakers live in. Defaults to `interviewSpeakers`. */
|
|
261
|
+
speakersField?: string;
|
|
262
|
+
/** Document types a speaker may point at, e.g. `['author', 'bandMember']`. */
|
|
263
|
+
speakerTypes?: string[];
|
|
264
|
+
/** Marks allowed in an answer. Defaults to bold, italic and link. */
|
|
265
|
+
answerMarks?: {
|
|
266
|
+
decorators?: string[];
|
|
267
|
+
link?: boolean;
|
|
268
|
+
};
|
|
269
|
+
/** Adds the timestamp field. Defaults to `true`. */
|
|
270
|
+
timestamps?: boolean;
|
|
271
|
+
/** Adds the translation fields. Defaults to `true`. */
|
|
272
|
+
translation?: boolean;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Two speech turns, drawn to match the stroke weight of `@sanity/icons`.
|
|
277
|
+
*/
|
|
278
|
+
export declare function InterviewIcon(): React.JSX.Element;
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* One question and its answer.
|
|
282
|
+
*
|
|
283
|
+
* @public
|
|
284
|
+
*/
|
|
285
|
+
export declare interface InterviewValue {
|
|
286
|
+
_key?: string;
|
|
287
|
+
_type?: string;
|
|
288
|
+
question?: string;
|
|
289
|
+
answer?: PortableTextBlock[];
|
|
290
|
+
/** Who is answering; matches {@link Speaker.id}. */
|
|
291
|
+
speaker?: string;
|
|
292
|
+
/** Who is asking, when it is not the document's default interviewer. */
|
|
293
|
+
askedBy?: string;
|
|
294
|
+
/** `12:34` or `1:02:33`, pointing into the recording. */
|
|
295
|
+
timestamp?: string;
|
|
296
|
+
/** Set when the answer was translated rather than quoted verbatim. */
|
|
297
|
+
translated?: boolean;
|
|
298
|
+
/** The answer as it was actually said. */
|
|
299
|
+
originalAnswer?: string;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* A Portable Text Block can be thought of as one paragraph, quote or list item.
|
|
304
|
+
* In other words, it is a container for text, that can have a visual style associated with it.
|
|
305
|
+
* The actual text value is stored in portable text spans inside of the `childen` array.
|
|
306
|
+
*
|
|
307
|
+
* @typeParam M - Mark types that be used for text spans
|
|
308
|
+
* @typeParam C - Types allowed as children of this block
|
|
309
|
+
* @typeParam S - Allowed block styles (eg `normal`, `blockquote`, `h3` etc)
|
|
310
|
+
* @typeParam L - Allowed list item types (eg `number`, `bullet` etc)
|
|
311
|
+
* @public
|
|
312
|
+
*/
|
|
313
|
+
declare interface PortableTextBlock<
|
|
314
|
+
M extends PortableTextMarkDefinition = PortableTextMarkDefinition,
|
|
315
|
+
C extends TypedObject = ArbitraryTypedObject | PortableTextSpan,
|
|
316
|
+
S extends string = PortableTextBlockStyle,
|
|
317
|
+
L extends string = PortableTextListItemType,
|
|
318
|
+
> extends TypedObject {
|
|
319
|
+
/**
|
|
320
|
+
* Type name identifying this as a portable text block.
|
|
321
|
+
* All items within a portable text array should have a `_type` property.
|
|
322
|
+
*
|
|
323
|
+
* Usually 'block', but can be customized to other values
|
|
324
|
+
*/
|
|
325
|
+
_type: "block" | (string & {});
|
|
326
|
+
/**
|
|
327
|
+
* A key that identifies this block uniquely within the parent array. Used to more easily address
|
|
328
|
+
* the block when editing collaboratively, but is also very useful for keys inside of React and
|
|
329
|
+
* other rendering frameworks that can use keys to optimize operations.
|
|
330
|
+
*/
|
|
331
|
+
_key?: string;
|
|
332
|
+
/**
|
|
333
|
+
* Array of inline items for this block. Usually contain text spans, but can be
|
|
334
|
+
* configured to include inline objects of other types as well.
|
|
335
|
+
*/
|
|
336
|
+
children: C[];
|
|
337
|
+
/**
|
|
338
|
+
* Array of mark definitions used in child text spans. By having them be on the block level,
|
|
339
|
+
* the same mark definition can be reused for multiple text spans, which is often the case
|
|
340
|
+
* with nested marks.
|
|
341
|
+
*/
|
|
342
|
+
markDefs?: M[];
|
|
343
|
+
/**
|
|
344
|
+
* Visual style of the block
|
|
345
|
+
* Common values: 'normal', 'blockquote', 'h1'...'h6'
|
|
346
|
+
*/
|
|
347
|
+
style?: S;
|
|
348
|
+
/**
|
|
349
|
+
* If this block is a list item, identifies which style of list item this is
|
|
350
|
+
* Common values: 'bullet', 'number', but can be configured
|
|
351
|
+
*/
|
|
352
|
+
listItem?: L;
|
|
353
|
+
/**
|
|
354
|
+
* If this block is a list item, identifies which level of nesting it belongs within
|
|
355
|
+
*/
|
|
356
|
+
level?: number;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* A set of _common_ (but not required/standarized) block styles
|
|
361
|
+
* @public
|
|
362
|
+
*/
|
|
363
|
+
declare type PortableTextBlockStyle =
|
|
364
|
+
| "normal"
|
|
365
|
+
| "blockquote"
|
|
366
|
+
| "h1"
|
|
367
|
+
| "h2"
|
|
368
|
+
| "h3"
|
|
369
|
+
| "h4"
|
|
370
|
+
| "h5"
|
|
371
|
+
| "h6"
|
|
372
|
+
| (string & {});
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* A set of _common_ (but not required/standardized) list item types
|
|
376
|
+
* @public
|
|
377
|
+
*/
|
|
378
|
+
declare type PortableTextListItemType = "bullet" | "number" | (string & {});
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* A mark definition holds information for marked text. For instance, a text span could reference
|
|
382
|
+
* a mark definition for a hyperlink, a geoposition, a reference to a document or anything that is
|
|
383
|
+
* representable as a JSON object.
|
|
384
|
+
* @public
|
|
385
|
+
*/
|
|
386
|
+
declare interface PortableTextMarkDefinition {
|
|
387
|
+
/**
|
|
388
|
+
* Unknown properties
|
|
389
|
+
*/
|
|
390
|
+
[key: string]: unknown;
|
|
391
|
+
/**
|
|
392
|
+
* Identifies the type of mark this is, and is used to pick the correct React components to use
|
|
393
|
+
* when rendering a text span marked with this mark type.
|
|
394
|
+
*/
|
|
395
|
+
_type: string;
|
|
396
|
+
/**
|
|
397
|
+
* Uniquely identifies this mark definition within the block
|
|
398
|
+
*/
|
|
399
|
+
_key: string;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* A Portable Text Span holds a chunk of the actual text value of a Portable Text Block
|
|
404
|
+
* @public
|
|
405
|
+
*/
|
|
406
|
+
declare interface PortableTextSpan {
|
|
407
|
+
/**
|
|
408
|
+
* Type is always `span` for portable text spans, as these don't vary in shape
|
|
409
|
+
*/
|
|
410
|
+
_type: "span";
|
|
411
|
+
/**
|
|
412
|
+
* Unique (within parent block) key for this portable text span
|
|
413
|
+
*/
|
|
414
|
+
_key?: string;
|
|
415
|
+
/**
|
|
416
|
+
* The actual text value of this text span
|
|
417
|
+
*/
|
|
418
|
+
text: string;
|
|
419
|
+
/**
|
|
420
|
+
* An array of marks this text span is annotated with, identified by its `_key`.
|
|
421
|
+
* If the key cannot be found in the parent blocks mark definition, the mark is assumed to be a
|
|
422
|
+
* decorator (a simpler mark without any properties - for instance `strong` or `em`)
|
|
423
|
+
*/
|
|
424
|
+
marks?: string[];
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* Someone taking part in the interview, defined once on the document.
|
|
429
|
+
*
|
|
430
|
+
* @public
|
|
431
|
+
*/
|
|
432
|
+
export declare interface Speaker {
|
|
433
|
+
_key?: string;
|
|
434
|
+
/** Short id the blocks refer to, e.g. `till`. */
|
|
435
|
+
id?: string;
|
|
436
|
+
name?: string;
|
|
437
|
+
/** Shown under the name, e.g. `Guitarist` or `Editor`. */
|
|
438
|
+
role?: string;
|
|
439
|
+
/** Marks who is asking, so a renderer can style questions differently. */
|
|
440
|
+
interviewer?: boolean;
|
|
441
|
+
/** Set when the speaker is a reference to a person document. */
|
|
442
|
+
person?: {
|
|
443
|
+
_ref?: string;
|
|
444
|
+
_type?: string;
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* Picks one of the speakers defined on the document.
|
|
450
|
+
*
|
|
451
|
+
* @public
|
|
452
|
+
*/
|
|
453
|
+
export declare function SpeakerInput(
|
|
454
|
+
props: StringInputProps,
|
|
455
|
+
): React.JSX.Element;
|
|
456
|
+
|
|
457
|
+
/**
|
|
458
|
+
* What to print for a speaker, falling back through name, id and a dash so a
|
|
459
|
+
* half-filled interview still renders.
|
|
460
|
+
*
|
|
461
|
+
* @public
|
|
462
|
+
*/
|
|
463
|
+
export declare function speakerLabel(
|
|
464
|
+
speaker: Speaker | undefined,
|
|
465
|
+
fallback?: string,
|
|
466
|
+
): string;
|
|
467
|
+
|
|
468
|
+
/**
|
|
469
|
+
* Turns `12:34` or `1:02:33` into seconds, for links into a recording.
|
|
470
|
+
*
|
|
471
|
+
* Returns `undefined` for anything it cannot read, so a typo becomes a plain
|
|
472
|
+
* label rather than a link to second zero.
|
|
473
|
+
*
|
|
474
|
+
* @public
|
|
475
|
+
*/
|
|
476
|
+
export declare function timestampSeconds(
|
|
477
|
+
input: string | undefined,
|
|
478
|
+
): number | undefined;
|
|
479
|
+
|
|
480
|
+
/**
|
|
481
|
+
* Adds a start time to a media URL, in the form each service expects.
|
|
482
|
+
*
|
|
483
|
+
* @public
|
|
484
|
+
*/
|
|
485
|
+
export declare function timestampUrl(
|
|
486
|
+
source: string | undefined,
|
|
487
|
+
seconds: number | undefined,
|
|
488
|
+
): string | undefined;
|
|
489
|
+
|
|
490
|
+
/**
|
|
491
|
+
* Any object with an `_type` property (which is required in portable text arrays),
|
|
492
|
+
* as well as a _potential_ `_key` (highly encouraged)
|
|
493
|
+
* @public
|
|
494
|
+
*/
|
|
495
|
+
declare interface TypedObject {
|
|
496
|
+
/**
|
|
497
|
+
* Identifies the type of object/span this is, and is used to pick the correct React components
|
|
498
|
+
* to use when rendering a span or inline object with this type.
|
|
499
|
+
*/
|
|
500
|
+
_type: string;
|
|
501
|
+
/**
|
|
502
|
+
* Uniquely identifies this object within its parent block.
|
|
503
|
+
* Not _required_, but highly encouraged.
|
|
504
|
+
*/
|
|
505
|
+
_key?: string;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
/**
|
|
509
|
+
* Speakers that can actually be offered in a picker.
|
|
510
|
+
*
|
|
511
|
+
* @public
|
|
512
|
+
*/
|
|
513
|
+
export declare function usableSpeakers(
|
|
514
|
+
speakers: Speaker[] | undefined,
|
|
515
|
+
): Speaker[];
|
|
516
|
+
|
|
517
|
+
export {};
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import "sanity";
|
|
2
|
+
import { InterviewIcon, SpeakerInput, createInterviewBlockType, createSpeakersField, interview } from "./_chunks-es/index.js";
|
|
3
|
+
import { askingSpeaker, findSpeaker, speakerLabel, timestampSeconds, timestampUrl, usableSpeakers } from "./_chunks-es/speakers.js";
|
|
4
|
+
export {
|
|
5
|
+
InterviewIcon,
|
|
6
|
+
SpeakerInput,
|
|
7
|
+
askingSpeaker,
|
|
8
|
+
createInterviewBlockType,
|
|
9
|
+
createSpeakersField,
|
|
10
|
+
findSpeaker,
|
|
11
|
+
interview,
|
|
12
|
+
speakerLabel,
|
|
13
|
+
timestampSeconds,
|
|
14
|
+
timestampUrl,
|
|
15
|
+
usableSpeakers
|
|
16
|
+
};
|
|
17
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;"}
|