@portabletext/plugin-typography 2.0.3 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -76,21 +76,84 @@ The plugin includes the following typographic transformation rules:
76
76
  | `oneQuarter` | `1/4` → `¼` |
77
77
  | `threeQuarters` | `3/4` → `¾` |
78
78
 
79
- ## Configuring what Rules are Enabled
79
+ ## Configuring Rules
80
80
 
81
- Use the `rules` prop on the `TypographyPlugin` to control which rules are enabled:
81
+ The plugin supports flexible configuration through `preset`, `enable`, and `disable` props:
82
+
83
+ ### Using Presets
84
+
85
+ The `preset` prop provides quick configuration:
86
+
87
+ - `'default'` (default): Common typography rules enabled
88
+ - `'all'`: Enable all rules
89
+ - `'none'`: Start with no rules (use with `enable`)
82
90
 
83
91
  ```tsx
84
- return <TypographyPlugin rules={{multiplication: 'on'}} />
92
+ // Use default rules (most common transformations)
93
+ <TypographyPlugin />
94
+
95
+ // Enable everything
96
+ <TypographyPlugin preset="all" />
97
+
98
+ // Start from scratch and enable only specific rules
99
+ <TypographyPlugin preset="none" enable={['emDash', 'ellipsis']} />
100
+ ```
101
+
102
+ ### Enabling Additional Rules
103
+
104
+ Use `enable` to add rules beyond the preset:
105
+
106
+ ```tsx
107
+ // Add multiplication and plusMinus to default rules
108
+ <TypographyPlugin enable={['multiplication', 'plusMinus']} />
109
+
110
+ // Enable all rules except a few
111
+ <TypographyPlugin preset="all" disable={['emDash', 'ellipsis']} />
85
112
  ```
86
113
 
87
- `emDash`, `ellipsis`, `openingDoubleQuote`, `closingDoubleQuote`, `openingSingleQuote`, `closingSingleQuote`, `leftArrow`, `rightArrow`, `copyright`, `trademark`, `servicemark`, and `registeredTrademark` are `'on'` by default.
114
+ ### Disabling Rules
115
+
116
+ Use `disable` to remove rules from the preset:
117
+
118
+ ```tsx
119
+ // Use defaults but disable quote transformations
120
+ <TypographyPlugin
121
+ disable={[
122
+ 'openingDoubleQuote',
123
+ 'closingDoubleQuote',
124
+ 'openingSingleQuote',
125
+ 'closingSingleQuote',
126
+ ]}
127
+ />
128
+ ```
129
+
130
+ ### Combining Options
131
+
132
+ All three props work together. The order of operations is:
133
+
134
+ 1. Start with `preset` (default: `'default'`)
135
+ 2. Add rules from `enable`
136
+ 3. Remove rules from `disable`
137
+
138
+ ```tsx
139
+ // Start with all rules, then customize
140
+ <TypographyPlugin
141
+ preset="all"
142
+ disable={['multiplication']}
143
+ />
144
+
145
+ // Start with none, enable only math symbols
146
+ <TypographyPlugin
147
+ preset="none"
148
+ enable={['multiplication', 'plusMinus', 'notEqual']}
149
+ />
150
+ ```
88
151
 
89
152
  ## Controlling when Text Transformations Run
90
153
 
91
154
  The `TypographyPlugin` has an optional `guard` prop that accepts any type of `BehaviorGuard`. Here, you'll have access to the current `EditorSnapshot` as well as information about the current rule being matched.
92
155
 
93
- Because disallowing text transformations inside code is a common use case, this plugin ships a built-in `createDecoratorGuard` function. use that to create a `guard` that disallows text transformations inside certain decorators:
156
+ Because disallowing text transformations inside code is a common use case, this plugin ships a built-in `createDecoratorGuard` function. use that to create a `guard` that only allows text transformations inside certain decorators:
94
157
 
95
158
  ```tsx
96
159
  import {
@@ -101,9 +164,9 @@ import {
101
164
  return (
102
165
  <TypographyPlugin
103
166
  guard={createDecoratorGuard({
104
- decorators: ({schema}) =>
105
- schema.decorators.flatMap((decorator) =>
106
- decorator.name === 'code' ? [decorator.name] : [],
167
+ decorators: ({context}) =>
168
+ context.schema.decorators.flatMap((decorator) =>
169
+ decorator.name === 'code' ? [] : [decorator.name],
107
170
  ),
108
171
  })}
109
172
  />
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type {EditorSchema} from '@portabletext/editor'
1
+ import type {EditorContext, EditorSchema} from '@portabletext/editor'
2
2
  import {InputRule, InputRuleGuard} from '@portabletext/plugin-input-rule'
3
3
  import {JSX} from 'react'
4
4
 
@@ -25,16 +25,133 @@ export declare const copyrightRule: InputRule<true>
25
25
  * @example
26
26
  * ```tsx
27
27
  * const guard = createDecoratorGuard({
28
- * decorators: ({schema}) => schema.decorators.flatMap((decorator) => decorator.name === 'code' ? [decorator.name] : []),
28
+ * decorators: ({context}) => context.schema.decorators.flatMap((decorator) => decorator.name === 'code' ? [] : [decorator.name]),
29
29
  * })
30
30
  *
31
31
  * <TypographyPlugin guard={guard} />
