Skip to content

Implement splitText#241

Open
ydaniv wants to merge 7 commits into
masterfrom
splittext-implement
Open

Implement splitText#241
ydaniv wants to merge 7 commits into
masterfrom
splittext-implement

Conversation

@ydaniv
Copy link
Copy Markdown
Collaborator

@ydaniv ydaniv commented Jun 2, 2026

Description

  • Implement SplitText package

Copy link
Copy Markdown
Contributor

@ameerf-wix ameerf-wix left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Went through everything but the tests. Will update after I go through them too.

const { chars } = splitText('.headline', { type: 'chars' });

// Animate with any library
animate(chars, { opacity: [0, 1], transform: ['translateY(8px)', 'translateY(0)'], stagger: 0.03 });
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not show usage with interact? Also, what syntax is this? it is not standard WAAPI which should be chars.forEach((char, index) => char.animate({ opacity: [0, 1], transform: ['translateY(8px)', 'translateY(0)']}, {duration: 100, delay: index * 30}));

Comment on lines +54 to +66
```css
.char {
opacity: 0;
animation: fadeUp 0.4s ease forwards;
animation-delay: calc(var(--char-index) * 0.04s);
}
@keyframes fadeUp {
to {
opacity: 1;
transform: translateY(0);
}
}
```
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think an interact example is called for

const result = useSplitText(ref, { type: 'chars' });

useEffect(() => {
if (!result) return;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does this suggest that useSplitText might return a nullable (on failure)? If so is if (!result) return ()=>result.revert() is needed?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see now that it is explained in the comment inside useSpiltText - maybe put that comment here instead of there

Comment on lines +102 to +105
| `type` | `SplitType \| SplitType[]` | — | Split eagerly on call instead of lazily |
| `wrapperClass` | `string \| WrapperClassConfig` | — | Extra CSS class(es) on wrapper spans |
| `wrapperStyle` | `Partial<CSSStyleDeclaration> \| WrapperStyleConfig` | — | Inline styles on wrapper spans |
| `wrapperAttrs` | `Record<string, string> \| WrapperAttrsConfig` | — | Custom attributes on wrapper spans |
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this kind of types are probably good for LLM, but from a human's perspective it gives no information because I don't know what SplitType or WrapperClassConfig (etc..) are.

Comment on lines +103 to +107
| `wrapperClass` | `string \| WrapperClassConfig` | — | Extra CSS class(es) on wrapper spans |
| `wrapperStyle` | `Partial<CSSStyleDeclaration> \| WrapperStyleConfig` | — | Inline styles on wrapper spans |
| `wrapperAttrs` | `Record<string, string> \| WrapperAttrsConfig` | — | Custom attributes on wrapper spans |
| `contentAttribute` | `'none' \| 'both' \| 'attribute-only'` | `'both'` | Controls `data-content` on char/word wrappers |
| `aria` | `'auto' \| 'none'` | `'auto'` | ARIA handling mode |
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unrelated to this PR, but this idea of having a configuration for adding styles and attributes seems interesting also for interact-element and can help creating complex compositions (e.g. background-scroll)

// Fallback for environments without Constructable Stylesheets (e.g. jsdom)
const style = doc.createElement('style');
style.textContent = BASE_CSS;
(doc.head ?? doc.documentElement)?.appendChild(style);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this might not run. Maybe log a warning in that case?


/** Return `true` when `segment` contains only whitespace characters. */
export function isWhitespaceOnly(segment: string): boolean {
return segment.length > 0 && /^\s+$/.test(segment);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+ already captures that it is not empty

Suggested change
return segment.length > 0 && /^\s+$/.test(segment);
return /^\s+$/.test(segment);

Comment on lines +101 to +125
const tokens: string[] = [];
let current = '';

for (const { segment } of segments) {
if (!segment) continue;

if (isWhitespaceOnly(segment)) {
if (current) {
tokens.push(current + segment);
current = '';
} else if (tokens.length > 0) {
tokens[tokens.length - 1] += segment;
} else {
current += segment;
}
continue;
}

current += segment;
}

if (current) tokens.push(current);

return tokens;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

kind of weirdly written. It seems that using some while loop here should make it more readable

* the result.
*/
export function getTextContent(element: HTMLElement): string {
return (element.textContent ?? '').trim();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have definitely seen this line in the other files (and not calling getTextContent), which kind of proves it is not enough to be an actual utility. If we want to keep it, then let's replace those lines with a proper call to this function.

return NodeFilter.FILTER_REJECT;
}
}
return NodeFilter.FILTER_SKIP;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why skip? are we accepting only non-element nodes?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants