Skip to content

Commit 82515f4

Browse files
committed
feat: merge with default styles
1 parent 6917372 commit 82515f4

9 files changed

Lines changed: 130 additions & 93 deletions

src/types.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -838,7 +838,8 @@ export interface EnrichedTextProps extends ViewProps {
838838
onMentionPress?: (event: OnMentionPressEvent) => void;
839839
}
840840

841-
interface EnrichedTextMentionStyleProperties extends MentionStyleProperties {
841+
export interface EnrichedTextMentionStyleProperties
842+
extends MentionStyleProperties {
842843
pressColor?: ColorValue;
843844
pressBackgroundColor?: ColorValue;
844845
}

src/utils/expandMentionStylesForIndicators.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
import type { HtmlStyle, MentionStyleProperties } from '../types';
2-
import { DEFAULT_HTML_STYLE } from './defaultHtmlStyle';
32
import { isMentionStyleRecord } from './isMentionStyleRecord';
43

54
export function expandMentionStylesForIndicators(
65
mention: HtmlStyle['mention'] | undefined,
7-
indicators: string[]
6+
indicators: string[],
7+
htmlStyleToMergeWith: HtmlStyle
88
): Record<string, MentionStyleProperties> {
99
const out: Record<string, MentionStyleProperties> = {};
1010
for (const indicator of indicators) {
1111
out[indicator] = {
12-
...DEFAULT_HTML_STYLE.mention,
12+
...htmlStyleToMergeWith.mention,
1313
...(isMentionStyleRecord(mention)
1414
? (mention[indicator] ?? mention.default ?? {})
1515
: mention),

src/utils/normalizeHtmlStyle.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ const convertToHtmlStyleInternal = (
3636
): HtmlStyleInternal => {
3737
const mentionStyles = expandMentionStylesForIndicators(
3838
style.mention,
39-
mentionIndicators
39+
mentionIndicators,
40+
DEFAULT_HTML_STYLE
4041
);
4142

4243
let markerFontWeight: string | undefined;

src/web/EnrichedText.tsx

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@ import { memo, useMemo, useRef, type CSSProperties } from 'react';
22
import type { EnrichedTextProps } from '../types';
33
import './EnrichedText.css';
44
import { enrichedTextStyleToCSSProperties } from './styleConversion/enrichedTextStyleToCSSProperties';
5-
import { htmlStyleToCSSVariables } from './styleConversion/htmlStyleToCSSVariables';
5+
import {
6+
htmlStyleToCSSVariables,
7+
mergeWithDefaultEnrichedTextHtmlStyle,
8+
} from './styleConversion/htmlStyleToCSSVariables';
69
import { ENRICHED_TEXT_CLASSNAME } from './constants/classNames';
710
import { enrichedInputThemingToCSSProperties } from './styleConversion/enrichedThemingToCSSProperties';
811
import { buildMentionRulesCSS } from './styleConversion/buildMentionRulesCSS';
@@ -22,17 +25,22 @@ export const EnrichedText = memo(
2225
[sanitizedHtml]
2326
);
2427

28+
const resolvedHtmlStyle = useMemo(
29+
() => mergeWithDefaultEnrichedTextHtmlStyle(htmlStyle),
30+
[htmlStyle]
31+
);
32+
2533
const textStyle: CSSProperties = useMemo(
2634
() => enrichedTextStyleToCSSProperties(style ?? {}),
2735
[style]
2836
);
2937

3038
const cssVars = useMemo(
3139
() => ({
32-
...htmlStyleToCSSVariables(htmlStyle),
40+
...htmlStyleToCSSVariables(resolvedHtmlStyle),
3341
...INLINE_IMAGE_CSS_VARIABLES,
3442
}),
35-
[htmlStyle]
43+
[resolvedHtmlStyle]
3644
);
3745

3846
const themingStyle = useMemo(
@@ -41,8 +49,8 @@ export const EnrichedText = memo(
4149
);
4250

4351
const mentionRulesCSS = useMemo(
44-
() => buildMentionRulesCSS('text', htmlStyle),
45-
[htmlStyle]
52+
() => buildMentionRulesCSS(resolvedHtmlStyle),
53+
[resolvedHtmlStyle]
4654
);
4755

4856
const finalStyle = useMemo(

src/web/EnrichedTextInput.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -395,7 +395,7 @@ export const EnrichedTextInput = ({
395395
);
396396

397397
const mentionRulesCSS = useMemo(
398-
() => buildMentionRulesCSS('input', resolvedHtmlStyle),
398+
() => buildMentionRulesCSS(resolvedHtmlStyle),
399399
[resolvedHtmlStyle]
400400
);
401401

src/web/styleConversion/__tests__/buildMentionRulesCSS.test.ts

Lines changed: 21 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -6,45 +6,33 @@ import {
66
} from '../../constants/classNames';
77

88
describe('buildMentionRulesCSS', () => {
9-
it.each([
10-
{
11-
description: 'EnrichedTextInput',
12-
input: 'input' as const,
13-
expected: ENRICHED_TEXT_INPUT_CLASSNAME,
14-
},
15-
{
16-
description: 'EnrichedText',
17-
input: 'text' as const,
18-
expected: ENRICHED_TEXT_CLASSNAME,
19-
},
20-
])(
21-
'[$description] emits default class rule and attribute rule for @',
22-
({ input, expected }) => {
23-
const merged = mergeWithDefaultHtmlStyle({
24-
mention: { '@': { color: 'red' } },
25-
});
26-
const css = buildMentionRulesCSS(input, merged);
9+
it('emits base rules for both containers (default + @)', () => {
10+
const merged = mergeWithDefaultHtmlStyle({
11+
mention: { '@': { color: 'red' } },
12+
});
13+
const css = buildMentionRulesCSS(merged);
2714

28-
expect(css).toMatch(new RegExp(`\\.${expected} mention\\s*\\{`));
29-
expect(css).toContain('var(--et-mention-default-color)');
30-
expect(css).toContain('var(--et-mention-default-background-color)');
31-
expect(css).toContain('var(--et-mention-default-text-decoration-line)');
15+
expect(css).toContain(`.${ENRICHED_TEXT_INPUT_CLASSNAME} mention`);
16+
expect(css).toContain(`.${ENRICHED_TEXT_CLASSNAME} mention`);
17+
expect(css).toContain('var(--et-mention-default-color)');
18+
expect(css).toContain('var(--et-mention-default-background-color)');
19+
expect(css).toContain('var(--et-mention-default-text-decoration-line)');
3220

33-
expect(css).toContain(`.${expected} mention[indicator="@"]`);
34-
expect(css).toContain('var(--et-mention-u0040-color)');
35-
expect(css).toContain('var(--et-mention-u0040-background-color)');
36-
expect(css).toContain('var(--et-mention-u0040-text-decoration-line)');
37-
}
38-
);
21+
expect(css).toContain(
22+
`.${ENRICHED_TEXT_INPUT_CLASSNAME} mention[indicator="@"]`
23+
);
24+
expect(css).toContain(`.${ENRICHED_TEXT_CLASSNAME} mention[indicator="@"]`);
25+
expect(css).toContain('var(--et-mention-u0040-color)');
26+
expect(css).toContain('var(--et-mention-u0040-background-color)');
27+
expect(css).toContain('var(--et-mention-u0040-text-decoration-line)');
28+
});
3929

4030
it('returns empty string when mention is missing', () => {
41-
expect(buildMentionRulesCSS('input', undefined)).toBe('');
42-
expect(buildMentionRulesCSS('input', {})).toBe('');
31+
expect(buildMentionRulesCSS(undefined)).toBe('');
32+
expect(buildMentionRulesCSS({})).toBe('');
4333
});
4434

4535
it('returns empty string when mention is not a style record', () => {
46-
expect(buildMentionRulesCSS('input', { mention: { color: 'red' } })).toBe(
47-
''
48-
);
36+
expect(buildMentionRulesCSS({ mention: { color: 'red' } })).toBe('');
4937
});
5038
});

src/web/styleConversion/__tests__/htmlStyleToCSSVariables.test.ts

Lines changed: 7 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,6 @@ import {
88

99
type CodeStyle = HtmlStyle['code'];
1010

11-
const DEFAULT_MENTION_CSS_VARS: Record<string, string> = {
12-
'--et-mention-default-color': String(DEFAULT_HTML_STYLE.mention.color),
13-
'--et-mention-default-background-color': String(
14-
DEFAULT_HTML_STYLE.mention.backgroundColor
15-
),
16-
'--et-mention-default-text-decoration-line': String(
17-
DEFAULT_HTML_STYLE.mention.textDecorationLine
18-
),
19-
};
20-
2111
const defaultMentionOnlyResolved = {
2212
default: { ...DEFAULT_HTML_STYLE.mention },
2313
};
@@ -108,16 +98,8 @@ describe('mergeWithDefaultHtmlStyle', () => {
10898
});
10999

110100
describe('htmlStyleToCSSVariables', () => {
111-
it('undefined → default mention vars only', () => {
112-
expect(htmlStyleToCSSVariables(undefined)).toEqual(
113-
DEFAULT_MENTION_CSS_VARS as CSSProperties
114-
);
115-
});
116-
117-
it('empty style → default mention vars only', () => {
118-
expect(htmlStyleToCSSVariables({})).toEqual(
119-
DEFAULT_MENTION_CSS_VARS as CSSProperties
120-
);
101+
it('empty style → empty vars', () => {
102+
expect(htmlStyleToCSSVariables({})).toEqual({} as CSSProperties);
121103
});
122104

123105
it('integer color → rgba string', () => {
@@ -275,9 +257,10 @@ describe('htmlStyleToCSSVariables', () => {
275257

276258
describe('mention CSS variables', () => {
277259
it('flat mention → default vars', () => {
278-
const vars = htmlStyleToCSSVariables({
260+
const merged = mergeWithDefaultHtmlStyle({
279261
mention: { color: '#f00' },
280-
}) as Record<string, string>;
262+
});
263+
const vars = htmlStyleToCSSVariables(merged) as Record<string, string>;
281264
expect(vars['--et-mention-default-color']).toBe('#f00');
282265
});
283266

@@ -293,10 +276,8 @@ describe('mention CSS variables', () => {
293276
});
294277

295278
it('mention {} → default vars', () => {
296-
const vars = htmlStyleToCSSVariables({ mention: {} }) as Record<
297-
string,
298-
string
299-
>;
279+
const merged = mergeWithDefaultHtmlStyle({ mention: {} });
280+
const vars = htmlStyleToCSSVariables(merged) as Record<string, string>;
300281
expect(vars['--et-mention-default-color']).toBe(
301282
DEFAULT_HTML_STYLE.mention.color
302283
);

src/web/styleConversion/buildMentionRulesCSS.ts

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
import type { HtmlStyle, MentionStyleProperties } from '../../types';
1+
import type {
2+
EnrichedTextHtmlStyle,
3+
HtmlStyle,
4+
MentionStyleProperties,
5+
} from '../../types';
26
import { isMentionStyleRecord } from '../../utils/isMentionStyleRecord';
37
import {
48
ENRICHED_TEXT_CLASSNAME,
@@ -14,9 +18,14 @@ function escapeIndicatorForCssAttributeSelector(indicator: string): string {
1418
return indicator.replace(/["\\]/g, '\\$&');
1519
}
1620

21+
function mentionSelector(className: string, indicator: string): string {
22+
return indicator === MENTION_STYLE_DEFAULT_KEY
23+
? `.${className} mention`
24+
: `.${className} mention[indicator="${escapeIndicatorForCssAttributeSelector(indicator)}"]`;
25+
}
26+
1727
export function buildMentionRulesCSS(
18-
component: 'input' | 'text',
19-
htmlStyle?: HtmlStyle
28+
htmlStyle?: HtmlStyle | EnrichedTextHtmlStyle
2029
): string {
2130
const mapRaw = htmlStyle?.mention;
2231
if (!mapRaw || typeof mapRaw !== 'object' || !isMentionStyleRecord(mapRaw)) {
@@ -29,20 +38,18 @@ export function buildMentionRulesCSS(
2938
return '';
3039
}
3140

32-
const className =
33-
component === 'input'
34-
? ENRICHED_TEXT_INPUT_CLASSNAME
35-
: ENRICHED_TEXT_CLASSNAME;
36-
3741
const lines: string[] = [];
42+
3843
for (const indicator of keys) {
39-
const selector =
40-
indicator === MENTION_STYLE_DEFAULT_KEY
41-
? `.${className} mention`
42-
: `.${className} mention[indicator="${escapeIndicatorForCssAttributeSelector(indicator)}"]`;
44+
const inputSelector = mentionSelector(
45+
ENRICHED_TEXT_INPUT_CLASSNAME,
46+
indicator
47+
);
48+
const textSelector = mentionSelector(ENRICHED_TEXT_CLASSNAME, indicator);
4349

4450
lines.push(
45-
`${selector} {
51+
`${inputSelector},
52+
${textSelector} {
4653
color: var(${ET_MENTION_CSS_VARS.color(indicator)});
4754
background-color: var(${ET_MENTION_CSS_VARS.backgroundColor(indicator)});
4855
text-decoration-line: var(${ET_MENTION_CSS_VARS.textDecorationLine(indicator)});

0 commit comments

Comments
 (0)