32
32
  * ```
33
33
  */
34
34
  export declare function createDecoratorGuard(config: {
35
- decorators: ({schema}: {schema: EditorSchema}) => Array<string>
35
+ decorators: ({
36
+ context,
37
+ }: {
38
+ context: Pick<EditorContext, 'schema'>
39
+ }) => Array<EditorSchema['decorators'][number]['name']>
36
40
  }): InputRuleGuard
37
41
 
42
+ declare const defaultRuleConfig: readonly [
43
+ {
44
+ readonly name: 'emDash'
45
+ readonly rule: InputRule<true>
46
+ readonly state: 'on'
47
+ },
48
+ {
49
+ readonly name: 'ellipsis'
50
+ readonly rule: InputRule<true>
51
+ readonly state: 'on'
52
+ },
53
+ {
54
+ readonly name: 'openingDoubleQuote'
55
+ readonly rule: InputRule<true>
56
+ readonly state: 'on'
57
+ },
58
+ {
59
+ readonly name: 'closingDoubleQuote'
60
+ readonly rule: InputRule<true>
61
+ readonly state: 'on'
62
+ },
63
+ {
64
+ readonly name: 'openingSingleQuote'
65
+ readonly rule: InputRule<true>
66
+ readonly state: 'on'
67
+ },
68
+ {
69
+ readonly name: 'closingSingleQuote'
70
+ readonly rule: InputRule<true>
71
+ readonly state: 'on'
72
+ },
73
+ {
74
+ readonly name: 'leftArrow'
75
+ readonly rule: InputRule<true>
76
+ readonly state: 'on'
77
+ },
78
+ {
79
+ readonly name: 'rightArrow'
80
+ readonly rule: InputRule<true>
81
+ readonly state: 'on'
82
+ },
83
+ {
84
+ readonly name: 'copyright'
85
+ readonly rule: InputRule<true>
86
+ readonly state: 'on'
87
+ },
88
+ {
89
+ readonly name: 'trademark'
90
+ readonly rule: InputRule<true>
91
+ readonly state: 'on'
92
+ },
93
+ {
94
+ readonly name: 'servicemark'
95
+ readonly rule: InputRule<true>
96
+ readonly state: 'on'
97
+ },
98
+ {
99
+ readonly name: 'registeredTrademark'
100
+ readonly rule: InputRule<true>
101
+ readonly state: 'on'
102
+ },
103
+ {
104
+ readonly name: 'oneHalf'
105
+ readonly rule: InputRule<true>
106
+ readonly state: 'off'
107
+ },
108
+ {
109
+ readonly name: 'plusMinus'
110
+ readonly rule: InputRule<true>
111
+ readonly state: 'off'
112
+ },
113
+ {
114
+ readonly name: 'laquo'
115
+ readonly rule: InputRule<true>
116
+ readonly state: 'off'
117
+ },
118
+ {
119
+ readonly name: 'notEqual'
120
+ readonly rule: InputRule<true>
121
+ readonly state: 'off'
122
+ },
123
+ {
124
+ readonly name: 'raquo'
125
+ readonly rule: InputRule<true>
126
+ readonly state: 'off'
127
+ },
128
+ {
129
+ readonly name: 'multiplication'
130
+ readonly rule: InputRule<true>
131
+ readonly state: 'off'
132
+ },
133
+ {
134
+ readonly name: 'superscriptTwo'
135
+ readonly rule: InputRule<true>
136
+ readonly state: 'off'
137
+ },
138
+ {
139
+ readonly name: 'superscriptThree'
140
+ readonly rule: InputRule<true>
141
+ readonly state: 'off'
142
+ },
143
+ {
144
+ readonly name: 'oneQuarter'
145
+ readonly rule: InputRule<true>
146
+ readonly state: 'off'
147
+ },
148
+ {
149
+ readonly name: 'threeQuarters'
150
+ readonly rule: InputRule<true>
151
+ readonly state: 'off'
152
+ },
153
+ ]
154
+
38
155
  /**
39
156
  * @public
40
157
  */
@@ -105,6 +222,8 @@ export declare const registeredTrademarkRule: InputRule<true>
105
222
  */
106
223
  export declare const rightArrowRule: InputRule<true>
107
224
 
225
+ declare type RuleName = (typeof defaultRuleConfig)[number]['name']
226
+
108
227
  /**
109
228
  * @public
110
229
  */
@@ -138,109 +257,53 @@ export declare const trademarkRule: InputRule<true>
138
257
  /**
139
258
  * @public
140
259
  */
141
- export declare function TypographyPlugin(
142
- props: TypographyPluginProps,
260
+ export declare function TypographyPlugin<
261
+ TEnabledRuleName extends RuleName = never,
262
+ TDisabledRuleName extends Exclude<RuleName, TEnabledRuleName> = never,
263
+ >(
264
+ props: TypographyPluginProps<TEnabledRuleName, TDisabledRuleName>,
143
265
  ): JSX.Element
144
266
 
145
267
  /**
146
268
  * @public
147
269
  */
148
- export declare type TypographyPluginProps = {
270
+ export declare type TypographyPluginProps<
271
+ TEnabledRuleName extends RuleName = never,
272
+ TDisabledRuleName extends Exclude<RuleName, TEnabledRuleName> = never,
273
+ > = {
149
274
  guard?: InputRuleGuard
150
275
  /**
151
- * Configure which rules to enable or disable. Ordinary rules like `emDash` and `ellipsis` are enabled by default.
152
- * Less common rules like `multiplication` are disabled by default.
276
+ * Preset configuration for rules.
277
+ * - `'default'`: Common typography rules enabled (em dash, ellipsis, quotes, arrows, copyright symbols)
278
+ * - `'all'`: All rules enabled
279
+ * - `'none'`: No rules enabled (use with `enable` prop)
280
+ *
281
+ * @defaultValue 'default'
282
+ */
283
+ preset?: 'default' | 'all' | 'none'
284
+ /**
285
+ * Enable specific rules (additive to preset).
286
+ * Use this to enable additional rules beyond the preset.
287
+ *
288
+ * @example
289
+ * ```tsx
290
+ * // Enable multiplication and plusMinus in addition to default rules
291
+ * <TypographyPlugin enable={['multiplication', 'plusMinus']} />
292
+ * ```
293
+ */
294
+ enable?: ReadonlyArray<TEnabledRuleName>
295
+ /**
296
+ * Disable specific rules (subtractive from preset).
297
+ * Use this to disable rules that would otherwise be enabled by the preset.
298
+ * Cannot contain rules that are in the `enable` array (TypeScript will enforce this).
299
+ *
300
+ * @example
301
+ * ```tsx
302
+ * // Disable em dash from the default rules
303
+ * <TypographyPlugin disable={['emDash']} />
304
+ * ```
153
305
  */
154
- rules?: {
155
- /**
156
- * @defaultValue 'on'
157
- */
158
- emDash?: 'on' | 'off'
159
- /**
160
- * @defaultValue 'on'
161
- */
162
- ellipsis?: 'on' | 'off'
163
- /**
164
- * @defaultValue 'on'
165
- */
166
- openingDoubleQuote?: 'on' | 'off'
167
- /**
168
- * @defaultValue 'on'
169
- */
170
- closingDoubleQuote?: 'on' | 'off'
171
- /**
172
- * @defaultValue 'on'
173
- */
174
- openingSingleQuote?: 'on' | 'off'
175
- /**
176
- * @defaultValue 'on'
177
- */
178
- closingSingleQuote?: 'on' | 'off'
179
- /**
180
- * @defaultValue 'on'
181
- */
182
- leftArrow?: 'on' | 'off'
183
- /**
184
- * @defaultValue 'on'
185
- */
186
- rightArrow?: 'on' | 'off'
187
- /**
188
- * @defaultValue 'on'
189
- */
190
- copyright?: 'on' | 'off'
191
- /**
192
- * @defaultValue 'on'
193
- */
194
- trademark?: 'on' | 'off'
195
- /**
196
- * @defaultValue 'on'
197
- */
198
- servicemark?: 'on' | 'off'
199
- /**
200
- * @defaultValue 'on'
201
- */
202
- registeredTrademark?: 'on' | 'off'
203
- /**
204
- * @defaultValue 'off'
205
- */
206
- oneHalf?: 'on' | 'off'
207
- /**
208
- * @defaultValue 'off'
209
- */
210
- plusMinus?: 'on' | 'off'
211
- /**
212
- * @defaultValue 'off'
213
- */
214
- notEqual?: 'on' | 'off'
215
- /**
216
- * @defaultValue 'off'
217
- */
218
- laquo?: 'on' | 'off'
219
- /**
220
- * @defaultValue 'off'
221
- */
222
- raquo?: 'on' | 'off'
223
- /**
224
- * @defaultValue 'off'
225
- */
226
- multiplication?: 'on' | 'off'
227
- /**
228
- * @defaultValue 'off'
229
- */
230
- superscriptTwo?: 'on' | 'off'
231
- /**
232
- * @defaultValue 'off'
233
- */
234
- superscriptThree?: 'on' | 'off'
235
- /**
236
- * @defaultValue 'off'
237
- */
238
- oneQuarter?: 'on' | 'off'
239
- /**
240
- * @defaultValue 'off'
241
- */
242
- threeQuarters?: 'on' | 'off'
243
- }
306
+ disable?: ReadonlyArray<TDisabledRuleName>
244
307
  }
245
308
 
246
309
  export {}
package/dist/index.js CHANGED
@@ -1,15 +1,17 @@
1
1
  import { getSelectedSpans, isActiveDecorator } from "@portabletext/editor/selectors";
2
2
  import { defineTextTransformRule, InputRulePlugin } from "@portabletext/plugin-input-rule";
3
3
  import { jsx } from "react/jsx-runtime";
4
- import { c } from "react/compiler-runtime";
4
+ import { c } from "react-compiler-runtime";
5
5
  function createDecoratorGuard(config) {
6
6
  return ({
7
7
  snapshot,
8
8
  event
9
9
  }) => {
10
- const decorators = config.decorators({
11
- schema: snapshot.context.schema
12
- });
10
+ const allowedDecorators = config.decorators({
11
+ context: {
12
+ schema: snapshot.context.schema
13
+ }
14
+ }), decorators = snapshot.context.schema.decorators.flatMap((decorator) => allowedDecorators.includes(decorator.name) ? [] : [decorator.name]);
13
15
  if (decorators.length === 0)
14
16
  return !0;
15
17
  const matchedSpans = event.matches.flatMap((match) => getSelectedSpans({
@@ -189,21 +191,41 @@ const emDashRule = defineTextTransformRule({
189
191
  state: "off"
190
192
  }];
191
193
  function TypographyPlugin(props) {
192
- const $ = c(6);
193
- let t0;
194
- $[0] !== props.guard || $[1] !== props.rules ? (t0 = defaultRuleConfig.flatMap((rule) => props.rules && props.rules[rule.name] === "on" ? {
195
- ...rule.rule,
196
- guard: props.guard ?? _temp
197
- } : props.rules && props.rules[rule.name] === "off" || rule.state === "off" ? [] : {
198
- ...rule.rule,
199
- guard: props.guard ?? _temp2
200
- }), $[0] = props.guard, $[1] = props.rules, $[2] = t0) : t0 = $[2];
201
- const configuredInputRules = t0;
202
- let t1;
203
- return $[3] !== configuredInputRules || $[4] !== props ? (t1 = /* @__PURE__ */ jsx(InputRulePlugin, { ...props, rules: configuredInputRules }), $[3] = configuredInputRules, $[4] = props, $[5] = t1) : t1 = $[5], t1;
204
- }
205
- function _temp2() {
206
- return !0;
194
+ const $ = c(13), {
195
+ preset: t0,
196
+ enable: t1,
197
+ disable: t2,
198
+ guard
199
+ } = props, preset = t0 === void 0 ? "default" : t0;
200
+ let t3;
201
+ $[0] !== t1 ? (t3 = t1 === void 0 ? [] : t1, $[0] = t1, $[1] = t3) : t3 = $[1];
202
+ const enable = t3;
203
+ let t4;
204
+ $[2] !== t2 ? (t4 = t2 === void 0 ? [] : t2, $[2] = t2, $[3] = t4) : t4 = $[3];
205
+ const disable = t4;
206
+ let enabledRules;
207
+ if ($[4] !== disable || $[5] !== enable || $[6] !== preset) {
208
+ if (enabledRules = /* @__PURE__ */ new Set(), preset === "all")
209
+ for (const rule of defaultRuleConfig)
210
+ enabledRules.add(rule.name);
211
+ else if (preset === "default")
212
+ for (const rule_0 of defaultRuleConfig)
213
+ rule_0.state === "on" && enabledRules.add(rule_0.name);
214
+ for (const ruleName of enable)
215
+ enabledRules.add(ruleName);
216
+ for (const ruleName_0 of disable)
217
+ enabledRules.delete(ruleName_0);
218
+ $[4] = disable, $[5] = enable, $[6] = preset, $[7] = enabledRules;
219
+ } else
220
+ enabledRules = $[7];
221
+ let t5;
222
+ $[8] !== enabledRules || $[9] !== guard ? (t5 = defaultRuleConfig.flatMap((rule_1) => enabledRules.has(rule_1.name) ? [{
223
+ ...rule_1.rule,
224
+ guard: guard ?? _temp
225
+ }] : []), $[8] = enabledRules, $[9] = guard, $[10] = t5) : t5 = $[10];
226
+ const configuredInputRules = t5;
227
+ let t6;
228
+ return $[11] !== configuredInputRules ? (t6 = /* @__PURE__ */ jsx(InputRulePlugin, { rules: configuredInputRules }), $[11] = configuredInputRules, $[12] = t6) : t6 = $[12], t6;
207
229
  }
208
230
  function _temp() {
209
231
  return !0;
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/create-decorator-guard.ts","../src/input-rules.typography.ts","../src/plugin.typography.tsx"],"sourcesContent":["import type {EditorSchema} from '@portabletext/editor'\nimport {\n getSelectedSpans,\n isActiveDecorator,\n} from '@portabletext/editor/selectors'\nimport type {InputRuleGuard} from '@portabletext/plugin-input-rule'\n\n/**\n * @public\n * Create an `InputRuleGuard` that can prevent the rule from running inside\n * certain decorators.\n *\n * @example\n * ```tsx\n * const guard = createDecoratorGuard({\n * decorators: ({schema}) => schema.decorators.flatMap((decorator) => decorator.name === 'code' ? [decorator.name] : []),\n * })\n *\n * <TypographyPlugin guard={guard} />\n * ```\n */\nexport function createDecoratorGuard(config: {\n decorators: ({schema}: {schema: EditorSchema}) => Array<string>\n}): InputRuleGuard {\n return ({snapshot, event}) => {\n const decorators = config.decorators({schema: snapshot.context.schema})\n\n if (decorators.length === 0) {\n return true\n }\n\n const matchedSpans = event.matches.flatMap((match) =>\n getSelectedSpans({\n ...snapshot,\n context: {\n ...snapshot.context,\n selection: match.selection,\n },\n }),\n )\n\n let preventInputRule = false\n\n for (const decorator of decorators) {\n if (isActiveDecorator(decorator)(snapshot)) {\n preventInputRule = true\n break\n }\n\n if (matchedSpans.some((span) => span.node.marks?.includes(decorator))) {\n preventInputRule = true\n break\n }\n }\n\n return !preventInputRule\n }\n}\n","import {\n defineTextTransformRule,\n type InputRule,\n} from '@portabletext/plugin-input-rule'\n\n/**\n * @public\n */\nexport const emDashRule = defineTextTransformRule({\n on: /--/,\n transform: () => '—',\n})\n\n/**\n * @public\n */\nexport const ellipsisRule = defineTextTransformRule({\n on: /\\.\\.\\./,\n transform: () => '…',\n})\n\n/**\n * @public\n */\nexport const openingDoubleQuoteRule = defineTextTransformRule({\n on: /(?:^|(?<=[\\s{[(<'\"\\u2018\\u201C]))\"/g,\n transform: () => '“',\n})\n\n/**\n * @public\n */\nexport const closingDoubleQuoteRule = defineTextTransformRule({\n on: /\"/g,\n transform: () => '”',\n})\n\n/**\n * @public\n */\nexport const openingSingleQuoteRule = defineTextTransformRule({\n on: /(?:^|(?<=[\\s{[(<'\"\\u2018\\u201C]))'/g,\n transform: () => '‘',\n})\n\n/**\n * @public\n */\nexport const closingSingleQuoteRule = defineTextTransformRule({\n on: /'/g,\n transform: () => '’',\n})\n\n/**\n * @public\n */\nexport const smartQuotesRules: Array<InputRule> = [\n openingDoubleQuoteRule,\n closingDoubleQuoteRule,\n openingSingleQuoteRule,\n closingSingleQuoteRule,\n]\n\n/**\n * @public\n */\nexport const leftArrowRule = defineTextTransformRule({\n on: /<-/,\n transform: () => '←',\n})\n\n/**\n * @public\n */\nexport const rightArrowRule = defineTextTransformRule({\n on: /->/,\n transform: () => '→',\n})\n\n/**\n * @public\n */\nexport const copyrightRule = defineTextTransformRule({\n on: /\\(c\\)/,\n transform: () => '©',\n})\n\n/**\n * @public\n */\nexport const servicemarkRule = defineTextTransformRule({\n on: /\\(sm\\)/,\n transform: () => '℠',\n})\n\n/**\n * @public\n */\nexport const trademarkRule = defineTextTransformRule({\n on: /\\(tm\\)/,\n transform: () => '™',\n})\n\n/**\n * @beta\n */\nexport const registeredTrademarkRule = defineTextTransformRule({\n on: /\\(r\\)/,\n transform: () => '®',\n})\n\n/**\n * @public\n */\nexport const oneHalfRule = defineTextTransformRule({\n on: /(?:^|\\s)(1\\/2)\\s/,\n transform: () => '½',\n})\n\n/**\n * @public\n */\nexport const plusMinusRule = defineTextTransformRule({\n on: /\\+\\/-/,\n transform: () => '±',\n})\n\n/**\n * @public\n */\nexport const notEqualRule = defineTextTransformRule({\n on: /!=/,\n transform: () => '≠',\n})\n\n/**\n * @public\n */\nexport const laquoRule = defineTextTransformRule({\n on: /<</,\n transform: () => '«',\n})\n\n/**\n * @public\n */\nexport const raquoRule = defineTextTransformRule({\n on: />>/,\n transform: () => '»',\n})\n\n/**\n * @public\n */\nexport const multiplicationRule = defineTextTransformRule({\n on: /\\d+\\s?([*x])\\s?\\d+/,\n transform: () => '×',\n})\n\n/**\n * @public\n */\nexport const superscriptTwoRule = defineTextTransformRule({\n on: /\\^2/,\n transform: () => '²',\n})\n\n/**\n * @public\n */\nexport const superscriptThreeRule = defineTextTransformRule({\n on: /\\^3/,\n transform: () => '³',\n})\n\n/**\n * @public\n */\nexport const oneQuarterRule = defineTextTransformRule({\n on: /(?:^|\\s)(1\\/4)\\s/,\n transform: () => '¼',\n})\n\n/**\n * @public\n */\nexport const threeQuartersRule = defineTextTransformRule({\n on: /(?:^|\\s)(3\\/4)\\s/,\n transform: () => '¾',\n})\n","import {\n InputRulePlugin,\n type InputRule,\n type InputRuleGuard,\n} from '@portabletext/plugin-input-rule'\nimport {useMemo} from 'react'\nimport {\n closingDoubleQuoteRule,\n closingSingleQuoteRule,\n copyrightRule,\n ellipsisRule,\n emDashRule,\n laquoRule,\n leftArrowRule,\n multiplicationRule,\n notEqualRule,\n oneHalfRule,\n oneQuarterRule,\n openingDoubleQuoteRule,\n openingSingleQuoteRule,\n plusMinusRule,\n raquoRule,\n registeredTrademarkRule,\n rightArrowRule,\n servicemarkRule,\n superscriptThreeRule,\n superscriptTwoRule,\n threeQuartersRule,\n trademarkRule,\n} from './input-rules.typography'\n\n/**\n * @public\n */\nexport type TypographyPluginProps = {\n guard?: InputRuleGuard\n /**\n * Configure which rules to enable or disable. Ordinary rules like `emDash` and `ellipsis` are enabled by default.\n * Less common rules like `multiplication` are disabled by default.\n */\n rules?: {\n /**\n * @defaultValue 'on'\n */\n emDash?: 'on' | 'off'\n /**\n * @defaultValue 'on'\n */\n ellipsis?: 'on' | 'off'\n /**\n * @defaultValue 'on'\n */\n openingDoubleQuote?: 'on' | 'off'\n /**\n * @defaultValue 'on'\n */\n closingDoubleQuote?: 'on' | 'off'\n /**\n * @defaultValue 'on'\n */\n openingSingleQuote?: 'on' | 'off'\n /**\n * @defaultValue 'on'\n */\n closingSingleQuote?: 'on' | 'off'\n /**\n * @defaultValue 'on'\n */\n leftArrow?: 'on' | 'off'\n /**\n * @defaultValue 'on'\n */\n rightArrow?: 'on' | 'off'\n /**\n * @defaultValue 'on'\n */\n copyright?: 'on' | 'off'\n /**\n * @defaultValue 'on'\n */\n trademark?: 'on' | 'off'\n /**\n * @defaultValue 'on'\n */\n servicemark?: 'on' | 'off'\n /**\n * @defaultValue 'on'\n */\n registeredTrademark?: 'on' | 'off'\n /**\n * @defaultValue 'off'\n */\n oneHalf?: 'on' | 'off'\n /**\n * @defaultValue 'off'\n */\n plusMinus?: 'on' | 'off'\n /**\n * @defaultValue 'off'\n */\n notEqual?: 'on' | 'off'\n /**\n * @defaultValue 'off'\n */\n laquo?: 'on' | 'off'\n /**\n * @defaultValue 'off'\n */\n raquo?: 'on' | 'off'\n /**\n * @defaultValue 'off'\n */\n multiplication?: 'on' | 'off'\n /**\n * @defaultValue 'off'\n */\n superscriptTwo?: 'on' | 'off'\n /**\n * @defaultValue 'off'\n */\n superscriptThree?: 'on' | 'off'\n /**\n * @defaultValue 'off'\n */\n oneQuarter?: 'on' | 'off'\n /**\n * @defaultValue 'off'\n */\n threeQuarters?: 'on' | 'off'\n }\n}\n\ntype RuleName = keyof NonNullable<TypographyPluginProps['rules']>\n\nconst defaultRuleConfig: Array<{\n name: RuleName\n rule: InputRule\n state: 'on' | 'off'\n}> = [\n {name: 'emDash', rule: emDashRule, state: 'on'},\n {name: 'ellipsis', rule: ellipsisRule, state: 'on'},\n {name: 'openingDoubleQuote', rule: openingDoubleQuoteRule, state: 'on'},\n {name: 'closingDoubleQuote', rule: closingDoubleQuoteRule, state: 'on'},\n {name: 'openingSingleQuote', rule: openingSingleQuoteRule, state: 'on'},\n {name: 'closingSingleQuote', rule: closingSingleQuoteRule, state: 'on'},\n {name: 'leftArrow', rule: leftArrowRule, state: 'on'},\n {name: 'rightArrow', rule: rightArrowRule, state: 'on'},\n {name: 'copyright', rule: copyrightRule, state: 'on'},\n {name: 'trademark', rule: trademarkRule, state: 'on'},\n {name: 'servicemark', rule: servicemarkRule, state: 'on'},\n {name: 'registeredTrademark', rule: registeredTrademarkRule, state: 'on'},\n {name: 'oneHalf', rule: oneHalfRule, state: 'off'},\n {name: 'plusMinus', rule: plusMinusRule, state: 'off'},\n {name: 'laquo', rule: laquoRule, state: 'off'},\n {name: 'notEqual', rule: notEqualRule, state: 'off'},\n {name: 'raquo', rule: raquoRule, state: 'off'},\n {name: 'multiplication', rule: multiplicationRule, state: 'off'},\n {name: 'superscriptTwo', rule: superscriptTwoRule, state: 'off'},\n {name: 'superscriptThree', rule: superscriptThreeRule, state: 'off'},\n {name: 'oneQuarter', rule: oneQuarterRule, state: 'off'},\n {name: 'threeQuarters', rule: threeQuartersRule, state: 'off'},\n]\n\n/**\n * @public\n */\nexport function TypographyPlugin(props: TypographyPluginProps) {\n const configuredInputRules = useMemo(\n () =>\n defaultRuleConfig.flatMap((rule) =>\n props.rules && props.rules[rule.name] === 'on'\n ? {...rule.rule, guard: props.guard ?? (() => true)}\n : (props.rules && props.rules[rule.name] === 'off') ||\n rule.state === 'off'\n ? []\n : {...rule.rule, guard: props.guard ?? (() => true)},\n ),\n [props.guard, props.rules],\n )\n\n return <InputRulePlugin {...props} rules={configuredInputRules} />\n}\n"],"names":["createDecoratorGuard","config","snapshot","event","decorators","schema","context","length","matchedSpans","matches","flatMap","match","getSelectedSpans","selection","preventInputRule","decorator","isActiveDecorator","some","span","node","marks","includes","emDashRule","defineTextTransformRule","on","transform","ellipsisRule","openingDoubleQuoteRule","closingDoubleQuoteRule","openingSingleQuoteRule","closingSingleQuoteRule","smartQuotesRules","leftArrowRule","rightArrowRule","copyrightRule","servicemarkRule","trademarkRule","registeredTrademarkRule","oneHalfRule","plusMinusRule","notEqualRule","laquoRule","raquoRule","multiplicationRule","superscriptTwoRule","superscriptThreeRule","oneQuarterRule","threeQuartersRule","defaultRuleConfig","name","rule","state","TypographyPlugin","props","$","_c","t0","guard","rules","_temp","_temp2","configuredInputRules","t1"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../src/create-decorator-guard.ts","../src/input-rules.typography.ts","../src/plugin.typography.tsx"],"sourcesContent":["import type {EditorContext, EditorSchema} from '@portabletext/editor'\nimport {\n getSelectedSpans,\n isActiveDecorator,\n} from '@portabletext/editor/selectors'\nimport type {InputRuleGuard} from '@portabletext/plugin-input-rule'\n\n/**\n * @public\n * Create an `InputRuleGuard` that can prevent the rule from running inside\n * certain decorators.\n *\n * @example\n * ```tsx\n * const guard = createDecoratorGuard({\n * decorators: ({context}) => context.schema.decorators.flatMap((decorator) => decorator.name === 'code' ? [] : [decorator.name]),\n * })\n *\n * <TypographyPlugin guard={guard} />\n * ```\n */\nexport function createDecoratorGuard(config: {\n decorators: ({\n context,\n }: {\n context: Pick<EditorContext, 'schema'>\n }) => Array<EditorSchema['decorators'][number]['name']>\n}): InputRuleGuard {\n return ({snapshot, event}) => {\n const allowedDecorators = config.decorators({\n context: {\n schema: snapshot.context.schema,\n },\n })\n const decorators = snapshot.context.schema.decorators.flatMap(\n (decorator) =>\n allowedDecorators.includes(decorator.name) ? [] : [decorator.name],\n )\n\n if (decorators.length === 0) {\n return true\n }\n\n const matchedSpans = event.matches.flatMap((match) =>\n getSelectedSpans({\n ...snapshot,\n context: {\n ...snapshot.context,\n selection: match.selection,\n },\n }),\n )\n\n let preventInputRule = false\n\n for (const decorator of decorators) {\n if (isActiveDecorator(decorator)(snapshot)) {\n preventInputRule = true\n break\n }\n\n if (matchedSpans.some((span) => span.node.marks?.includes(decorator))) {\n preventInputRule = true\n break\n }\n }\n\n return !preventInputRule\n }\n}\n","import {\n defineTextTransformRule,\n type InputRule,\n} from '@portabletext/plugin-input-rule'\n\n/**\n * @public\n */\nexport const emDashRule = defineTextTransformRule({\n on: /--/,\n transform: () => '—',\n})\n\n/**\n * @public\n */\nexport const ellipsisRule = defineTextTransformRule({\n on: /\\.\\.\\./,\n transform: () => '…',\n})\n\n/**\n * @public\n */\nexport const openingDoubleQuoteRule = defineTextTransformRule({\n on: /(?:^|(?<=[\\s{[(<'\"\\u2018\\u201C]))\"/g,\n transform: () => '“',\n})\n\n/**\n * @public\n */\nexport const closingDoubleQuoteRule = defineTextTransformRule({\n on: /\"/g,\n transform: () => '”',\n})\n\n/**\n * @public\n */\nexport const openingSingleQuoteRule = defineTextTransformRule({\n on: /(?:^|(?<=[\\s{[(<'\"\\u2018\\u201C]))'/g,\n transform: () => '‘',\n})\n\n/**\n * @public\n */\nexport const closingSingleQuoteRule = defineTextTransformRule({\n on: /'/g,\n transform: () => '’',\n})\n\n/**\n * @public\n */\nexport const smartQuotesRules: Array<InputRule> = [\n openingDoubleQuoteRule,\n closingDoubleQuoteRule,\n openingSingleQuoteRule,\n closingSingleQuoteRule,\n]\n\n/**\n * @public\n */\nexport const leftArrowRule = defineTextTransformRule({\n on: /<-/,\n transform: () => '←',\n})\n\n/**\n * @public\n */\nexport const rightArrowRule = defineTextTransformRule({\n on: /->/,\n transform: () => '→',\n})\n\n/**\n * @public\n */\nexport const copyrightRule = defineTextTransformRule({\n on: /\\(c\\)/,\n transform: () => '©',\n})\n\n/**\n * @public\n */\nexport const servicemarkRule = defineTextTransformRule({\n on: /\\(sm\\)/,\n transform: () => '℠',\n})\n\n/**\n * @public\n */\nexport const trademarkRule = defineTextTransformRule({\n on: /\\(tm\\)/,\n transform: () => '™',\n})\n\n/**\n * @beta\n */\nexport const registeredTrademarkRule = defineTextTransformRule({\n on: /\\(r\\)/,\n transform: () => '®',\n})\n\n/**\n * @public\n */\nexport const oneHalfRule = defineTextTransformRule({\n on: /(?:^|\\s)(1\\/2)\\s/,\n transform: () => '½',\n})\n\n/**\n * @public\n */\nexport const plusMinusRule = defineTextTransformRule({\n on: /\\+\\/-/,\n transform: () => '±',\n})\n\n/**\n * @public\n */\nexport const notEqualRule = defineTextTransformRule({\n on: /!=/,\n transform: () => '≠',\n})\n\n/**\n * @public\n */\nexport const laquoRule = defineTextTransformRule({\n on: /<</,\n transform: () => '«',\n})\n\n/**\n * @public\n */\nexport const raquoRule = defineTextTransformRule({\n on: />>/,\n transform: () => '»',\n})\n\n/**\n * @public\n */\nexport const multiplicationRule = defineTextTransformRule({\n on: /\\d+\\s?([*x])\\s?\\d+/,\n transform: () => '×',\n})\n\n/**\n * @public\n */\nexport const superscriptTwoRule = defineTextTransformRule({\n on: /\\^2/,\n transform: () => '²',\n})\n\n/**\n * @public\n */\nexport const superscriptThreeRule = defineTextTransformRule({\n on: /\\^3/,\n transform: () => '³',\n})\n\n/**\n * @public\n */\nexport const oneQuarterRule = defineTextTransformRule({\n on: /(?:^|\\s)(1\\/4)\\s/,\n transform: () => '¼',\n})\n\n/**\n * @public\n */\nexport const threeQuartersRule = defineTextTransformRule({\n on: /(?:^|\\s)(3\\/4)\\s/,\n transform: () => '¾',\n})\n","import {\n InputRulePlugin,\n type InputRuleGuard,\n} from '@portabletext/plugin-input-rule'\nimport {useMemo} from 'react'\nimport {\n closingDoubleQuoteRule,\n closingSingleQuoteRule,\n copyrightRule,\n ellipsisRule,\n emDashRule,\n laquoRule,\n leftArrowRule,\n multiplicationRule,\n notEqualRule,\n oneHalfRule,\n oneQuarterRule,\n openingDoubleQuoteRule,\n openingSingleQuoteRule,\n plusMinusRule,\n raquoRule,\n registeredTrademarkRule,\n rightArrowRule,\n servicemarkRule,\n superscriptThreeRule,\n superscriptTwoRule,\n threeQuartersRule,\n trademarkRule,\n} from './input-rules.typography'\n\nconst defaultRuleConfig = [\n {name: 'emDash', rule: emDashRule, state: 'on'},\n {name: 'ellipsis', rule: ellipsisRule, state: 'on'},\n {name: 'openingDoubleQuote', rule: openingDoubleQuoteRule, state: 'on'},\n {name: 'closingDoubleQuote', rule: closingDoubleQuoteRule, state: 'on'},\n {name: 'openingSingleQuote', rule: openingSingleQuoteRule, state: 'on'},\n {name: 'closingSingleQuote', rule: closingSingleQuoteRule, state: 'on'},\n {name: 'leftArrow', rule: leftArrowRule, state: 'on'},\n {name: 'rightArrow', rule: rightArrowRule, state: 'on'},\n {name: 'copyright', rule: copyrightRule, state: 'on'},\n {name: 'trademark', rule: trademarkRule, state: 'on'},\n {name: 'servicemark', rule: servicemarkRule, state: 'on'},\n {name: 'registeredTrademark', rule: registeredTrademarkRule, state: 'on'},\n {name: 'oneHalf', rule: oneHalfRule, state: 'off'},\n {name: 'plusMinus', rule: plusMinusRule, state: 'off'},\n {name: 'laquo', rule: laquoRule, state: 'off'},\n {name: 'notEqual', rule: notEqualRule, state: 'off'},\n {name: 'raquo', rule: raquoRule, state: 'off'},\n {name: 'multiplication', rule: multiplicationRule, state: 'off'},\n {name: 'superscriptTwo', rule: superscriptTwoRule, state: 'off'},\n {name: 'superscriptThree', rule: superscriptThreeRule, state: 'off'},\n {name: 'oneQuarter', rule: oneQuarterRule, state: 'off'},\n {name: 'threeQuarters', rule: threeQuartersRule, state: 'off'},\n] as const\n\ntype RuleName = (typeof defaultRuleConfig)[number]['name']\n\n/**\n * @public\n */\nexport type TypographyPluginProps<\n TEnabledRuleName extends RuleName = never,\n TDisabledRuleName extends Exclude<RuleName, TEnabledRuleName> = never,\n> = {\n guard?: InputRuleGuard\n /**\n * Preset configuration for rules.\n * - `'default'`: Common typography rules enabled (em dash, ellipsis, quotes, arrows, copyright symbols)\n * - `'all'`: All rules enabled\n * - `'none'`: No rules enabled (use with `enable` prop)\n *\n * @defaultValue 'default'\n */\n preset?: 'default' | 'all' | 'none'\n /**\n * Enable specific rules (additive to preset).\n * Use this to enable additional rules beyond the preset.\n *\n * @example\n * ```tsx\n * // Enable multiplication and plusMinus in addition to default rules\n * <TypographyPlugin enable={['multiplication', 'plusMinus']} />\n * ```\n */\n enable?: ReadonlyArray<TEnabledRuleName>\n /**\n * Disable specific rules (subtractive from preset).\n * Use this to disable rules that would otherwise be enabled by the preset.\n * Cannot contain rules that are in the `enable` array (TypeScript will enforce this).\n *\n * @example\n * ```tsx\n * // Disable em dash from the default rules\n * <TypographyPlugin disable={['emDash']} />\n * ```\n */\n disable?: ReadonlyArray<TDisabledRuleName>\n}\n\n/**\n * @public\n */\nexport function TypographyPlugin<\n TEnabledRuleName extends RuleName = never,\n TDisabledRuleName extends Exclude<RuleName, TEnabledRuleName> = never,\n>(props: TypographyPluginProps<TEnabledRuleName, TDisabledRuleName>) {\n const {preset = 'default', enable = [], disable = [], guard} = props\n\n const configuredInputRules = useMemo(() => {\n // Determine which rules should be enabled based on preset\n const enabledRules = new Set<RuleName>()\n\n if (preset === 'all') {\n // Enable all rules\n for (const rule of defaultRuleConfig) {\n enabledRules.add(rule.name)\n }\n } else if (preset === 'default') {\n // Enable only default rules (state: 'on')\n for (const rule of defaultRuleConfig) {\n if (rule.state === 'on') {\n enabledRules.add(rule.name)\n }\n }\n }\n // preset === 'none' starts with empty set\n\n // Apply enable list (additive)\n for (const ruleName of enable) {\n enabledRules.add(ruleName)\n }\n\n // Apply disable list (subtractive)\n for (const ruleName of disable) {\n enabledRules.delete(ruleName)\n }\n\n // Build final rule list\n return defaultRuleConfig.flatMap((rule) =>\n enabledRules.has(rule.name)\n ? [{...rule.rule, guard: guard ?? (() => true)}]\n : [],\n )\n }, [preset, enable, disable, guard])\n\n return <InputRulePlugin rules={configuredInputRules} />\n}\n"],"names":["createDecoratorGuard","config","snapshot","event","allowedDecorators","decorators","context","schema","flatMap","decorator","includes","name","length","matchedSpans","matches","match","getSelectedSpans","selection","preventInputRule","isActiveDecorator","some","span","node","marks","emDashRule","defineTextTransformRule","on","transform","ellipsisRule","openingDoubleQuoteRule","closingDoubleQuoteRule","openingSingleQuoteRule","closingSingleQuoteRule","smartQuotesRules","leftArrowRule","rightArrowRule","copyrightRule","servicemarkRule","trademarkRule","registeredTrademarkRule","oneHalfRule","plusMinusRule","notEqualRule","laquoRule","raquoRule","multiplicationRule","superscriptTwoRule","superscriptThreeRule","oneQuarterRule","threeQuartersRule","defaultRuleConfig","rule","state","TypographyPlugin","props","$","_c","preset","t0","enable","t1","disable","t2","guard","undefined","t3","t4","enabledRules","Set","add","rule_0","ruleName","ruleName_0","delete","t5","rule_1","has","_temp","configuredInputRules","t6"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@portabletext/plugin-typography",
3
- "version": "2.0.3",
3
+ "version": "3.0.1",
4
4
  "description": "Automatically transform text to typographic variants",
5
5
  "keywords": [
6
6
  "portabletext",
@@ -35,7 +35,7 @@
35
35
  ],
36
36
  "dependencies": {
37
37
  "react-compiler-runtime": "1.0.0",
38
- "@portabletext/plugin-input-rule": "~0.5.3"
38
+ "@portabletext/plugin-input-rule": "~0.5.4"
39
39
  },
40
40
  "devDependencies": {
41
41
  "@sanity/pkg-utils": "^9.0.1",
@@ -51,13 +51,13 @@
51
51
  "typescript": "5.9.3",
52
52
  "typescript-eslint": "^8.46.1",
53
53
  "vitest": "^4.0.6",
54
- "@portabletext/editor": "^2.19.1",
54
+ "@portabletext/editor": "^2.19.2",
55
55
  "@portabletext/schema": "^2.0.0",
56
56
  "racejar": "2.0.0"
57
57
  },
58
58
  "peerDependencies": {
59
- "@portabletext/editor": "^2.19.1",
60
- "react": "^19.1.1"
59
+ "@portabletext/editor": "^2.19.2",
60
+ "react": "^18.3 || ^19"
61
61
  },
62
62
  "engines": {
63
63
  "node": ">=20.19 <22 || >=22.12"
@@ -1,4 +1,4 @@
1
- import type {EditorSchema} from '@portabletext/editor'
1
+ import type {EditorContext, EditorSchema} from '@portabletext/editor'
2
2
  import {
3
3
  getSelectedSpans,
4
4
  isActiveDecorator,
@@ -13,17 +13,29 @@ import type {InputRuleGuard} from '@portabletext/plugin-input-rule'
13
13
  * @example
14
14
  * ```tsx
15
15
  * const guard = createDecoratorGuard({
16
- * decorators: ({schema}) => schema.decorators.flatMap((decorator) => decorator.name === 'code' ? [decorator.name] : []),
16
+ * decorators: ({context}) => context.schema.decorators.flatMap((decorator) => decorator.name === 'code' ? [] : [decorator.name]),
17
17
  * })
18
18
  *
19
19
  * <TypographyPlugin guard={guard} />
20
20
  * ```
21
21
  */
22
22
  export function createDecoratorGuard(config: {
23
- decorators: ({schema}: {schema: EditorSchema}) => Array<string>
23
+ decorators: ({
24
+ context,
25
+ }: {
26
+ context: Pick<EditorContext, 'schema'>
27
+ }) => Array<EditorSchema['decorators'][number]['name']>
24
28
  }): InputRuleGuard {
25
29
  return ({snapshot, event}) => {
26
- const decorators = config.decorators({schema: snapshot.context.schema})
30
+ const allowedDecorators = config.decorators({
31
+ context: {
32
+ schema: snapshot.context.schema,
33
+ },
34
+ })
35
+ const decorators = snapshot.context.schema.decorators.flatMap(
36
+ (decorator) =>
37
+ allowedDecorators.includes(decorator.name) ? [] : [decorator.name],
38
+ )
27
39
 
28
40
  if (decorators.length === 0) {
29
41
  return true
@@ -41,14 +41,17 @@ Feature: Disallow in code
41
41
 
42
42
  Scenario Outline: Decorated and annotated text
43
43
  Given the text "foo bar baz"
44
- And "code" around "bar"
44
+ And <decorator> around "bar"
45
45
  And a "link" "l1" around "bar"
46
46
  When the caret is put <position>
47
47
  And "->" is typed
48
48
  Then the text is <new text>
49
49
 
50
50
  Examples:
51
- | position | new text |
52
- | after "bar" | "foo ,bar,→ baz" |
53
- | before "bar" | "foo →,bar, baz" |
54
- | before "ar baz" | "foo ,b->ar, baz" |
51
+ | decorator | position | new text |
52
+ | "code" | after "bar" | "foo ,bar,→ baz" |
53
+ | "strong" | after "bar" | "foo ,bar,→ baz" |
54
+ | "code" | before "bar" | "foo →,bar, baz" |
55
+ | "strong" | before "bar" | "foo →,bar, baz" |
56
+ | "code" | before "ar baz" | "foo ,b->ar, baz" |
57
+ | "strong" | before "ar baz" | "foo ,b→ar, baz" |
@@ -12,9 +12,9 @@ import disallowInCodeFeature from './disallow-in-code.feature?raw'
12
12
  import {TypographyPlugin} from './plugin.typography'
13
13
 
14
14
  const codeGuard = createDecoratorGuard({
15
- decorators: ({schema}) =>
16
- schema.decorators.flatMap((decorator) =>
17
- decorator.name === 'code' ? [decorator.name] : [],
15
+ decorators: ({context}) =>
16
+ context.schema.decorators.flatMap((decorator) =>
17
+ decorator.name === 'code' ? [] : [decorator.name],
18
18
  ),
19
19
  })
20
20
 
@@ -23,7 +23,7 @@ Feature({
23
23
  Before(async (context: Context) => {
24
24
  const {editor, locator} = await createTestEditor({
25
25
  children: (
26
- <TypographyPlugin guard={codeGuard} rules={{multiplication: 'on'}} />
26
+ <TypographyPlugin guard={codeGuard} enable={['multiplication']} />
27
27
  ),
28
28
  schemaDefinition: defineSchema({
29
29
  decorators: [{name: 'strong'}, {name: 'code'}],
@@ -14,7 +14,7 @@ Feature({
14
14
  hooks: [
15
15
  Before(async (context: Context) => {
16
16
  const {editor, locator} = await createTestEditor({
17
- children: <TypographyPlugin rules={{multiplication: 'on'}} />,
17
+ children: <TypographyPlugin enable={['multiplication']} />,
18
18
  schemaDefinition: defineSchema({
19
19
  decorators: [{name: 'strong'}],
20
20
  annotations: [{name: 'link'}],
@@ -1,6 +1,5 @@
1
1
  import {
2
2
  InputRulePlugin,
3
- type InputRule,
4
3
  type InputRuleGuard,
5
4
  } from '@portabletext/plugin-input-rule'
6
5
  import {useMemo} from 'react'
@@ -29,114 +28,7 @@ import {
29
28
  trademarkRule,
30
29
  } from './input-rules.typography'
31
30
 
32
- /**
33
- * @public
34
- */
35
- export type TypographyPluginProps = {
36
- guard?: InputRuleGuard
37
- /**
38
- * Configure which rules to enable or disable. Ordinary rules like `emDash` and `ellipsis` are enabled by default.
39
- * Less common rules like `multiplication` are disabled by default.
40
- */
41
- rules?: {
42
- /**
43
- * @defaultValue 'on'
44
- */
45
- emDash?: 'on' | 'off'
46
- /**
47
- * @defaultValue 'on'
48
- */
49
- ellipsis?: 'on' | 'off'
50
- /**
51
- * @defaultValue 'on'
52
- */
53
- openingDoubleQuote?: 'on' | 'off'
54
- /**
55
- * @defaultValue 'on'
56
- */
57
- closingDoubleQuote?: 'on' | 'off'
58
- /**
59
- * @defaultValue 'on'
60
- */
61
- openingSingleQuote?: 'on' | 'off'
62
- /**
63
- * @defaultValue 'on'
64
- */
65
- closingSingleQuote?: 'on' | 'off'
66
- /**
67
- * @defaultValue 'on'
68
- */
69
- leftArrow?: 'on' | 'off'
70
- /**
71
- * @defaultValue 'on'
72
- */
73
- rightArrow?: 'on' | 'off'
74
- /**
75
- * @defaultValue 'on'
76
- */
77
- copyright?: 'on' | 'off'
78
- /**
79
- * @defaultValue 'on'
80
- */
81
- trademark?: 'on' | 'off'
82
- /**
83
- * @defaultValue 'on'
84
- */
85
- servicemark?: 'on' | 'off'
86
- /**
87
- * @defaultValue 'on'
88
- */
89
- registeredTrademark?: 'on' | 'off'
90
- /**
91
- * @defaultValue 'off'
92
- */
93
- oneHalf?: 'on' | 'off'
94
- /**
95
- * @defaultValue 'off'
96
- */
97
- plusMinus?: 'on' | 'off'
98
- /**
99
- * @defaultValue 'off'
100
- */
101
- notEqual?: 'on' | 'off'
102
- /**
103
- * @defaultValue 'off'
104
- */
105
- laquo?: 'on' | 'off'
106
- /**
107
- * @defaultValue 'off'
108
- */
109
- raquo?: 'on' | 'off'
110
- /**
111
- * @defaultValue 'off'
112
- */
113
- multiplication?: 'on' | 'off'
114
- /**
115
- * @defaultValue 'off'
116
- */
117
- superscriptTwo?: 'on' | 'off'
118
- /**
119
- * @defaultValue 'off'
120
- */
121
- superscriptThree?: 'on' | 'off'
122
- /**
123
- * @defaultValue 'off'
124
- */
125
- oneQuarter?: 'on' | 'off'
126
- /**
127
- * @defaultValue 'off'
128
- */
129
- threeQuarters?: 'on' | 'off'
130
- }
131
- }
132
-
133
- type RuleName = keyof NonNullable<TypographyPluginProps['rules']>
134
-
135
- const defaultRuleConfig: Array<{
136
- name: RuleName
137
- rule: InputRule
138
- state: 'on' | 'off'
139
- }> = [
31
+ const defaultRuleConfig = [
140
32
  {name: 'emDash', rule: emDashRule, state: 'on'},
141
33
  {name: 'ellipsis', rule: ellipsisRule, state: 'on'},
142
34
  {name: 'openingDoubleQuote', rule: openingDoubleQuoteRule, state: 'on'},
@@ -159,24 +51,97 @@ const defaultRuleConfig: Array<{
159
51
  {name: 'superscriptThree', rule: superscriptThreeRule, state: 'off'},
160
52
  {name: 'oneQuarter', rule: oneQuarterRule, state: 'off'},
161
53
  {name: 'threeQuarters', rule: threeQuartersRule, state: 'off'},
162
- ]
54
+ ] as const
55
+
56
+ type RuleName = (typeof defaultRuleConfig)[number]['name']
163
57
 
164
58
  /**
165
59
  * @public
166
60
  */
167
- export function TypographyPlugin(props: TypographyPluginProps) {
168
- const configuredInputRules = useMemo(
169
- () =>
170
- defaultRuleConfig.flatMap((rule) =>
171
- props.rules && props.rules[rule.name] === 'on'
172
- ? {...rule.rule, guard: props.guard ?? (() => true)}
173
- : (props.rules && props.rules[rule.name] === 'off') ||
174
- rule.state === 'off'
175
- ? []
176
- : {...rule.rule, guard: props.guard ?? (() => true)},
177
- ),
178
- [props.guard, props.rules],
179
- )
61
+ export type TypographyPluginProps<
62
+ TEnabledRuleName extends RuleName = never,
63
+ TDisabledRuleName extends Exclude<RuleName, TEnabledRuleName> = never,
64
+ > = {
65
+ guard?: InputRuleGuard
66
+ /**
67
+ * Preset configuration for rules.
68
+ * - `'default'`: Common typography rules enabled (em dash, ellipsis, quotes, arrows, copyright symbols)
69
+ * - `'all'`: All rules enabled
70
+ * - `'none'`: No rules enabled (use with `enable` prop)
71
+ *
72
+ * @defaultValue 'default'
73
+ */
74
+ preset?: 'default' | 'all' | 'none'
75
+ /**
76
+ * Enable specific rules (additive to preset).
77
+ * Use this to enable additional rules beyond the preset.
78
+ *
79
+ * @example
80
+ * ```tsx
81
+ * // Enable multiplication and plusMinus in addition to default rules
82
+ * <TypographyPlugin enable={['multiplication', 'plusMinus']} />
83
+ * ```
84
+ */
85
+ enable?: ReadonlyArray<TEnabledRuleName>
86
+ /**
87
+ * Disable specific rules (subtractive from preset).
88
+ * Use this to disable rules that would otherwise be enabled by the preset.
89
+ * Cannot contain rules that are in the `enable` array (TypeScript will enforce this).
90
+ *
91
+ * @example
92
+ * ```tsx
93
+ * // Disable em dash from the default rules
94
+ * <TypographyPlugin disable={['emDash']} />
95
+ * ```
96
+ */
97
+ disable?: ReadonlyArray<TDisabledRuleName>
98
+ }
99
+
100
+ /**
101
+ * @public
102
+ */
103
+ export function TypographyPlugin<
104
+ TEnabledRuleName extends RuleName = never,
105
+ TDisabledRuleName extends Exclude<RuleName, TEnabledRuleName> = never,
106
+ >(props: TypographyPluginProps<TEnabledRuleName, TDisabledRuleName>) {
107
+ const {preset = 'default', enable = [], disable = [], guard} = props
108
+
109
+ const configuredInputRules = useMemo(() => {
110
+ // Determine which rules should be enabled based on preset
111
+ const enabledRules = new Set<RuleName>()
112
+
113
+ if (preset === 'all') {
114
+ // Enable all rules
115
+ for (const rule of defaultRuleConfig) {
116
+ enabledRules.add(rule.name)
117
+ }
118
+ } else if (preset === 'default') {
119
+ // Enable only default rules (state: 'on')
120
+ for (const rule of defaultRuleConfig) {
121
+ if (rule.state === 'on') {
122
+ enabledRules.add(rule.name)
123
+ }
124
+ }
125
+ }
126
+ // preset === 'none' starts with empty set
127
+
128
+ // Apply enable list (additive)
129
+ for (const ruleName of enable) {
130
+ enabledRules.add(ruleName)
131
+ }
132
+
133
+ // Apply disable list (subtractive)
134
+ for (const ruleName of disable) {
135
+ enabledRules.delete(ruleName)
136
+ }
137
+
138
+ // Build final rule list
139
+ return defaultRuleConfig.flatMap((rule) =>
140
+ enabledRules.has(rule.name)
141
+ ? [{...rule.rule, guard: guard ?? (() => true)}]
142
+ : [],
143
+ )
144
+ }, [preset, enable, disable, guard])
180
145
 
181
- return <InputRulePlugin {...props} rules={configuredInputRules} />
146
+ return <InputRulePlugin rules={configuredInputRules} />
182
147
  }