@portabletext/plugin-typography 4.0.23 → 4.0.24

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@portabletext/plugin-typography",
3
- "version": "4.0.23",
3
+ "version": "4.0.24",
4
4
  "description": "Automatically transform text to typographic variants",
5
5
  "keywords": [
6
6
  "portabletext",
@@ -30,12 +30,11 @@
30
30
  "main": "./dist/index.js",
31
31
  "types": "./dist/index.d.ts",
32
32
  "files": [
33
- "dist",
34
- "src"
33
+ "dist"
35
34
  ],
36
35
  "dependencies": {
37
36
  "react-compiler-runtime": "^1.0.0",
38
- "@portabletext/plugin-input-rule": "^1.0.23"
37
+ "@portabletext/plugin-input-rule": "^1.0.24"
39
38
  },
40
39
  "devDependencies": {
41
40
  "@sanity/pkg-utils": "^10.1.1",
@@ -52,12 +51,12 @@
52
51
  "typescript": "5.9.3",
53
52
  "typescript-eslint": "^8.48.0",
54
53
  "vitest": "^4.0.14",
55
- "@portabletext/editor": "^3.3.3",
56
- "@portabletext/schema": "^2.0.0",
57
- "racejar": "2.0.0"
54
+ "@portabletext/editor": "^3.3.4",
55
+ "@portabletext/schema": "^2.0.1",
56
+ "racejar": "2.0.1"
58
57
  },
59
58
  "peerDependencies": {
60
- "@portabletext/editor": "^3.3.3",
59
+ "@portabletext/editor": "^3.3.4",
61
60
  "react": "^18.3 || ^19"
62
61
  },
63
62
  "engines": {
@@ -1,70 +0,0 @@
1
- import type {EditorContext, EditorSchema} from '@portabletext/editor'
2
- import {
3
- getSelectedSpans,
4
- isActiveDecorator,
5
- } from '@portabletext/editor/selectors'
6
- import type {InputRuleGuard} from '@portabletext/plugin-input-rule'
7
-
8
- /**
9
- * @public
10
- * Create an `InputRuleGuard` that can prevent the rule from running inside
11
- * certain decorators.
12
- *
13
- * @example
14
- * ```tsx
15
- * const guard = createDecoratorGuard({
16
- * decorators: ({context}) => context.schema.decorators.flatMap((decorator) => decorator.name === 'code' ? [] : [decorator.name]),
17
- * })
18
- *
19
- * <TypographyPlugin guard={guard} />
20
- * ```
21
- */
22
- export function createDecoratorGuard(config: {
23
- decorators: ({
24
- context,
25
- }: {
26
- context: Pick<EditorContext, 'schema'>
27
- }) => Array<EditorSchema['decorators'][number]['name']>
28
- }): InputRuleGuard {
29
- return ({snapshot, event}) => {
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
- )
39
-
40
- if (decorators.length === 0) {
41
- return true
42
- }
43
-
44
- const matchedSpans = event.matches.flatMap((match) =>
45
- getSelectedSpans({
46
- ...snapshot,
47
- context: {
48
- ...snapshot.context,
49
- selection: match.selection,
50
- },
51
- }),
52
- )
53
-
54
- let preventInputRule = false
55
-
56
- for (const decorator of decorators) {
57
- if (isActiveDecorator(decorator)(snapshot)) {
58
- preventInputRule = true
59
- break
60
- }
61
-
62
- if (matchedSpans.some((span) => span.node.marks?.includes(decorator))) {
63
- preventInputRule = true
64
- break
65
- }
66
- }
67
-
68
- return !preventInputRule
69
- }
70
- }
@@ -1,53 +0,0 @@
1
- Feature: Disallow in code
2
-
3
- Scenario Outline: Decorated text
4
- Given the text <text>
5
- And "code" around <decorated>
6
- When the caret is put <position>
7
- And "->" is typed
8
- Then the text is <new text>
9
-
10
- Examples:
11
- | text | decorated | position | new text |
12
- | "foo" | "foo" | after "foo" | "foo->" |
13
- | "foo bar baz" | "bar" | after "bar" | "foo ,bar->, baz" |
14
- | "foo bar baz" | "bar" | before "bar" | "foo →,bar, baz" |
15
- | "foo bar baz" | "bar" | before "ar baz" | "foo ,b->ar, baz" |
16
-
17
- Scenario Outline: Partially decorated text
18
- Given the text "foo bar2x baz"
19
- And "code" around "bar2x"
20
- When the caret is put after "bar2x"
21
- And "2" is typed
22
- Then the text is "foo ,bar2x2, baz"
23
-
24
- Scenario Outline: Partially decorated text with decorator toggled off
25
- Given the text <text>
26
- And "code" around <decorated>
27
- When the caret is put <position>
28
- And "code" is toggled
29
- And <inserted text> is typed
30
- Then the text is <new text>
31
-
32
- Examples:
33
- | text | decorated | position | inserted text | new text |
34
- | "foo bar baz" | "bar" | after "bar" | "2x2" | "foo ,bar,2×2 baz" |
35
- | "foo bar2 baz" | "bar2" | after "bar2" | "x2" | "foo ,bar2,x2 baz" |
36
- | "foo bar2x baz" | "bar2x" | after "bar2x" | "2" | "foo ,bar2x,2 baz" |
37
-
38
- Scenario Outline: Decorated and annotated text
39
- Given the text "foo bar baz"
40
- And <decorator> around "bar"
41
- And a "link" "l1" around "bar"
42
- When the caret is put <position>
43
- And "->" is typed
44
- Then the text is <new text>
45
-
46
- Examples:
47
- | decorator | position | new text |
48
- | "code" | after "bar" | "foo ,bar,→ baz" |
49
- | "strong" | after "bar" | "foo ,bar,→ baz" |
50
- | "code" | before "bar" | "foo →,bar, baz" |
51
- | "strong" | before "bar" | "foo →,bar, baz" |
52
- | "code" | before "ar baz" | "foo ,b->ar, baz" |
53
- | "strong" | before "ar baz" | "foo ,b→ar, baz" |
@@ -1,41 +0,0 @@
1
- import {parameterTypes} from '@portabletext/editor/test'
2
- import {
3
- createTestEditor,
4
- stepDefinitions,
5
- type Context,
6
- } from '@portabletext/editor/test/vitest'
7
- import {defineSchema} from '@portabletext/schema'
8
- import {Before} from 'racejar'
9
- import {Feature} from 'racejar/vitest'
10
- import {createDecoratorGuard} from './create-decorator-guard'
11
- import disallowInCodeFeature from './disallow-in-code.feature?raw'
12
- import {TypographyPlugin} from './plugin.typography'
13
-
14
- const codeGuard = createDecoratorGuard({
15
- decorators: ({context}) =>
16
- context.schema.decorators.flatMap((decorator) =>
17
- decorator.name === 'code' ? [] : [decorator.name],
18
- ),
19
- })
20
-
21
- Feature({
22
- hooks: [
23
- Before(async (context: Context) => {
24
- const {editor, locator} = await createTestEditor({
25
- children: (
26
- <TypographyPlugin guard={codeGuard} enable={['multiplication']} />
27
- ),
28
- schemaDefinition: defineSchema({
29
- decorators: [{name: 'strong'}, {name: 'code'}],
30
- annotations: [{name: 'link'}],
31
- }),
32
- })
33
-
34
- context.locator = locator
35
- context.editor = editor
36
- }),
37
- ],
38
- featureText: disallowInCodeFeature,
39
- stepDefinitions,
40
- parameterTypes,
41
- })
package/src/global.d.ts DELETED
@@ -1,4 +0,0 @@
1
- declare module '*.feature?raw' {
2
- const content: string
3
- export default content
4
- }
package/src/index.ts DELETED
@@ -1,3 +0,0 @@
1
- export * from './create-decorator-guard'
2
- export * from './input-rules.typography'
3
- export * from './plugin.typography'
@@ -1,95 +0,0 @@
1
- Feature: Ellipsis Input Rule
2
-
3
- Background:
4
- Given a global keymap
5
-
6
- Scenario Outline: Inserting ellipsis in unformatted text
7
- Given the text <text>
8
- # The "When {string} is inserted" step inserts all characters at once to
9
- # mimic how insert.text behaves on Android
10
- When <inserted text> is inserted
11
- Then the text is <before undo>
12
- When undo is performed
13
- Then the text is <after undo>
14
-
15
- Examples:
16
- | text | inserted text | before undo | after undo |
17
- | ".." | "." | "…" | "..." |
18
- | "" | "..." | "…" | "..." |
19
- | "foo" | "..." | "foo…" | "foo..." |
20
- | "" | "foo..." | "foo…" | "foo..." |
21
- | "foo.." | ".bar" | "foo…bar" | "foo...bar" |
22
- | "" | "foo...bar..." | "foo…bar…" | "foo...bar..." |
23
- | "foo...bar..." | "baz..." | "foo...bar...baz…" | "foo...bar...baz..." |
24
-
25
- Scenario: Inserting ellipsis inside a decorator
26
- Given the text "foo.."
27
- And "strong" around "foo.."
28
- When the caret is put after "foo.."
29
- And "." is typed
30
- Then the text is "foo…"
31
- And "foo…" has marks "strong"
32
-
33
- Scenario: Inserting ellipsis at the edge of a decorator
34
- Given the text "foo.."
35
- And "strong" around "foo"
36
- When the caret is put after "foo.."
37
- And "." is typed
38
- Then the text is "foo,…"
39
- And "foo" has marks "strong"
40
- And "…" has no marks
41
-
42
- Scenario: Inserting ellipsis inside an annotation
43
- Given the text "foo.."
44
- And a "link" "l1" around "foo.."
45
- When the caret is put after "foo.."
46
- And "." is typed
47
- Then the text is "foo…"
48
- And "foo…" has marks "l1"
49
-
50
- Scenario: Inserting ellipsis at the edge of an annotation
51
- Given the text "foo.."
52
- And a "link" "l1" around "foo"
53
- When the caret is put after "foo.."
54
- And "." is typed
55
- Then the text is "foo,…"
56
- And "foo" has marks "l1"
57
- And "…" has no marks
58
-
59
- Scenario Outline: Smart undo with Backspace
60
- Given the text <text>
61
- When <inserted text> is inserted
62
- And "{Backspace}" is pressed
63
- Then the text is <new text>
64
-
65
- Examples:
66
- | text | inserted text | new text |
67
- | ".." | "." | "..." |
68
- | "" | "..." | "..." |
69
- | "foo" | "..." | "foo..." |
70
- | "" | "foo..." | "foo..." |
71
- | "foo.." | ".bar" | "foo...bar" |
72
- | "" | "foo...bar..." | "foo...bar..." |
73
- | "foo...bar..." | "baz..." | "foo...bar...baz..." |
74
-
75
- Scenario Outline: Smart undo aborted after text changes
76
- Given the text <text>
77
- When <inserted text> is inserted
78
- And <new text> is typed
79
- And "{Backspace}" is pressed
80
- Then the text is <final text>
81
-
82
- Examples:
83
- | text | inserted text | new text | final text |
84
- | "" | "..." | "n" | "…" |
85
-
86
- Scenario Outline: Smart undo aborted after selection changes
87
- Given the text <text>
88
- When <inserted text> is inserted
89
- And "{ArrowLeft}" is pressed
90
- And "{Backspace}" is pressed
91
- Then the text is <final text>
92
-
93
- Examples:
94
- | text | inserted text | final text |
95
- | "foo" | "..." | "fo…" |
@@ -1,31 +0,0 @@
1
- import {parameterTypes} from '@portabletext/editor/test'
2
- import {
3
- createTestEditor,
4
- stepDefinitions,
5
- type Context,
6
- } from '@portabletext/editor/test/vitest'
7
- import {defineSchema} from '@portabletext/schema'
8
- import {Before} from 'racejar'
9
- import {Feature} from 'racejar/vitest'
10
- import ellipsisFeature from './input-rule.ellipsis.feature?raw'
11
- import {TypographyPlugin} from './plugin.typography'
12
-
13
- Feature({
14
- hooks: [
15
- Before(async (context: Context) => {
16
- const {editor, locator} = await createTestEditor({
17
- children: <TypographyPlugin />,
18
- schemaDefinition: defineSchema({
19
- decorators: [{name: 'strong'}],
20
- annotations: [{name: 'link'}],
21
- }),
22
- })
23
-
24
- context.locator = locator
25
- context.editor = editor
26
- }),
27
- ],
28
- featureText: ellipsisFeature,
29
- stepDefinitions,
30
- parameterTypes,
31
- })
@@ -1,103 +0,0 @@
1
- Feature: Em Dash Input Rule
2
-
3
- Background:
4
- Given a global keymap
5
-
6
- Scenario Outline: Inserting em dash in unformatted text
7
- Given the text <text>
8
- # The "When {string} is inserted" step inserts all characters at once to
9
- # mimic how insert.text behaves on Android
10
- When <inserted text> is inserted
11
- Then the text is <before undo>
12
- When undo is performed
13
- Then the text is <after undo>
14
-
15
- Examples:
16
- | text | inserted text | before undo | after undo |
17
- | "-" | "-" | "—" | "--" |
18
- | "" | "--" | "—" | "--" |
19
- | "foo" | "--" | "foo—" | "foo--" |
20
- | "" | "foo--" | "foo—" | "foo--" |
21
- | "foo-" | "-bar" | "foo—bar" | "foo--bar" |
22
- | "" | "foo--bar--" | "foo—bar—" | "foo--bar--" |
23
- | "foo--bar--" | "baz--" | "foo--bar--baz—" | "foo--bar--baz--" |
24
-
25
- Scenario: Inserting em dash inside a decorator
26
- Given the text "foo-"
27
- And "strong" around "foo-"
28
- When the caret is put after "foo-"
29
- And "-" is typed
30
- Then the text is "foo—"
31
- And "foo—" has marks "strong"
32
-
33
- Scenario: Inserting em dash at the edge of a decorator
34
- Given the text "foo-"
35
- And "strong" around "foo"
36
- When the caret is put after "foo-"
37
- And "-" is typed
38
- Then the text is "foo,—"
39
- And "foo" has marks "strong"
40
- And "—" has no marks
41
-
42
- Scenario: Inserting em dash inside an annotation
43
- Given the text "foo-"
44
- And a "link" "l1" around "foo-"
45
- When the caret is put after "foo-"
46
- And "-" is typed
47
- Then the text is "foo—"
48
- And "foo—" has marks "l1"
49
-
50
- Scenario: Inserting em dash halfway inside an annotation
51
- Given the text "foo-"
52
- And a "link" "l1" around "foo-"
53
- When the caret is put after "foo-"
54
- And "-" is typed
55
- Then the text is "foo—"
56
- And "foo—" has marks "l1"
57
-
58
- Scenario: Inserting em dash at the edge of an annotation
59
- Given the text "foo-"
60
- And a "link" "l1" around "foo"
61
- When the caret is put after "foo-"
62
- And "-" is typed
63
- Then the text is "foo,—"
64
- And "foo" has marks "l1"
65
- And "—" has no marks
66
-
67
- Scenario Outline: Smart undo with Backspace
68
- Given the text <text>
69
- When <inserted text> is inserted
70
- And "{Backspace}" is pressed
71
- Then the text is <new text>
72
-
73
- Examples:
74
- | text | inserted text | new text |
75
- | "-" | "-" | "--" |
76
- | "" | "--" | "--" |
77
- | "foo" | "--" | "foo--" |
78
- | "" | "foo--" | "foo--" |
79
- | "foo-" | "-bar" | "foo--bar" |
80
- | "" | "foo--bar--" | "foo--bar--" |
81
- | "foo--bar--" | "baz--" | "foo--bar--baz--" |
82
-
83
- Scenario Outline: Smart undo aborted after text changes
84
- Given the text <text>
85
- When <inserted text> is inserted
86
- And <new text> is typed
87
- And "{Backspace}" is pressed
88
- Then the text is <final text>
89
-
90
- Examples:
91
- | text | inserted text | new text | final text |
92
- | "" | "--" | "n" | "—" |
93
-
94
- Scenario Outline: Smart undo aborted after selection changes
95
- Given the text <text>
96
- When <inserted text> is inserted
97
- And "{ArrowLeft}" is pressed
98
- And "{Backspace}" is pressed
99
- Then the text is <final text>
100
-
101
- Examples:
102
- | text | inserted text | final text |
103
- | "foo" | "--" | "fo—" |
@@ -1,31 +0,0 @@
1
- import {parameterTypes} from '@portabletext/editor/test'
2
- import {
3
- createTestEditor,
4
- stepDefinitions,
5
- type Context,
6
- } from '@portabletext/editor/test/vitest'
7
- import {defineSchema} from '@portabletext/schema'
8
- import {Before} from 'racejar'
9
- import {Feature} from 'racejar/vitest'
10
- import emDashFeature from './input-rule.em-dash.feature?raw'
11
- import {TypographyPlugin} from './plugin.typography'
12
-
13
- Feature({
14
- hooks: [
15
- Before(async (context: Context) => {
16
- const {editor, locator} = await createTestEditor({
17
- children: <TypographyPlugin />,
18
- schemaDefinition: defineSchema({
19
- decorators: [{name: 'strong'}],
20
- annotations: [{name: 'link'}],
21
- }),
22
- })
23
-
24
- context.locator = locator
25
- context.editor = editor
26
- }),
27
- ],
28
- featureText: emDashFeature,
29
- stepDefinitions,
30
- parameterTypes,
31
- })
@@ -1,22 +0,0 @@
1
- Feature: Multiplication Input Rule
2
-
3
- Scenario Outline: Inserting multiplication sign
4
- Given the text <text>
5
- When <inserted text> is inserted
6
- Then the text is <new text>
7
-
8
- Examples:
9
- | text | inserted text | new text |
10
- | "" | "1*2" | "1×2" |
11
- | "" | "1*2*3" | "1×2×3" |
12
-
13
- Scenario Outline: Inserting multiplication sign and writing afterwards
14
- Given the text <text>
15
- When <inserted text> is inserted
16
- And "new" is typed
17
- Then the text is <new text>
18
-
19
- Examples:
20
- | text | inserted text | new text |
21
- | "" | "1*2" | "1×2new" |
22
- | "" | "1*2*3" | "1×2×3new" |
@@ -1,31 +0,0 @@
1
- import {parameterTypes} from '@portabletext/editor/test'
2
- import {
3
- createTestEditor,
4
- stepDefinitions,
5
- type Context,
6
- } from '@portabletext/editor/test/vitest'
7
- import {defineSchema} from '@portabletext/schema'
8
- import {Before} from 'racejar'
9
- import {Feature} from 'racejar/vitest'
10
- import multiplicationFeature from './input-rule.multiplication.feature?raw'
11
- import {TypographyPlugin} from './plugin.typography'
12
-
13
- Feature({
14
- hooks: [
15
- Before(async (context: Context) => {
16
- const {editor, locator} = await createTestEditor({
17
- children: <TypographyPlugin enable={['multiplication']} />,
18
- schemaDefinition: defineSchema({
19
- decorators: [{name: 'strong'}],
20
- annotations: [{name: 'link'}],
21
- }),
22
- })
23
-
24
- context.locator = locator
25
- context.editor = editor
26
- }),
27
- ],
28
- featureText: multiplicationFeature,
29
- stepDefinitions,
30
- parameterTypes,
31
- })
@@ -1,72 +0,0 @@
1
- Feature: Smart Quotes Input Rule
2
-
3
- Background:
4
- Given a global keymap
5
-
6
- Scenario Outline: Typing turns double quotes into smart quotes
7
- Given the text <text>
8
- When <inserted text> is typed
9
- Then the text is <new text>
10
-
11
- Examples:
12
- | text | inserted text | new text |
13
- | "" | "\"foo\"" | "“foo”" |
14
- | "“foo”" | " \"bar\"" | "“foo” “bar”" |
15
- | "" | "\"foo\" \"bar\"" | "“foo” “bar”" |
16
-
17
- Scenario Outline: Inserting double smart quotes in unformatted text
18
- Given the text <text>
19
- # The "When {string} is inserted" step inserts all characters at once to
20
- # mimic how insert.text behaves on Android
21
- When <inserted text> is inserted
22
- Then the text is <before undo>
23
- When undo is performed
24
- Then the text is <after undo>
25
-
26
- Examples:
27
- | text | inserted text | before undo | after undo |
28
- | "" | "\"" | "“" | """ |
29
- | "" | "\"\"" | "““" | """" |
30
- | "" | "\"\"\"" | "“““" | """"" |
31
- | "”" | "\"" | "””" | "”"" |
32
- # | "”" | "\"\"" | "”””" | "””"" |
33
- | "" | "\"foo\"" | "“foo”" | ""foo"" |
34
- | "“foo" | "\"" | "“foo”" | "“foo"" |
35
- | ""foo" | "\"" | ""foo”" | ""foo"" |
36
- | ""foo"" | "\"bar\"" | ""foo"“bar”" | ""foo""bar"" |
37
- | "“foo”" | "\"" | "“foo””" | "“foo”"" |
38
-
39
- # | "“foo”" | "\"\"" | "“foo”””" | "“foo”""" |
40
- Scenario Outline: Inserting single smart quotes in unformatted text
41
- Given the text <text>
42
- # The "When {string} is inserted" step inserts all characters at once to
43
- # mimic how insert.text behaves on Android
44
- When <inserted text> is inserted
45
- Then the text is <before undo>
46
- When undo is performed
47
- Then the text is <after undo>
48
-
49
- Examples:
50
- | text | inserted text | before undo | after undo |
51
- | "" | "'" | "‘" | "'" |
52
- | "" | "'foo'" | "‘foo’" | "'foo'" |
53
- | "‘foo" | "'" | "‘foo’" | "‘foo'" |
54
- | "'foo" | "'" | "'foo’" | "'foo'" |
55
- | "'foo'" | "'bar'" | "'foo'‘bar’" | "'foo''bar'" |
56
-
57
- Scenario: Mixed quotes
58
- Given the text ""
59
- When "\"'sorry' you say?\" she asked" is typed
60
- Then the text is "“‘sorry’ you say?” she asked"
61
-
62
- Scenario Outline: Contractions
63
- When <text> is typed
64
- Then the text is <new text>
65
-
66
- Examples:
67
- | text | new text |
68
- | "it's" | "it’s" |
69
- | "don't" | "don’t" |
70
- | "won't" | "won’t" |
71
- | "I'm" | "I’m" |
72
- | "you're" | "you’re" |
@@ -1,31 +0,0 @@
1
- import {parameterTypes} from '@portabletext/editor/test'
2
- import {
3
- createTestEditor,
4
- stepDefinitions,
5
- type Context,
6
- } from '@portabletext/editor/test/vitest'
7
- import {defineSchema} from '@portabletext/schema'
8
- import {Before} from 'racejar'
9
- import {Feature} from 'racejar/vitest'
10
- import smartQuotesFeature from './input-rule.smart-quotes.feature?raw'
11
- import {TypographyPlugin} from './plugin.typography'
12
-
13
- Feature({
14
- hooks: [
15
- Before(async (context: Context) => {
16
- const {editor, locator} = await createTestEditor({
17
- children: <TypographyPlugin />,
18
- schemaDefinition: defineSchema({
19
- decorators: [{name: 'strong'}],
20
- annotations: [{name: 'link'}],
21
- }),
22
- })
23
-
24
- context.locator = locator
25
- context.editor = editor
26
- }),
27
- ],
28
- featureText: smartQuotesFeature,
29
- stepDefinitions,
30
- parameterTypes,
31
- })
@@ -1,190 +0,0 @@
1
- import {
2
- defineTextTransformRule,
3
- type InputRule,
4
- } from '@portabletext/plugin-input-rule'
5
-
6
- /**
7
- * @public
8
- */
9
- export const emDashRule = defineTextTransformRule({
10
- on: /--/,
11
- transform: () => '—',
12
- })
13
-
14
- /**
15
- * @public
16
- */
17
- export const ellipsisRule = defineTextTransformRule({
18
- on: /\.\.\./,
19
- transform: () => '…',
20
- })
21
-
22
- /**
23
- * @public
24
- */
25
- export const openingDoubleQuoteRule = defineTextTransformRule({
26
- on: /(?:^|(?<=[\s{[(<'"\u2018\u201C]))"/g,
27
- transform: () => '“',
28
- })
29
-
30
- /**
31
- * @public
32
- */
33
- export const closingDoubleQuoteRule = defineTextTransformRule({
34
- on: /"/g,
35
- transform: () => '”',
36
- })
37
-
38
- /**
39
- * @public
40
- */
41
- export const openingSingleQuoteRule = defineTextTransformRule({
42
- on: /(?:^|(?<=[\s{[(<'"\u2018\u201C]))'/g,
43
- transform: () => '‘',
44
- })
45
-
46
- /**
47
- * @public
48
- */
49
- export const closingSingleQuoteRule = defineTextTransformRule({
50
- on: /'/g,
51
- transform: () => '’',
52
- })
53
-
54
- /**
55
- * @public
56
- */
57
- export const smartQuotesRules: Array<InputRule> = [
58
- openingDoubleQuoteRule,
59
- closingDoubleQuoteRule,
60
- openingSingleQuoteRule,
61
- closingSingleQuoteRule,
62
- ]
63
-
64
- /**
65
- * @public
66
- */
67
- export const leftArrowRule = defineTextTransformRule({
68
- on: /<-/,
69
- transform: () => '←',
70
- })
71
-
72
- /**
73
- * @public
74
- */
75
- export const rightArrowRule = defineTextTransformRule({
76
- on: /->/,
77
- transform: () => '→',
78
- })
79
-
80
- /**
81
- * @public
82
- */
83
- export const copyrightRule = defineTextTransformRule({
84
- on: /\(c\)/,
85
- transform: () => '©',
86
- })
87
-
88
- /**
89
- * @public
90
- */
91
- export const servicemarkRule = defineTextTransformRule({
92
- on: /\(sm\)/,
93
- transform: () => '℠',
94
- })
95
-
96
- /**
97
- * @public
98
- */
99
- export const trademarkRule = defineTextTransformRule({
100
- on: /\(tm\)/,
101
- transform: () => '™',
102
- })
103
-
104
- /**
105
- * @beta
106
- */
107
- export const registeredTrademarkRule = defineTextTransformRule({
108
- on: /\(r\)/,
109
- transform: () => '®',
110
- })
111
-
112
- /**
113
- * @public
114
- */
115
- export const oneHalfRule = defineTextTransformRule({
116
- on: /(?:^|\s)(1\/2)\s/,
117
- transform: () => '½',
118
- })
119
-
120
- /**
121
- * @public
122
- */
123
- export const plusMinusRule = defineTextTransformRule({
124
- on: /\+\/-/,
125
- transform: () => '±',
126
- })
127
-
128
- /**
129
- * @public
130
- */
131
- export const notEqualRule = defineTextTransformRule({
132
- on: /!=/,
133
- transform: () => '≠',
134
- })
135
-
136
- /**
137
- * @public
138
- */
139
- export const laquoRule = defineTextTransformRule({
140
- on: /<</,
141
- transform: () => '«',
142
- })
143
-
144
- /**
145
- * @public
146
- */
147
- export const raquoRule = defineTextTransformRule({
148
- on: />>/,
149
- transform: () => '»',
150
- })
151
-
152
- /**
153
- * @public
154
- */
155
- export const multiplicationRule = defineTextTransformRule({
156
- on: /\d+\s?([*x])\s?\d+/,
157
- transform: () => '×',
158
- })
159
-
160
- /**
161
- * @public
162
- */
163
- export const superscriptTwoRule = defineTextTransformRule({
164
- on: /\^2/,
165
- transform: () => '²',
166
- })
167
-
168
- /**
169
- * @public
170
- */
171
- export const superscriptThreeRule = defineTextTransformRule({
172
- on: /\^3/,
173
- transform: () => '³',
174
- })
175
-
176
- /**
177
- * @public
178
- */
179
- export const oneQuarterRule = defineTextTransformRule({
180
- on: /(?:^|\s)(1\/4)\s/,
181
- transform: () => '¼',
182
- })
183
-
184
- /**
185
- * @public
186
- */
187
- export const threeQuartersRule = defineTextTransformRule({
188
- on: /(?:^|\s)(3\/4)\s/,
189
- transform: () => '¾',
190
- })
@@ -1,147 +0,0 @@
1
- import {
2
- InputRulePlugin,
3
- type InputRuleGuard,
4
- } from '@portabletext/plugin-input-rule'
5
- import {useMemo} from 'react'
6
- import {
7
- closingDoubleQuoteRule,
8
- closingSingleQuoteRule,
9
- copyrightRule,
10
- ellipsisRule,
11
- emDashRule,
12
- laquoRule,
13
- leftArrowRule,
14
- multiplicationRule,
15
- notEqualRule,
16
- oneHalfRule,
17
- oneQuarterRule,
18
- openingDoubleQuoteRule,
19
- openingSingleQuoteRule,
20
- plusMinusRule,
21
- raquoRule,
22
- registeredTrademarkRule,
23
- rightArrowRule,
24
- servicemarkRule,
25
- superscriptThreeRule,
26
- superscriptTwoRule,
27
- threeQuartersRule,
28
- trademarkRule,
29
- } from './input-rules.typography'
30
-
31
- const defaultRuleConfig = [
32
- {name: 'emDash', rule: emDashRule, state: 'on'},
33
- {name: 'ellipsis', rule: ellipsisRule, state: 'on'},
34
- {name: 'openingDoubleQuote', rule: openingDoubleQuoteRule, state: 'on'},
35
- {name: 'closingDoubleQuote', rule: closingDoubleQuoteRule, state: 'on'},
36
- {name: 'openingSingleQuote', rule: openingSingleQuoteRule, state: 'on'},
37
- {name: 'closingSingleQuote', rule: closingSingleQuoteRule, state: 'on'},
38
- {name: 'leftArrow', rule: leftArrowRule, state: 'on'},
39
- {name: 'rightArrow', rule: rightArrowRule, state: 'on'},
40
- {name: 'copyright', rule: copyrightRule, state: 'on'},
41
- {name: 'trademark', rule: trademarkRule, state: 'on'},
42
- {name: 'servicemark', rule: servicemarkRule, state: 'on'},
43
- {name: 'registeredTrademark', rule: registeredTrademarkRule, state: 'on'},
44
- {name: 'oneHalf', rule: oneHalfRule, state: 'off'},
45
- {name: 'plusMinus', rule: plusMinusRule, state: 'off'},
46
- {name: 'laquo', rule: laquoRule, state: 'off'},
47
- {name: 'notEqual', rule: notEqualRule, state: 'off'},
48
- {name: 'raquo', rule: raquoRule, state: 'off'},
49
- {name: 'multiplication', rule: multiplicationRule, state: 'off'},
50
- {name: 'superscriptTwo', rule: superscriptTwoRule, state: 'off'},
51
- {name: 'superscriptThree', rule: superscriptThreeRule, state: 'off'},
52
- {name: 'oneQuarter', rule: oneQuarterRule, state: 'off'},
53
- {name: 'threeQuarters', rule: threeQuartersRule, state: 'off'},
54
- ] as const
55
-
56
- type RuleName = (typeof defaultRuleConfig)[number]['name']
57
-
58
- /**
59
- * @public
60
- */
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])
145
-
146
- return <InputRulePlugin rules={configuredInputRules} />
147
- }