about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authordan <dan.abramov@gmail.com>2024-11-21 20:44:40 +0000
committerGitHub <noreply@github.com>2024-11-21 20:44:40 +0000
commitcc60566cde880deeea1e50774a2108af8f7766f8 (patch)
tree5b6c925bf4331907fe57a094e30d6a23615debf4 /src
parentff23ddb556be4b2a9c4029dce6f857df34fc0b6b (diff)
downloadvoidsky-cc60566cde880deeea1e50774a2108af8f7766f8.tar.zst
Fix Fast Refresh in <Text> files (#6609)
* Separate non-components from components

* Mark old Text as deprecated

* Move emoji utilities to non-React file

* Fix type

* Fix import
Diffstat (limited to 'src')
-rw-r--r--src/alf/typography.tsx135
-rw-r--r--src/components/Typography.tsx145
-rw-r--r--src/screens/Onboarding/Layout.tsx3
-rw-r--r--src/view/com/composer/text-input/TextInput.tsx2
-rw-r--r--src/view/com/composer/text-input/TextInput.web.tsx2
-rw-r--r--src/view/com/util/text/Text.tsx8
6 files changed, 156 insertions, 139 deletions
diff --git a/src/alf/typography.tsx b/src/alf/typography.tsx
new file mode 100644
index 000000000..d00910bf8
--- /dev/null
+++ b/src/alf/typography.tsx
@@ -0,0 +1,135 @@
+import React from 'react'
+import {TextProps as RNTextProps} from 'react-native'
+import {StyleProp, TextStyle} from 'react-native'
+import {UITextView} from 'react-native-uitextview'
+import createEmojiRegex from 'emoji-regex'
+
+import {isNative} from '#/platform/detection'
+import {Alf, applyFonts, atoms, flatten} from '#/alf'
+
+/**
+ * Util to calculate lineHeight from a text size atom and a leading atom
+ *
+ * Example:
+ *   `leading(atoms.text_md, atoms.leading_normal)` // => 24
+ */
+export function leading<
+  Size extends {fontSize?: number},
+  Leading extends {lineHeight?: number},
+>(textSize: Size, leading: Leading) {
+  const size = textSize?.fontSize || atoms.text_md.fontSize
+  const lineHeight = leading?.lineHeight || atoms.leading_normal.lineHeight
+  return Math.round(size * lineHeight)
+}
+
+/**
+ * Ensures that `lineHeight` defaults to a relative value of `1`, or applies
+ * other relative leading atoms.
+ *
+ * If the `lineHeight` value is > 2, we assume it's an absolute value and
+ * returns it as-is.
+ */
+export function normalizeTextStyles(
+  styles: StyleProp<TextStyle>,
+  {
+    fontScale,
+    fontFamily,
+  }: {
+    fontScale: number
+    fontFamily: Alf['fonts']['family']
+  } & Pick<Alf, 'flags'>,
+) {
+  const s = flatten(styles)
+  // should always be defined on these components
+  s.fontSize = (s.fontSize || atoms.text_md.fontSize) * fontScale
+
+  if (s?.lineHeight) {
+    if (s.lineHeight !== 0 && s.lineHeight <= 2) {
+      s.lineHeight = Math.round(s.fontSize * s.lineHeight)
+    }
+  } else if (!isNative) {
+    s.lineHeight = s.fontSize
+  }
+
+  applyFonts(s, fontFamily)
+
+  return s
+}
+
+export type StringChild = string | (string | null)[]
+export type TextProps = Omit<RNTextProps, 'children'> & {
+  /**
+   * Lets the user select text, to use the native copy and paste functionality.
+   */
+  selectable?: boolean
+  /**
+   * Provides `data-*` attributes to the underlying `UITextView` component on
+   * web only.
+   */
+  dataSet?: Record<string, string | number | undefined>
+  /**
+   * Appears as a small tooltip on web hover.
+   */
+  title?: string
+} & (
+    | {
+        emoji?: true
+        children: StringChild
+      }
+    | {
+        emoji?: false
+        children: RNTextProps['children']
+      }
+  )
+
+const EMOJI = createEmojiRegex()
+
+export function childHasEmoji(children: React.ReactNode) {
+  return (Array.isArray(children) ? children : [children]).some(
+    child => typeof child === 'string' && createEmojiRegex().test(child),
+  )
+}
+
+export function childIsString(
+  children: React.ReactNode,
+): children is StringChild {
+  return (
+    typeof children === 'string' ||
+    (Array.isArray(children) &&
+      children.every(child => typeof child === 'string' || child === null))
+  )
+}
+
+export function renderChildrenWithEmoji(
+  children: StringChild,
+  props: Omit<TextProps, 'children'> = {},
+) {
+  const normalized = Array.isArray(children) ? children : [children]
+
+  return (
+    <UITextView {...props}>
+      {normalized.map(child => {
+        if (typeof child !== 'string') return child
+
+        const emojis = child.match(EMOJI)
+
+        if (emojis === null) {
+          return child
+        }
+
+        return child.split(EMOJI).map((stringPart, index) => (
+          <UITextView key={index} {...props}>
+            {stringPart}
+            {emojis[index] ? (
+              <UITextView
+                {...props}
+                style={[props?.style, {color: 'black', fontFamily: 'System'}]}>
+                {emojis[index]}
+              </UITextView>
+            ) : null}
+          </UITextView>
+        ))
+      })}
+    </UITextView>
+  )
+}
diff --git a/src/components/Typography.tsx b/src/components/Typography.tsx
index 69e073271..4bcbf9f09 100644
--- a/src/components/Typography.tsx
+++ b/src/components/Typography.tsx
@@ -1,140 +1,17 @@
-import React from 'react'
-import {StyleProp, TextProps as RNTextProps, TextStyle} from 'react-native'
 import {UITextView} from 'react-native-uitextview'
-import createEmojiRegex from 'emoji-regex'
 
 import {logger} from '#/logger'
-import {isIOS, isNative} from '#/platform/detection'
-import {Alf, applyFonts, atoms, flatten, useAlf, useTheme, web} from '#/alf'
+import {isIOS} from '#/platform/detection'
+import {atoms, flatten, useAlf, useTheme, web} from '#/alf'
+import {
+  childHasEmoji,
+  childIsString,
+  normalizeTextStyles,
+  renderChildrenWithEmoji,
+  TextProps,
+} from '#/alf/typography'
 import {IS_DEV} from '#/env'
-
-export type StringChild = string | (string | null)[]
-
-export type TextProps = Omit<RNTextProps, 'children'> & {
-  /**
-   * Lets the user select text, to use the native copy and paste functionality.
-   */
-  selectable?: boolean
-  /**
-   * Provides `data-*` attributes to the underlying `UITextView` component on
-   * web only.
-   */
-  dataSet?: Record<string, string | number | undefined>
-  /**
-   * Appears as a small tooltip on web hover.
-   */
-  title?: string
-} & (
-    | {
-        emoji?: true
-        children: StringChild
-      }
-    | {
-        emoji?: false
-        children: RNTextProps['children']
-      }
-  )
-
-const EMOJI = createEmojiRegex()
-
-export function childHasEmoji(children: React.ReactNode) {
-  return (Array.isArray(children) ? children : [children]).some(
-    child => typeof child === 'string' && createEmojiRegex().test(child),
-  )
-}
-
-export function childIsString(
-  children: React.ReactNode,
-): children is StringChild {
-  return (
-    typeof children === 'string' ||
-    (Array.isArray(children) &&
-      children.every(child => typeof child === 'string' || child === null))
-  )
-}
-
-export function renderChildrenWithEmoji(
-  children: StringChild,
-  props: Omit<TextProps, 'children'> = {},
-) {
-  const normalized = Array.isArray(children) ? children : [children]
-
-  return (
-    <UITextView {...props}>
-      {normalized.map(child => {
-        if (typeof child !== 'string') return child
-
-        const emojis = child.match(EMOJI)
-
-        if (emojis === null) {
-          return child
-        }
-
-        return child.split(EMOJI).map((stringPart, index) => (
-          <UITextView key={index} {...props}>
-            {stringPart}
-            {emojis[index] ? (
-              <UITextView
-                {...props}
-                style={[props?.style, {color: 'black', fontFamily: 'System'}]}>
-                {emojis[index]}
-              </UITextView>
-            ) : null}
-          </UITextView>
-        ))
-      })}
-    </UITextView>
-  )
-}
-
-/**
- * Util to calculate lineHeight from a text size atom and a leading atom
- *
- * Example:
- *   `leading(atoms.text_md, atoms.leading_normal)` // => 24
- */
-export function leading<
-  Size extends {fontSize?: number},
-  Leading extends {lineHeight?: number},
->(textSize: Size, leading: Leading) {
-  const size = textSize?.fontSize || atoms.text_md.fontSize
-  const lineHeight = leading?.lineHeight || atoms.leading_normal.lineHeight
-  return Math.round(size * lineHeight)
-}
-
-/**
- * Ensures that `lineHeight` defaults to a relative value of `1`, or applies
- * other relative leading atoms.
- *
- * If the `lineHeight` value is > 2, we assume it's an absolute value and
- * returns it as-is.
- */
-export function normalizeTextStyles(
-  styles: StyleProp<TextStyle>,
-  {
-    fontScale,
-    fontFamily,
-  }: {
-    fontScale: number
-    fontFamily: Alf['fonts']['family']
-  } & Pick<Alf, 'flags'>,
-) {
-  const s = flatten(styles)
-  // should always be defined on these components
-  s.fontSize = (s.fontSize || atoms.text_md.fontSize) * fontScale
-
-  if (s?.lineHeight) {
-    if (s.lineHeight !== 0 && s.lineHeight <= 2) {
-      s.lineHeight = Math.round(s.fontSize * s.lineHeight)
-    }
-  } else if (!isNative) {
-    s.lineHeight = s.fontSize
-  }
-
-  applyFonts(s, fontFamily)
-
-  return s
-}
+export type {TextProps}
 
 /**
  * Our main text component. Use this most of the time.
@@ -183,7 +60,7 @@ export function Text({
   )
 }
 
-export function createHeadingElement({level}: {level: number}) {
+function createHeadingElement({level}: {level: number}) {
   return function HeadingElement({style, ...rest}: TextProps) {
     const attr =
       web({
diff --git a/src/screens/Onboarding/Layout.tsx b/src/screens/Onboarding/Layout.tsx
index 02b207303..4a07ebd83 100644
--- a/src/screens/Onboarding/Layout.tsx
+++ b/src/screens/Onboarding/Layout.tsx
@@ -17,10 +17,11 @@ import {
   useTheme,
   web,
 } from '#/alf'
+import {leading} from '#/alf/typography'
 import {Button, ButtonIcon, ButtonText} from '#/components/Button'
 import {ChevronLeft_Stroke2_Corner0_Rounded as ChevronLeft} from '#/components/icons/Chevron'
 import {createPortalGroup} from '#/components/Portal'
-import {leading, P, Text} from '#/components/Typography'
+import {P, Text} from '#/components/Typography'
 import {IS_DEV} from '#/env'
 
 const COL_WIDTH = 420
diff --git a/src/view/com/composer/text-input/TextInput.tsx b/src/view/com/composer/text-input/TextInput.tsx
index 10cf1a931..96cecb37c 100644
--- a/src/view/com/composer/text-input/TextInput.tsx
+++ b/src/view/com/composer/text-input/TextInput.tsx
@@ -31,7 +31,7 @@ import {
   suggestLinkCardUri,
 } from '#/view/com/composer/text-input/text-input-util'
 import {atoms as a, useAlf} from '#/alf'
-import {normalizeTextStyles} from '#/components/Typography'
+import {normalizeTextStyles} from '#/alf/typography'
 import {Autocomplete} from './mobile/Autocomplete'
 
 export interface TextInputRef {
diff --git a/src/view/com/composer/text-input/TextInput.web.tsx b/src/view/com/composer/text-input/TextInput.web.tsx
index fa742d258..34d19e14c 100644
--- a/src/view/com/composer/text-input/TextInput.web.tsx
+++ b/src/view/com/composer/text-input/TextInput.web.tsx
@@ -23,8 +23,8 @@ import {
 } from '#/view/com/composer/text-input/text-input-util'
 import {textInputWebEmitter} from '#/view/com/composer/text-input/textInputWebEmitter'
 import {atoms as a, useAlf} from '#/alf'
+import {normalizeTextStyles} from '#/alf/typography'
 import {Portal} from '#/components/Portal'
-import {normalizeTextStyles} from '#/components/Typography'
 import {Text} from '../../util/text/Text'
 import {createSuggestion} from './web/Autocomplete'
 import {Emoji} from './web/EmojiPicker.web'
diff --git a/src/view/com/util/text/Text.tsx b/src/view/com/util/text/Text.tsx
index dbf5e2e13..a5278e0a0 100644
--- a/src/view/com/util/text/Text.tsx
+++ b/src/view/com/util/text/Text.tsx
@@ -12,7 +12,7 @@ import {
   childIsString,
   renderChildrenWithEmoji,
   StringChild,
-} from '#/components/Typography'
+} from '#/alf/typography'
 import {IS_DEV} from '#/env'
 
 export type CustomTextProps = Omit<TextProps, 'children'> & {
@@ -32,7 +32,11 @@ export type CustomTextProps = Omit<TextProps, 'children'> & {
       }
   )
 
-export function Text({
+export {Text_DEPRECATED as Text}
+/**
+ * @deprecated use Text from Typography instead.
+ */
+function Text_DEPRECATED({
   type = 'md',
   children,
   emoji,