├── .eslintignore ├── .eslintrc.js ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.yml │ └── config.yml ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── ci.yml │ ├── docs.yml │ └── npm.yml ├── .gitignore ├── .husky ├── .gitignore └── commit-msg ├── .prettierignore ├── .prettierrc.js ├── .vscode ├── extensions.json ├── ltex.dictionary.en-US.txt └── settings.json ├── .yarn ├── plugins │ └── @yarnpkg │ │ └── plugin-interactive-tools.cjs └── releases │ └── yarn-3.2.4.cjs ├── .yarnrc.yml ├── CHANGELOG.md ├── CONTRIBUTING.adoc ├── DEVELOPING.adoc ├── HELP.adoc ├── ISSUE_TEMPLATE.md ├── LICENSE ├── MAINTAINING.adoc ├── README.md ├── apps ├── benchmarking │ ├── .expo-shared │ │ └── assets.json │ ├── .gitignore │ ├── App.js │ ├── Benchmark.js │ ├── app.json │ ├── assets │ │ ├── adaptive-icon.png │ │ ├── favicon.png │ │ ├── icon.png │ │ └── splash.png │ ├── babel.config.js │ ├── eas.json │ ├── metro.config.js │ ├── package.json │ ├── profiles.js │ ├── profiles │ │ ├── ProfileSimple.js │ │ ├── ProfileV5.js │ │ ├── ProfileV6Source.js │ │ └── RenderHtmlSimple.js │ ├── source.js │ └── useBenchmark.js ├── discovery │ ├── .expo-shared │ │ └── assets.json │ ├── .gitignore │ ├── App.tsx │ ├── app.config.js │ ├── assets │ │ ├── fonts │ │ │ └── SpaceMono-Regular.ttf │ │ ├── images │ │ │ ├── anders-jilden-architecture.jpg │ │ │ ├── bank-phrom-rendering.jpg │ │ │ ├── diana-polekhina-tampering.jpg │ │ │ ├── fallback.png │ │ │ ├── favicon.png │ │ │ ├── icon.png │ │ │ ├── james-barnett-script.jpg │ │ │ ├── jj-ying-anchors.jpg │ │ │ ├── jon-cartagena-wheel.jpg │ │ │ ├── maik-jonietz-css.jpg │ │ │ ├── malcolm-lightbody-custom-rendering.jpg │ │ │ ├── markus-winkler-lists.jpg │ │ │ ├── pankaj-patel-html.jpg │ │ │ ├── russn-fckr-painting.jpg │ │ │ ├── ryan-baker-tre.jpg │ │ │ ├── simone-secci-question.jpg │ │ │ └── soragrit-wongsa-pictures.jpg │ │ └── svg │ │ │ ├── .gitignore │ │ │ └── logo.svg │ ├── babel.config.js │ ├── eas.json │ ├── index.js │ ├── metro.config.js │ ├── package.json │ ├── src │ │ ├── components │ │ │ ├── ArticleContainerAtom.tsx │ │ │ ├── BodyAdmonitionAtom.tsx │ │ │ ├── BodyChapterMolecule.tsx │ │ │ ├── BodyDividerAtom.tsx │ │ │ ├── BodyListAtom.tsx │ │ │ ├── BodyListItemAtom.tsx │ │ │ ├── BodyParagraphAtom.tsx │ │ │ ├── BodySectionMolecule.tsx │ │ │ ├── MaxWidthContainerAtom.tsx │ │ │ ├── RenderHtmlCardOrganism.tsx │ │ │ ├── TNodeTransformDisplayOrganism.tsx │ │ │ ├── TideListAtom.tsx │ │ │ ├── UIAppbarActionAtom.tsx │ │ │ ├── UIAppbarContentAtom.tsx │ │ │ ├── UIBidirectionalScrollViewAtom.tsx │ │ │ ├── UIBottomSheetAtom.tsx │ │ │ ├── UICardContainer.tsx │ │ │ ├── UIColorPickerControlAtom.tsx │ │ │ ├── UIDisplayLoadingAtom.tsx │ │ │ ├── UIFigureTitleAtom.tsx │ │ │ ├── UIHeaderAtom.tsx │ │ │ ├── UIHtmlDisplayMolecule.tsx │ │ │ ├── UIHyperlinkAtom.tsx │ │ │ ├── UILinkPressDisplayMolecule.tsx │ │ │ ├── UINavResourceTide.tsx │ │ │ ├── UINavTideMolecule.tsx │ │ │ ├── UIPickerControlAtom.tsx │ │ │ ├── UIRadioGroupControlMolecule.tsx │ │ │ ├── UIRadioItemAtom.tsx │ │ │ ├── UIRadioListControlMolecule.tsx │ │ │ ├── UISliderControlAtom.tsx │ │ │ ├── UISliderTideMolecule.tsx │ │ │ ├── UISnackbarAtom.tsx │ │ │ ├── UISourceDisplayMolecule.tsx │ │ │ ├── UISwitchControlAtom.tsx │ │ │ ├── UISwitchTideMolecule.tsx │ │ │ ├── UITTreeDisplayMolecule.tsx │ │ │ ├── UITideAtom.tsx │ │ │ ├── croles │ │ │ │ ├── CardColorRolesProvider.tsx │ │ │ │ ├── DefaultColorRolesProvider.tsx │ │ │ │ └── HeaderColorRolesProvider.tsx │ │ │ ├── nucleons │ │ │ │ ├── BoxNucleon.tsx │ │ │ │ ├── GestureHandlerAdapterNucleon.ts │ │ │ │ ├── IconNucleon.tsx │ │ │ │ ├── ListItemNucleon.tsx │ │ │ │ ├── TextRoleNucleon.tsx │ │ │ │ ├── contentWidthContextNucleon.ts │ │ │ │ ├── gestureHandlerContextNucleon.ts │ │ │ │ ├── types.ts │ │ │ │ ├── useContentWidthContext.ts │ │ │ │ ├── useSelectorPropsNucleon.ts │ │ │ │ ├── useSurfaceBackgroundStyleNucleon.ts │ │ │ │ └── useTextRoleNucleon.tsx │ │ │ ├── resources │ │ │ │ └── PlaygroundLists.tsx │ │ │ ├── screens │ │ │ │ └── HomeDrawerScreen │ │ │ │ │ ├── CustomDrawerContent.tsx │ │ │ │ │ ├── DrawerItemList.tsx │ │ │ │ │ ├── DrawerPlaygroundHeader.tsx │ │ │ │ │ ├── VersionDisplay.tsx │ │ │ │ │ ├── groupBy.ts │ │ │ │ │ └── index.tsx │ │ │ ├── selectedRadioItemContextAtom.ts │ │ │ └── templates │ │ │ │ ├── ArticleTemplate │ │ │ │ ├── AnimatedContextProvider.tsx │ │ │ │ ├── ArticleHeader.tsx │ │ │ │ ├── Scroller.ts │ │ │ │ ├── ScrollerProvider.tsx │ │ │ │ └── index.tsx │ │ │ │ └── PlaygroundTemplate │ │ │ │ ├── Playground.tsx │ │ │ │ ├── PlaygroundDisplay.tsx │ │ │ │ ├── Sheet.tsx │ │ │ │ ├── SheetChildrenRenderer.tsx │ │ │ │ ├── SheetControlsPortal.ts │ │ │ │ ├── SheetDescriptionPortal.ts │ │ │ │ ├── SheetNavigator.tsx │ │ │ │ ├── SheetNavigatorPortal.ts │ │ │ │ ├── SheetRouteColor.tsx │ │ │ │ ├── SheetRouteContainer.tsx │ │ │ │ ├── SheetRouteControls.tsx │ │ │ │ ├── SheetRouteFontFamily.tsx │ │ │ │ ├── SheetRouteHome.tsx │ │ │ │ ├── SheetRouteOlListType.tsx │ │ │ │ ├── SheetRouteSource.tsx │ │ │ │ ├── SheetRouteUlListType.tsx │ │ │ │ ├── SheetStack.tsx │ │ │ │ ├── contexts.ts │ │ │ │ ├── createPortal.ts │ │ │ │ ├── index.tsx │ │ │ │ ├── playgroundStore.tsx │ │ │ │ └── sheetSnapPoints.ts │ │ ├── constants │ │ │ └── index.ts │ │ ├── highlight │ │ │ ├── Highlighter.tsx │ │ │ ├── StylesheetsProvider.tsx │ │ │ ├── createStylesheets.ts │ │ │ ├── generateLines.tsx │ │ │ ├── highlighterStylesheetsContext.ts │ │ │ ├── lazyStylesheetRegistry.ts │ │ │ └── normalizeStylesheet.ts │ │ ├── hooks │ │ │ ├── useCachedResources.ts │ │ │ └── useOnLinkPress.ts │ │ ├── imagesMap.ts │ │ ├── nav-model.ts │ │ ├── navigation │ │ │ ├── RootNavigator.tsx │ │ │ ├── StackHeader.tsx │ │ │ └── index.tsx │ │ ├── providers │ │ │ └── PageToolkitProvider.tsx │ │ ├── state │ │ │ ├── ColorSchemeProvider.tsx │ │ │ ├── onLinkPressContext.ts │ │ │ └── store.tsx │ │ ├── substratum │ │ │ └── index.tsx │ │ ├── svg.d.ts │ │ ├── svgAssetsIndex.ts │ │ └── theme │ │ │ ├── ThemeProvider.tsx │ │ │ ├── alphaMixColor.ts │ │ │ ├── colorPrimitivesDeclaration.ts │ │ │ ├── colorSystem.ts │ │ │ ├── generateColorRoles.ts │ │ │ ├── shiftColor.ts │ │ │ └── themeAdaptedColors.ts │ ├── svgr.config.js │ ├── tsconfig.json │ ├── types.tsx │ └── version.js └── website │ ├── .gitignore │ ├── README.md │ ├── api │ └── .gitignore │ ├── babel.config.js │ ├── blog │ ├── 2021-06-07-foundry-announcement.mdx │ ├── 2021-06-27-create-blog-app-rnrh-I.mdx │ ├── 2021-06-28-create-blog-app-rnrh-II.mdx │ ├── 2021-06-29-create-blog-app-rnrh-III.mdx │ └── 2021-10-23-6-2-updates.mdx │ ├── docs │ ├── .gitignore │ ├── content │ │ └── _category_.json │ ├── flow │ │ └── _category_.json │ ├── guides │ │ └── _category_.json │ └── migration-guide.mdx │ ├── docusaurus.config.js │ ├── package.json │ ├── sidebars.js │ ├── src │ ├── components │ │ ├── APIReference.tsx │ │ ├── AdmonitionMDX.tsx │ │ ├── Badges.module.scss │ │ ├── Badges.tsx │ │ ├── DiscoveryFrame.module.scss │ │ ├── DiscoveryFrame.tsx │ │ ├── ExpoBlogCard.tsx │ │ ├── ExpoDiscoveryCard.tsx │ │ ├── ExpoSnippet.module.scss │ │ ├── ExpoSnippet.tsx │ │ ├── HomepageFeatures.module.scss │ │ ├── HomepageFeatures.tsx │ │ ├── HomepageHeader.module.scss │ │ ├── HomepageHeader.tsx │ │ ├── IPhoneFrame.module.scss │ │ ├── IPhoneFrame.tsx │ │ ├── ListsShowcase.module.scss │ │ ├── ListsShowcase.tsx │ │ ├── Reference.module.scss │ │ ├── Reference.tsx │ │ ├── RenderHTMLCard.module.scss │ │ ├── RenderHTMLCard.tsx │ │ ├── Screenshot.tsx │ │ ├── SocialLinks.module.scss │ │ ├── SocialLinks.tsx │ │ ├── SvgFigure.module.scss │ │ ├── SvgFigure.tsx │ │ ├── TNodeTransformDisplay.module.scss │ │ ├── TNodeTransformDisplay.tsx │ │ ├── TryOnExpoCard.module.scss │ │ ├── TryOnExpoCard.tsx │ │ ├── TwitterFollow.module.scss │ │ └── TwitterFollow.tsx │ ├── css │ │ ├── custom.scss │ │ └── shared.scss │ ├── pages │ │ ├── index.module.scss │ │ └── index.tsx │ ├── png.d.ts │ ├── scss.d.ts │ ├── svg.d.ts │ ├── svg │ │ └── .gitignore │ ├── theme │ │ ├── BlogPostItem │ │ │ ├── index.js │ │ │ └── styles.module.css │ │ ├── CodeBlock │ │ │ ├── __tests__ │ │ │ │ ├── index.test.js │ │ │ │ └── parseCodeBlockTitle.test.js │ │ │ ├── index.js │ │ │ ├── parseCodeBlockTitle.js │ │ │ └── styles.module.scss │ │ ├── DocSidebar │ │ │ ├── index.js │ │ │ └── styles.module.css │ │ ├── LayoutHead │ │ │ └── index.js │ │ └── Seo │ │ │ └── index.js │ └── typeui │ │ ├── Badges.tsx │ │ ├── DeclarationBox.tsx │ │ ├── HeaderTypeBox.tsx │ │ ├── Indent.tsx │ │ ├── Params.ts │ │ ├── reduceReflectionsWith.tsx │ │ ├── renderArrowSignature.tsx │ │ ├── renderFunctionSignature.tsx │ │ ├── renderReflection.tsx │ │ ├── renderSignatureParams.tsx │ │ ├── renderType.tsx │ │ ├── renderTypeParameters.tsx │ │ ├── styles.module.scss │ │ ├── tokens.tsx │ │ ├── useReflection.ts │ │ └── useReflectionIndex.tsx │ ├── static │ ├── .nojekyll │ ├── favicons │ │ ├── android-chrome-192x192.png │ │ ├── android-chrome-512x512.png │ │ ├── apple-touch-icon.png │ │ ├── browserconfig.xml │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ ├── favicon.ico │ │ ├── mstile-144x144.png │ │ ├── mstile-150x150.png │ │ ├── mstile-310x150.png │ │ ├── mstile-310x310.png │ │ ├── mstile-70x70.png │ │ ├── safari-pinned-tab.svg │ │ └── site.webmanifest │ ├── img │ │ ├── appaling-react-native-text-bg.png │ │ ├── article-6.2-release-notes.png │ │ ├── article-body-dark.png │ │ ├── article-body-light.png │ │ ├── article-create-webviewfree-blog-app.png │ │ ├── article-side-dark.png │ │ ├── article-side-light.png │ │ ├── banner.webp │ │ ├── blog-article-body-code-fixed.png │ │ ├── blog-article-body-code-issue.png │ │ ├── blog-article-body-refined.png │ │ ├── blog-article-body-unstyled.png │ │ ├── blog-article-home.png │ │ ├── blog-article-side.png │ │ ├── blog-expo-qr.png │ │ ├── discover-screenshot.jpeg │ │ ├── discover-screenshot.webp │ │ ├── discovery-expo-qr.png │ │ ├── foundry-announcement.png │ │ ├── home-dark.png │ │ ├── home-light.png │ │ ├── lists │ │ │ ├── circle.png │ │ │ ├── disc.png │ │ │ ├── disclosure-closed.png │ │ │ ├── disclosure-open.png │ │ │ ├── lower-alpha.png │ │ │ ├── lower-greek.png │ │ │ ├── lower-roman.png │ │ │ ├── numeric.png │ │ │ ├── rtl-arabic.png │ │ │ ├── rtl-bullet.png │ │ │ ├── russian.png │ │ │ ├── square.png │ │ │ └── thai.png │ │ ├── logo.svg │ │ ├── react-native-blog-anchors.png │ │ ├── tutorial │ │ │ ├── docsVersionDropdown.png │ │ │ └── localeDropdown.png │ │ ├── undraw_back_home.svg │ │ ├── undraw_certificate.svg │ │ ├── undraw_hacker_mind.svg │ │ ├── undraw_online_discussion.svg │ │ ├── undraw_relaxation.svg │ │ ├── undraw_security_on.svg │ │ └── undraw_static_assets.svg │ └── video │ │ └── discovery.webm │ ├── svgr.config.js │ └── tsconfig.json ├── assets ├── banner.svg ├── demo.gif ├── discovery-expo-qr.png ├── doc │ └── svg │ │ └── data-flow.svg ├── favicon.svg └── logo.svg ├── babel.config.js ├── commitlint.config.js ├── doc-tools ├── doc-constants │ ├── index.d.ts │ ├── index.js │ └── package.json ├── doc-docsusaurus-rfg-plugin │ ├── package.json │ ├── src │ │ └── index.ts │ └── tsconfig.json ├── doc-docusaurus-typedoc-plugin │ ├── package.json │ └── src │ │ ├── gen-pages.js │ │ ├── index.js │ │ └── typedoc-init.js ├── doc-mdx-gen-cli │ ├── .gitignore │ ├── build.js │ ├── package.json │ ├── src │ │ ├── MdxToolkitProvider.tsx │ │ ├── genPage.tsx │ │ ├── index.tsx │ │ └── render │ │ │ ├── MDXRenderer.ts │ │ │ ├── components │ │ │ ├── AdmonitionElement.ts │ │ │ ├── AnchorElement.ts │ │ │ ├── CodeBlockElement.ts │ │ │ ├── CodeElement.ts │ │ │ ├── H2Element.ts │ │ │ ├── H3Element.ts │ │ │ ├── HTMLElement.ts │ │ │ ├── MDXDocument.ts │ │ │ ├── NodeWithChildren.ts │ │ │ ├── ParagraphElement.ts │ │ │ ├── ReferenceElement.ts │ │ │ ├── RenderHTMLCardElement.ts │ │ │ ├── StrongElement.ts │ │ │ ├── SvgFigureElement.ts │ │ │ └── TNodeTransformDisplayElement.ts │ │ │ ├── createElement.ts │ │ │ ├── encodeAttributeVal.ts │ │ │ ├── index.ts │ │ │ └── renderMdx.ts │ └── tsconfig.json ├── doc-pages │ ├── .eslintignore │ ├── .gitignore │ ├── README.md │ ├── babel.config.js │ ├── jest.config.js │ ├── package.json │ ├── src │ │ ├── Page.tsx │ │ ├── __tests__ │ │ │ └── dummy.test.ts │ │ ├── acronymsIndex.ts │ │ ├── components │ │ │ ├── InternalRendererAdmonition.tsx │ │ │ ├── ListItemCode.tsx │ │ │ ├── RefCssProcessor.tsx │ │ │ ├── RefHtmlparser2.tsx │ │ │ ├── RefRNRH.tsx │ │ │ └── RefTRE.tsx │ │ ├── figuresIndex.ts │ │ ├── index.ts │ │ ├── pages-types.ts │ │ ├── pages │ │ │ ├── PageArchitecture.tsx │ │ │ ├── PageConceptCSS.tsx │ │ │ ├── PageConceptRendering.tsx │ │ │ ├── PageConceptTRE.tsx │ │ │ ├── PageContentAnchors.tsx │ │ │ ├── PageContentImages.tsx │ │ │ ├── PageContentLists.tsx │ │ │ ├── PageContentTextual.tsx │ │ │ ├── PageFAQ.tsx │ │ │ ├── PageGuideCustomRenderers.tsx │ │ │ ├── PageGuideDomTampering.tsx │ │ │ ├── PageGuideStylingComponents.tsx │ │ │ ├── PageIntroduction.tsx │ │ │ ├── PageReinventTheWheel.tsx │ │ │ └── cards │ │ │ │ ├── adsRenderersConfig.tsx │ │ │ │ ├── anchorsBlockConfig.tsx │ │ │ │ ├── anchorsCustomConfig.tsx │ │ │ │ ├── anchorsMinimalConfig.tsx │ │ │ │ ├── anchorsRelativeConfig.tsx │ │ │ │ ├── bluecircleConfig.tsx │ │ │ │ ├── cssInheritanceConfig.tsx │ │ │ │ ├── customImageRendererConfig.tsx │ │ │ │ ├── customListRussianConfig.tsx │ │ │ │ ├── customListThaiConfig.tsx │ │ │ │ ├── fontSelectionArial.tsx │ │ │ │ ├── fontSelectionCourierNewConfig.tsx │ │ │ │ ├── fontSelectionSpaceMonoConfig.tsx │ │ │ │ ├── ghostLineFixConfig.tsx │ │ │ │ ├── ghostLineOddityConfig.tsx │ │ │ │ ├── ignoreDomNodeConfig.tsx │ │ │ │ ├── imagesWithHeadersConfig.tsx │ │ │ │ ├── inlineImagesConfig.tsx │ │ │ │ ├── inlineStylesConfig.tsx │ │ │ │ ├── insertingElementConfig.tsx │ │ │ │ ├── internalImageRendererConfig.tsx │ │ │ │ ├── lineBreakBugConfig.tsx │ │ │ │ ├── lineBreakFixConfig.tsx │ │ │ │ ├── olUpperRomanConfig.tsx │ │ │ │ ├── paragraphsConfig.tsx │ │ │ │ ├── preformattedConfig.tsx │ │ │ │ ├── removeOlChildrenConfig.tsx │ │ │ │ ├── replaceDataConfig.tsx │ │ │ │ ├── rtlListArabicConfig.tsx │ │ │ │ ├── rtlListDiscConfig.tsx │ │ │ │ ├── selectDomRootConfig.tsx │ │ │ │ ├── selectableTextConfig.tsx │ │ │ │ ├── simpleCustomRenderersConfig.tsx │ │ │ │ ├── ulSquareConfig.tsx │ │ │ │ ├── whiteSpaceNormalConfig.tsx │ │ │ │ └── whiteSpacePreConfig.tsx │ │ ├── pagesSpecs.tsx │ │ └── toolkit │ │ │ ├── ToolkitProvider.tsx │ │ │ ├── defaultImports.ts │ │ │ ├── makeSnippet.tsx │ │ │ ├── toolkit-types.ts │ │ │ ├── toolkitContext.ts │ │ │ └── useToolkit.ts │ └── tsconfig.json ├── doc-svg-component │ ├── index.d.ts │ └── package.json └── doc-svgr-conf │ ├── index.js │ └── package.json ├── jest-config-base.js ├── package.json ├── packages └── render-html │ ├── .eslintrc.js │ ├── .gitignore │ ├── .release-it.js │ ├── CHANGELOG.md │ ├── api-extractor.json │ ├── babel.config.js │ ├── jest.config.js │ ├── jest │ ├── setup.ts │ └── setupAfterEnv.ts │ ├── package.json │ ├── scripts │ ├── post-release.sh │ └── prepack.sh │ ├── src │ ├── GenericPressable.tsx │ ├── RenderHTML.tsx │ ├── RenderHTMLConfigProvider.tsx │ ├── RenderHTMLDebug.tsx │ ├── RenderHTMLSource.tsx │ ├── RenderTTree.tsx │ ├── SourceLoaderDom.tsx │ ├── SourceLoaderInline.tsx │ ├── SourceLoaderUri.tsx │ ├── TChildrenRenderer.tsx │ ├── TDocumentRenderer.tsx │ ├── TNodeChildrenRenderer.tsx │ ├── TNodeRenderer.tsx │ ├── TRenderEngineProvider.tsx │ ├── __mocks__ │ │ └── react-native.js │ ├── __tests__ │ │ ├── __snapshots__ │ │ │ └── component.render-html.test.tsx.snap │ │ ├── component.generic-pressable.test.tsx │ │ ├── component.render-html-a11y.test.tsx │ │ ├── component.render-html-debug.test.tsx │ │ ├── component.render-html-source.test.tsx │ │ ├── component.render-html.test.tsx │ │ ├── component.tnoderenderer.test.tsx │ │ ├── regression.118.white-space-handling.test.tsx │ │ ├── regression.141.custom-blur-image.test.tsx │ │ ├── regression.172.custom-image-resize-mode.test.tsx │ │ ├── regression.193.text-selectable-prop.test.tsx │ │ ├── regression.252.css-text-align-rule.test.tsx │ │ ├── regression.257.css-display-rule.test.tsx │ │ ├── regression.266.css-regex.test.tsx │ │ ├── regression.276.cusom-renderer-wrapper.test.tsx │ │ ├── regression.319.css-border-rule.test.tsx │ │ ├── regression.343.tags-styles-re-render.test.tsx │ │ ├── regression.344.css-inline-inheritance-override.test.tsx │ │ ├── regression.361.css-margin-rule.test.tsx │ │ ├── regression.377.classes-styles-re-render.test.tsx │ │ ├── regression.383.html-bloated-anchor.test.tsx │ │ ├── regression.384.html-bloated-image.test.tsx │ │ ├── regression.408.img-url-two-forward-slashes.test.tsx │ │ ├── regression.524.img-enable-exp-percent-width.test.tsx │ │ ├── utils.tsx │ │ ├── w3c.css.box-model.test.tsx │ │ ├── w3c.css.inline.test.tsx │ │ ├── w3c.css.white-space.test.tsx │ │ └── w3c.html.pre.test.tsx │ ├── constants.ts │ ├── context │ │ ├── DocumentMetadataProvider.ts │ │ ├── ListStyleSpecsProvider.tsx │ │ ├── RenderRegistryProvider.tsx │ │ ├── RenderersPropsProvider.tsx │ │ ├── SharedPropsProvider.tsx │ │ ├── TChildrenRendererContext.ts │ │ ├── __tests__ │ │ │ ├── defaultRendererProps.test.ts │ │ │ └── useAmbientTRenderEngine.test.ts │ │ ├── contentWidthContext.ts │ │ ├── defaultRendererProps.ts │ │ ├── defaultSharedProps.ts │ │ ├── sourceLoaderContext.tsx │ │ └── ttreeEventsContext.ts │ ├── debugMessages.ts │ ├── defaultSystemFonts.android.ts │ ├── defaultSystemFonts.ios.ts │ ├── defaultSystemFonts.macos.ts │ ├── defaultSystemFonts.ts │ ├── defaultSystemFonts.windows.ts │ ├── elements │ │ ├── IMGElement.tsx │ │ ├── IMGElementContainer.tsx │ │ ├── IMGElementContentAlt.tsx │ │ ├── IMGElementContentError.tsx │ │ ├── IMGElementContentLoading.tsx │ │ ├── IMGElementContentSuccess.tsx │ │ ├── ListElement.tsx │ │ ├── OLElement.tsx │ │ ├── ULElement.tsx │ │ ├── __tests__ │ │ │ ├── IMGElement.test.tsx │ │ │ ├── ListElement.test.tsx │ │ │ ├── getStringPrefixFromIndex.test.ts │ │ │ ├── useIMGElementState.test.ts │ │ │ └── useIMGElementStateWithCache.test.ts │ │ ├── defaultInitialImageDimensions.ts │ │ ├── defaultListStyleSpecs.ts │ │ ├── extractImageStyleProps.ts │ │ ├── getDimensionsWithAspectRatio.ts │ │ ├── getIMGState.ts │ │ ├── getStringListPrefixFromIndex.ts │ │ ├── img-types.ts │ │ ├── symbolic │ │ │ ├── CircleSymbolRenderer.tsx │ │ │ ├── DiscSymbolRenderer.tsx │ │ │ ├── DisclosureClosedSymbolRenderer.tsx │ │ │ ├── DisclosureOpenSymbolRenderer.tsx │ │ │ ├── SquareSymbolRenderer.tsx │ │ │ └── useSymbolicMarkerRendererStyles.ts │ │ ├── useIMGElementState.ts │ │ ├── useIMGElementStateWithCache.ts │ │ ├── useIMGNormalizedSource.ts │ │ ├── useImageConcreteDimensions.ts │ │ └── useImageSpecifiedDimensions.ts │ ├── helpers │ │ ├── __tests__ │ │ │ ├── domNodeToHTMLString.test.ts │ │ │ ├── getCollapsedMargins.test.ts │ │ │ ├── normalizeResourceLocator.test.ts │ │ │ ├── selectSharedProps.test.ts │ │ │ └── splitBoxModelStyle.test.ts │ │ ├── buildTREFromConfig.ts │ │ ├── collapseTopMarginForChild.ts │ │ ├── domNodeToHTMLString.ts │ │ ├── getCollapsedMarginTop.ts │ │ ├── getNativePropsForTNode.ts │ │ ├── isDomSource.ts │ │ ├── isUriSource.ts │ │ ├── lookupRecord.ts │ │ ├── mergeCollapsedMargins.ts │ │ ├── normalizeResourceLocator.ts │ │ ├── selectSharedProps.ts │ │ └── splitBoxModelStyle.ts │ ├── hooks │ │ ├── useAssembledCommonProps.ts │ │ ├── useContentWidth.ts │ │ ├── useInternalRenderer.ts │ │ ├── useNormalizedUrl.ts │ │ ├── useProfiler.ts │ │ ├── useTRenderEngine.ts │ │ └── useTTree.ts │ ├── index.ts │ ├── internal-types.ts │ ├── render │ │ ├── RenderRegistry.ts │ │ ├── internalRenderers.ts │ │ └── render-types.tsx │ ├── renderBlockContent.tsx │ ├── renderChildren.tsx │ ├── renderEmptyContent.ts │ ├── renderTextualContent.tsx │ ├── renderers │ │ ├── ARenderer.tsx │ │ ├── BRRenderer.tsx │ │ ├── IMGRenderer.tsx │ │ ├── OLRenderer.tsx │ │ ├── ULRenderer.tsx │ │ └── WBRRenderer.tsx │ └── shared-types.ts │ ├── tsconfig.build.json │ └── tsconfig.json ├── patches └── react-native.patch ├── rfc ├── 001-A-deterministic-approach-to-embedded-content-scaling.adoc ├── 002-CSS-whitespace-collapsing.adoc └── img │ └── 001-problem-frame.svg ├── scripts └── processSvgAssets.js ├── tsconfig-base.json └── yarn.lock /.eslintignore: -------------------------------------------------------------------------------- 1 | /packages/legacy -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | extends: [ 4 | '@react-native-community', 5 | 'plugin:@typescript-eslint/recommended', 6 | 'prettier' 7 | ], 8 | parser: '@typescript-eslint/parser', 9 | plugins: ['@typescript-eslint'], 10 | ignorePatterns: ['lib/', 'types/', '*.js'], 11 | rules: { 12 | 'comma-dangle': ['error', 'never'], 13 | 'no-eval': 'off', 14 | '@typescript-eslint/no-unused-vars': [ 15 | 'error', 16 | { vars: 'all', args: 'after-used', ignoreRestSiblings: true } 17 | ], 18 | '@typescript-eslint/explicit-module-boundary-types': 0, 19 | '@typescript-eslint/no-explicit-any': 0, 20 | '@typescript-eslint/no-non-null-assertion': 0, 21 | '@typescript-eslint/no-empty-function': 0, 22 | '@typescript-eslint/ban-types': [ 23 | 'error', 24 | { 25 | types: { 26 | '{}': false 27 | } 28 | } 29 | ] 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: jsamr -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: true 2 | contact_links: 3 | - name: 📝 Feature Requests and Enhancements 4 | url: https://native-html.canny.io/features 5 | about: A page where all feature requests are handled. 6 | - name: 🆘 Getting Help 7 | url: https://github.com/meliorence/react-native-render-html/blob/master/HELP.adoc 8 | about: A resource which describes all the steps you should go through when you're facing an issue with this library. 9 | - name: 🌐 Official Website 10 | url: https://meliorence.github.io/react-native-render-html/ 11 | about: The official website with plenty of guides, blog posts and documentation! -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | ### Checks 7 | 8 | - [ ] I have read the contribution guidelines regarding Pull Requests here: https://git.io/JJ0Pg 9 | 10 | ### Description 11 | 12 | -------------------------------------------------------------------------------- /.github/workflows/npm.yml: -------------------------------------------------------------------------------- 1 | name: Npm 2 | on: 3 | workflow_dispatch: 4 | inputs: 5 | version: 6 | description: 'NPM exact version' 7 | required: true 8 | schedule: 9 | - cron: '0 0 * * *' 10 | 11 | jobs: 12 | build: 13 | runs-on: ubuntu-latest 14 | 15 | strategy: 16 | matrix: 17 | node-version: [18, 20, 22] 18 | 19 | steps: 20 | - name: Use Node.js ${{ matrix.node-version }} 21 | uses: actions/setup-node@v2 22 | with: 23 | node-version: ${{ matrix.node-version }} 24 | check-latest: true 25 | - name: Update NPM to latest 26 | run: npm --version;npm install -g npm;npm --version 27 | - name: Install React Native CLI 28 | run: npm install -g @react-native-community/cli 29 | - name: Init new project 30 | run: react-native init --npm test; cd test 31 | - name: 'Install version ${{ github.event.inputs.version }}' 32 | run: 'npm install react-native-render-html@${{ github.event.inputs.version }}' 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # Xcode 6 | # 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | project.xcworkspace 24 | 25 | # Android/IJ 26 | # 27 | .idea 28 | .gradle 29 | local.properties 30 | 31 | # Vscode 32 | # 33 | .vscode/* 34 | !.vscode/extensions.json 35 | !.vscode/ltex.dictionary.en-US.txt 36 | !.vscode/settings.json 37 | 38 | # node.js 39 | # 40 | node_modules/ 41 | npm-debug.log 42 | 43 | # Todos 44 | 45 | *.todo 46 | 47 | # yarn berry 48 | .yarn/* 49 | !.yarn/releases 50 | !.yarn/plugins 51 | !.yarn/sdks 52 | !.yarn/versions 53 | 54 | # Specific to this project 55 | coverage/ 56 | lib/ 57 | test.html 58 | 59 | # old sources 60 | /src/ 61 | /Demo/ 62 | /types/ 63 | -------------------------------------------------------------------------------- /.husky/.gitignore: -------------------------------------------------------------------------------- 1 | _ 2 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npx --no-install commitlint --edit "" 5 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | /packages/legacy -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | bracketSpacing: true, 3 | jsxBracketSameLine: true, 4 | singleQuote: true, 5 | trailingComma: 'none', 6 | }; 7 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "esbenp.prettier-vscode", 4 | "dbaeumer.vscode-eslint", 5 | "asciidoctor.asciidoctor-vscode", 6 | "valentjn.vscode-ltex" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /.vscode/ltex.dictionary.en-US.txt: -------------------------------------------------------------------------------- 1 | changelog 2 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "node_modules/typescript/lib", 3 | "ltex.enabled": ["html", "markdown", "asciidoc"], 4 | "ltex.language": "en-US", 5 | } -------------------------------------------------------------------------------- /.yarnrc.yml: -------------------------------------------------------------------------------- 1 | enableGlobalCache: true 2 | 3 | nodeLinker: node-modules 4 | 5 | plugins: 6 | - path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs 7 | spec: "@yarnpkg/plugin-interactive-tools" 8 | 9 | yarnPath: .yarn/releases/yarn-3.2.4.cjs 10 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | packages/render-html/CHANGELOG.md -------------------------------------------------------------------------------- /ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /apps/benchmarking/.expo-shared/assets.json: -------------------------------------------------------------------------------- 1 | { 2 | "12bb71342c6255bbf50437ec8f4441c083f47cdb74bd89160c15e4f43e52a1cb": true, 3 | "40b842e832070c58deac6aa9e08fa459302ee3f9da492c7e77d93d2fbf4a56fd": true 4 | } 5 | -------------------------------------------------------------------------------- /apps/benchmarking/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .expo/ 3 | npm-debug.* 4 | *.jks 5 | *.p8 6 | *.p12 7 | *.key 8 | *.mobileprovision 9 | *.orig.* 10 | web-build/ 11 | 12 | # macOS 13 | .DS_Store 14 | -------------------------------------------------------------------------------- /apps/benchmarking/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { SafeAreaView } from 'react-native-safe-area-context'; 3 | import { useKeepAwake } from 'expo-keep-awake'; 4 | import html from './source'; 5 | import { 6 | TRenderEngineProvider, 7 | RenderHTMLConfigProvider 8 | } from 'react-native-render-html'; 9 | 10 | import Benchmark from './Benchmark'; 11 | 12 | const config = { 13 | samples: 50, 14 | ignoredTags: ['svg', 'button', 'input', 'form', 'img', 'ol', 'table'] 15 | }; 16 | 17 | const props = { 18 | renderers: {} 19 | }; 20 | 21 | export default function App() { 22 | useKeepAwake(); 23 | return ( 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | ); 32 | } 33 | -------------------------------------------------------------------------------- /apps/benchmarking/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "expo": { 3 | "name": "benchmarking", 4 | "slug": "benchmarking", 5 | "version": "1.0.0", 6 | "orientation": "portrait", 7 | "icon": "./assets/icon.png", 8 | "splash": { 9 | "image": "./assets/splash.png", 10 | "resizeMode": "contain", 11 | "backgroundColor": "#ffffff" 12 | }, 13 | "updates": { 14 | "fallbackToCacheTimeout": 0 15 | }, 16 | "assetBundlePatterns": [ 17 | "**/*" 18 | ], 19 | "ios": { 20 | "supportsTablet": true, 21 | "bundleIdentifier": "org.nativehtml.benchmarking" 22 | }, 23 | "android": { 24 | "adaptiveIcon": { 25 | "foregroundImage": "./assets/adaptive-icon.png", 26 | "backgroundColor": "#FFFFFF" 27 | }, 28 | "package": "org.nativehtml.benchmarking" 29 | }, 30 | "web": { 31 | "favicon": "./assets/favicon.png" 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /apps/benchmarking/assets/adaptive-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meliorence/react-native-render-html/8325646fc8b39cf4ec8576b297a718ad2e1c44bc/apps/benchmarking/assets/adaptive-icon.png -------------------------------------------------------------------------------- /apps/benchmarking/assets/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meliorence/react-native-render-html/8325646fc8b39cf4ec8576b297a718ad2e1c44bc/apps/benchmarking/assets/favicon.png -------------------------------------------------------------------------------- /apps/benchmarking/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meliorence/react-native-render-html/8325646fc8b39cf4ec8576b297a718ad2e1c44bc/apps/benchmarking/assets/icon.png -------------------------------------------------------------------------------- /apps/benchmarking/assets/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meliorence/react-native-render-html/8325646fc8b39cf4ec8576b297a718ad2e1c44bc/apps/benchmarking/assets/splash.png -------------------------------------------------------------------------------- /apps/benchmarking/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function(api) { 2 | api.cache(true); 3 | return { 4 | presets: ['babel-preset-expo'], 5 | }; 6 | }; 7 | -------------------------------------------------------------------------------- /apps/benchmarking/eas.json: -------------------------------------------------------------------------------- 1 | { 2 | "build": { 3 | "development": { 4 | "developmentClient": true, 5 | "distribution": "internal" 6 | }, 7 | "preview": { 8 | "android": { 9 | "buildType": "apk" 10 | }, 11 | "distribution": "internal" 12 | }, 13 | "production": { 14 | "env": { 15 | "YARN_ENABLE_IMMUTABLE_INSTALLS": "false" 16 | } 17 | } 18 | }, 19 | "submit": { 20 | "production": {} 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /apps/benchmarking/metro.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const fs = require('fs'); 3 | const { getDefaultConfig } = require('expo/metro-config'); 4 | 5 | const packagesRoot = path.resolve(__dirname, '../../packages'); 6 | const docToolsRoot = path.resolve(__dirname, '../../doc-tools'); 7 | 8 | const localPkgs = fs.readdirSync(packagesRoot); 9 | const docToolksPkgs = fs.readdirSync(docToolsRoot); 10 | 11 | const watchFolders = localPkgs 12 | .map((f) => path.join(packagesRoot, f)) 13 | .concat(docToolksPkgs.map((f) => path.join(docToolsRoot, f))); 14 | 15 | module.exports = (async () => { 16 | const config = await getDefaultConfig(__dirname); 17 | return { 18 | ...config, 19 | watchFolders, 20 | resolver: { 21 | extraNodeModules: new Proxy( 22 | {}, 23 | { 24 | get: (target, name) => path.join(__dirname, `node_modules/${name}`) 25 | } 26 | ) 27 | } 28 | }; 29 | })(); 30 | -------------------------------------------------------------------------------- /apps/benchmarking/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "main": "node_modules/expo/AppEntry.js", 3 | "name": "benchmarking", 4 | "scripts": { 5 | "start": "expo start", 6 | "android": "expo start --android", 7 | "ios": "expo start --ios", 8 | "web": "expo start --web", 9 | "eject": "expo eject" 10 | }, 11 | "dependencies": { 12 | "expo": "^43.0.0", 13 | "expo-keep-awake": "~10.0.0", 14 | "expo-status-bar": "~1.1.0", 15 | "ramda": "^0.27.2", 16 | "react": "17.0.1", 17 | "react-dom": "17.0.1", 18 | "react-native": "0.64.2", 19 | "react-native-render-html": "workspace:*", 20 | "react-native-render-html-v5": "npm:react-native-render-html@^5.0.0", 21 | "react-native-safe-area-context": "3.3.2", 22 | "react-native-web": "0.17.1", 23 | "react-states": "^5.4.0" 24 | }, 25 | "devDependencies": { 26 | "@babel/core": "^7.12.9" 27 | }, 28 | "private": true, 29 | "installConfig": { 30 | "hoistingLimits": "workspaces" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /apps/benchmarking/profiles.js: -------------------------------------------------------------------------------- 1 | // import ProfileSimple from './profiles/ProfileSimple'; 2 | import ProfileV5 from './profiles/ProfileV5'; 3 | import ProfileV6Source from './profiles/ProfileV6Source'; 4 | 5 | const profiles = [ 6 | // Disabled because of htmlparser resolution 7 | // { 8 | // name: 'Simple implementation', 9 | // component: ProfileSimple 10 | // }, 11 | { 12 | name: 'V5', 13 | component: ProfileV5 14 | }, 15 | { 16 | name: 'V6', 17 | component: ProfileV6Source 18 | } 19 | ]; 20 | 21 | export default profiles; 22 | -------------------------------------------------------------------------------- /apps/benchmarking/profiles/ProfileSimple.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import RenderHtmlSimple from './RenderHtmlSimple'; 3 | 4 | export default function ProfileSimple({ running, html, ignoredTags }) { 5 | return ( 6 | html && 7 | running && 8 | ); 9 | } 10 | -------------------------------------------------------------------------------- /apps/benchmarking/profiles/ProfileV5.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import RenderHtmlv5 from 'react-native-render-html-v5'; 3 | 4 | export default function ProfileV5({ running, ignoredTags, html }) { 5 | return ( 6 | html && 7 | running && 8 | ); 9 | } 10 | -------------------------------------------------------------------------------- /apps/benchmarking/profiles/ProfileV6Source.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useWindowDimensions } from 'react-native'; 3 | import { RenderHTMLSource } from 'react-native-render-html'; 4 | 5 | export default function ProfileV6Source({ running, html, ignoredTags, props }) { 6 | const { width } = useWindowDimensions(); 7 | return ( 8 | html && 9 | running && ( 10 | 16 | ) 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /apps/discovery/.expo-shared/assets.json: -------------------------------------------------------------------------------- 1 | { 2 | "e997a5256149a4b76e6bfd6cbf519c5e5a0f1d278a3d8fa1253022b03c90473b": true, 3 | "af683c96e0ffd2cf81287651c9433fa44debc1220ca7cb431fe482747f34a505": true, 4 | "12bb71342c6255bbf50437ec8f4441c083f47cdb74bd89160c15e4f43e52a1cb": true, 5 | "40b842e832070c58deac6aa9e08fa459302ee3f9da492c7e77d93d2fbf4a56fd": true 6 | } 7 | -------------------------------------------------------------------------------- /apps/discovery/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/**/* 2 | .expo 3 | npm-debug.* 4 | *.jks 5 | *.p8 6 | *.p12 7 | *.key 8 | *.mobileprovision 9 | *.orig.* 10 | web-build/ 11 | 12 | # macOS 13 | .DS_Store 14 | -------------------------------------------------------------------------------- /apps/discovery/app.config.js: -------------------------------------------------------------------------------- 1 | const version = require('./version').demo; 2 | module.exports = { 3 | expo: { 4 | name: 'RNRH Discovery', 5 | slug: 'react-native-render-html-discovery', 6 | description: 7 | 'An App to discover React Native Render HTML features and API!', 8 | version: version, 9 | primaryColor: '#6767e2', 10 | orientation: 'default', 11 | icon: './assets/images/icon.png', 12 | scheme: 'myapp', 13 | userInterfaceStyle: 'automatic', 14 | splash: { 15 | backgroundColor: '#6767e2' 16 | }, 17 | updates: { 18 | fallbackToCacheTimeout: 0 19 | }, 20 | assetBundlePatterns: ['**/*'], 21 | ios: { 22 | supportsTablet: false, 23 | bundleIdentifier: 'org.nativehtml.discovery', 24 | buildNumber: '1.0.0' 25 | }, 26 | android: { 27 | package: 'org.nativehtml.discovery', 28 | versionCode: 1 29 | }, 30 | web: { 31 | favicon: './assets/images/favicon.png' 32 | } 33 | } 34 | }; 35 | -------------------------------------------------------------------------------- /apps/discovery/assets/fonts/SpaceMono-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meliorence/react-native-render-html/8325646fc8b39cf4ec8576b297a718ad2e1c44bc/apps/discovery/assets/fonts/SpaceMono-Regular.ttf -------------------------------------------------------------------------------- /apps/discovery/assets/images/anders-jilden-architecture.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meliorence/react-native-render-html/8325646fc8b39cf4ec8576b297a718ad2e1c44bc/apps/discovery/assets/images/anders-jilden-architecture.jpg -------------------------------------------------------------------------------- /apps/discovery/assets/images/bank-phrom-rendering.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meliorence/react-native-render-html/8325646fc8b39cf4ec8576b297a718ad2e1c44bc/apps/discovery/assets/images/bank-phrom-rendering.jpg -------------------------------------------------------------------------------- /apps/discovery/assets/images/diana-polekhina-tampering.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meliorence/react-native-render-html/8325646fc8b39cf4ec8576b297a718ad2e1c44bc/apps/discovery/assets/images/diana-polekhina-tampering.jpg -------------------------------------------------------------------------------- /apps/discovery/assets/images/fallback.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meliorence/react-native-render-html/8325646fc8b39cf4ec8576b297a718ad2e1c44bc/apps/discovery/assets/images/fallback.png -------------------------------------------------------------------------------- /apps/discovery/assets/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meliorence/react-native-render-html/8325646fc8b39cf4ec8576b297a718ad2e1c44bc/apps/discovery/assets/images/favicon.png -------------------------------------------------------------------------------- /apps/discovery/assets/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meliorence/react-native-render-html/8325646fc8b39cf4ec8576b297a718ad2e1c44bc/apps/discovery/assets/images/icon.png -------------------------------------------------------------------------------- /apps/discovery/assets/images/james-barnett-script.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meliorence/react-native-render-html/8325646fc8b39cf4ec8576b297a718ad2e1c44bc/apps/discovery/assets/images/james-barnett-script.jpg -------------------------------------------------------------------------------- /apps/discovery/assets/images/jj-ying-anchors.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meliorence/react-native-render-html/8325646fc8b39cf4ec8576b297a718ad2e1c44bc/apps/discovery/assets/images/jj-ying-anchors.jpg -------------------------------------------------------------------------------- /apps/discovery/assets/images/jon-cartagena-wheel.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meliorence/react-native-render-html/8325646fc8b39cf4ec8576b297a718ad2e1c44bc/apps/discovery/assets/images/jon-cartagena-wheel.jpg -------------------------------------------------------------------------------- /apps/discovery/assets/images/maik-jonietz-css.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meliorence/react-native-render-html/8325646fc8b39cf4ec8576b297a718ad2e1c44bc/apps/discovery/assets/images/maik-jonietz-css.jpg -------------------------------------------------------------------------------- /apps/discovery/assets/images/malcolm-lightbody-custom-rendering.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meliorence/react-native-render-html/8325646fc8b39cf4ec8576b297a718ad2e1c44bc/apps/discovery/assets/images/malcolm-lightbody-custom-rendering.jpg -------------------------------------------------------------------------------- /apps/discovery/assets/images/markus-winkler-lists.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meliorence/react-native-render-html/8325646fc8b39cf4ec8576b297a718ad2e1c44bc/apps/discovery/assets/images/markus-winkler-lists.jpg -------------------------------------------------------------------------------- /apps/discovery/assets/images/pankaj-patel-html.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meliorence/react-native-render-html/8325646fc8b39cf4ec8576b297a718ad2e1c44bc/apps/discovery/assets/images/pankaj-patel-html.jpg -------------------------------------------------------------------------------- /apps/discovery/assets/images/russn-fckr-painting.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meliorence/react-native-render-html/8325646fc8b39cf4ec8576b297a718ad2e1c44bc/apps/discovery/assets/images/russn-fckr-painting.jpg -------------------------------------------------------------------------------- /apps/discovery/assets/images/ryan-baker-tre.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meliorence/react-native-render-html/8325646fc8b39cf4ec8576b297a718ad2e1c44bc/apps/discovery/assets/images/ryan-baker-tre.jpg -------------------------------------------------------------------------------- /apps/discovery/assets/images/simone-secci-question.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meliorence/react-native-render-html/8325646fc8b39cf4ec8576b297a718ad2e1c44bc/apps/discovery/assets/images/simone-secci-question.jpg -------------------------------------------------------------------------------- /apps/discovery/assets/images/soragrit-wongsa-pictures.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meliorence/react-native-render-html/8325646fc8b39cf4ec8576b297a718ad2e1c44bc/apps/discovery/assets/images/soragrit-wongsa-pictures.jpg -------------------------------------------------------------------------------- /apps/discovery/assets/svg/.gitignore: -------------------------------------------------------------------------------- 1 | *.svg 2 | !logo.svg -------------------------------------------------------------------------------- /apps/discovery/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function (api) { 2 | api.cache(true); 3 | return { 4 | presets: ['babel-preset-expo'], 5 | plugins: ['react-native-reanimated/plugin'] 6 | }; 7 | }; 8 | -------------------------------------------------------------------------------- /apps/discovery/eas.json: -------------------------------------------------------------------------------- 1 | { 2 | "build": { 3 | "development": { 4 | "developmentClient": true, 5 | "distribution": "internal" 6 | }, 7 | "preview": { 8 | "android": { 9 | "buildType": "apk" 10 | }, 11 | "distribution": "internal" 12 | }, 13 | "production": { 14 | "env": { 15 | "YARN_ENABLE_IMMUTABLE_INSTALLS": "false" 16 | } 17 | } 18 | }, 19 | "submit": { 20 | "production": {} 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /apps/discovery/index.js: -------------------------------------------------------------------------------- 1 | import { registerRootComponent } from 'expo'; 2 | import App from './App'; 3 | 4 | registerRootComponent(App); 5 | -------------------------------------------------------------------------------- /apps/discovery/src/components/BodyDividerAtom.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { View } from 'react-native'; 3 | import { useColorRoles } from '../theme/colorSystem'; 4 | import { PropsWithStyle } from './nucleons/types'; 5 | 6 | export default function BodyDividerAtom({ 7 | color, 8 | height = 1, 9 | width = '100%' 10 | }: PropsWithStyle<{ 11 | color?: string; 12 | height?: number; 13 | width?: number | string; 14 | }>) { 15 | const { softDivider } = useColorRoles(); 16 | return ( 17 | 24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /apps/discovery/src/components/BodyListItemAtom.tsx: -------------------------------------------------------------------------------- 1 | import React, { PropsWithChildren } from 'react'; 2 | import TextRoleNucleon from './nucleons/TextRoleNucleon'; 3 | 4 | export default function BodyListItemAtom({ children }: PropsWithChildren<{}>) { 5 | return ( 6 | 7 | {children} 8 | 9 | ); 10 | } 11 | -------------------------------------------------------------------------------- /apps/discovery/src/components/BodyParagraphAtom.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { BODY_HZ_SPACING } from '../constants'; 3 | import BoxNucleon from './nucleons/BoxNucleon'; 4 | import TextRoleNucleon, { 5 | TextRoleNucleonProps 6 | } from './nucleons/TextRoleNucleon'; 7 | 8 | export default function BodyParagraphAtom( 9 | props: Omit 10 | ) { 11 | return ( 12 | 13 | 14 | 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /apps/discovery/src/components/MaxWidthContainerAtom.tsx: -------------------------------------------------------------------------------- 1 | import React, { PropsWithChildren } from 'react'; 2 | import { View } from 'react-native'; 3 | import contentWidthContextNucleon from './nucleons/contentWidthContextNucleon'; 4 | import { PropsWithStyle } from './nucleons/types'; 5 | import { useNuclearContentWidth } from './nucleons/useContentWidthContext'; 6 | 7 | export default function MaxWidthContainerAtom({ 8 | children, 9 | style 10 | }: PropsWithStyle>) { 11 | const width = useNuclearContentWidth(); 12 | const containerWidth = Math.min(width, 650); 13 | return ( 14 | 15 | 16 | {children} 17 | 18 | 19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /apps/discovery/src/components/TideListAtom.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Stack, useSpacing } from '@mobily/stacks'; 3 | import { PropsWithChildren } from 'react'; 4 | import { StyleProp, ViewStyle } from 'react-native'; 5 | import BoxNucleon from './nucleons/BoxNucleon'; 6 | import { useNuclearContentWidth } from './nucleons/useContentWidthContext'; 7 | import contentWidthContextNucleon from './nucleons/contentWidthContextNucleon'; 8 | 9 | export default function UITideListAtom({ 10 | children, 11 | spaces = 0, 12 | style 13 | }: PropsWithChildren<{ style?: StyleProp; spaces?: number }>) { 14 | const contentWidth = useNuclearContentWidth(); 15 | const controlContentWidth = contentWidth - useSpacing(2 * spaces); 16 | return ( 17 | 18 | 19 | {children} 20 | 21 | 22 | ); 23 | } 24 | -------------------------------------------------------------------------------- /apps/discovery/src/components/UIAppbarActionAtom.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Appbar } from 'react-native-paper'; 3 | import { useColorRoles } from '../theme/colorSystem'; 4 | 5 | export type UIAppbarActionAtomProps = React.ComponentProps< 6 | typeof Appbar.Action 7 | >; 8 | 9 | export default function UIAppbarActionAtom({ 10 | color, 11 | rippleColor, 12 | ...props 13 | }: UIAppbarActionAtomProps) { 14 | const { pressable } = useColorRoles(); 15 | return ( 16 | 21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /apps/discovery/src/components/UIAppbarContentAtom.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { StyleSheet } from 'react-native'; 3 | import { Appbar } from 'react-native-paper'; 4 | import { useColorRoles } from '../theme/colorSystem'; 5 | import useTextRoleNucleon from './nucleons/useTextRoleNucleon'; 6 | 7 | export type UIAppbarContentAtomProps = Omit< 8 | React.ComponentProps, 9 | 'subtitleStyle' | 'color' 10 | >; 11 | 12 | const styles = StyleSheet.create({ 13 | title: { 14 | letterSpacing: 1.5 15 | } 16 | }); 17 | 18 | export default function UIAppbarContentAtom(props: UIAppbarContentAtomProps) { 19 | const { surface } = useColorRoles(); 20 | const subtitleStyle = useTextRoleNucleon({ 21 | role: 'headerSubtitle', 22 | color: surface.content 23 | }); 24 | const titleStyle = useTextRoleNucleon({ 25 | role: 'headerTitle', 26 | color: surface.content 27 | }); 28 | return ( 29 | 35 | ); 36 | } 37 | -------------------------------------------------------------------------------- /apps/discovery/src/components/UIBidirectionalScrollViewAtom.tsx: -------------------------------------------------------------------------------- 1 | import React, { PropsWithChildren } from 'react'; 2 | import { ScrollView } from 'react-native-gesture-handler'; 3 | 4 | // TODO use spacing 5 | 6 | export default function UIBidirectionalScrollViewAtom({ 7 | children, 8 | padding 9 | }: PropsWithChildren<{ padding?: number }>) { 10 | return ( 11 | 15 | 18 | {children} 19 | 20 | 21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /apps/discovery/src/components/UIDisplayLoadingAtom.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { StyleProp, ViewStyle } from 'react-native'; 3 | import { ActivityIndicator } from 'react-native-paper'; 4 | import { useColorRoles } from '../theme/colorSystem'; 5 | import BoxNucleon from './nucleons/BoxNucleon'; 6 | 7 | export default function UIDisplayLoadingAtom({ 8 | style 9 | }: { 10 | style?: StyleProp; 11 | }) { 12 | const { spinnerColor } = useColorRoles(); 13 | return ( 14 | 15 | 16 | 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /apps/discovery/src/components/UIFigureTitleAtom.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { PropsWithChildren } from 'react'; 3 | import { useColorPrimitives } from '../theme/colorSystem'; 4 | import BoxNucleon from './nucleons/BoxNucleon'; 5 | import TextRoleNucleon from './nucleons/TextRoleNucleon'; 6 | import { PropsWithStyle } from './nucleons/types'; 7 | 8 | export default function UIFigureTitleAtom( 9 | props: PropsWithStyle> 10 | ) { 11 | const { accent } = useColorPrimitives(); 12 | return ( 13 | 23 | 29 | {props.children} 30 | 31 | 32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /apps/discovery/src/components/UIHyperlinkAtom.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useColorRoles } from '../theme/colorSystem'; 3 | import { TextRoleNucleonProps } from './nucleons/TextRoleNucleon'; 4 | import useTextRoleNucleon from './nucleons/useTextRoleNucleon'; 5 | import { Text } from 'react-native'; 6 | import { NativeViewGestureHandler } from 'react-native-gesture-handler'; 7 | 8 | export default function UIHyperlinkAtom({ 9 | children, 10 | role = 'bodyInlineCode', 11 | color, 12 | ...props 13 | }: Partial) { 14 | const { hyperlinkColor } = useColorRoles(); 15 | const textStyle = useTextRoleNucleon({ 16 | color: color || hyperlinkColor, 17 | role 18 | }); 19 | return ( 20 | 21 | 22 | 23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /apps/discovery/src/components/UISnackbarAtom.tsx: -------------------------------------------------------------------------------- 1 | import React, { ComponentProps } from 'react'; 2 | import { Falsy } from 'react-native'; 3 | import { Snackbar } from 'react-native-paper'; 4 | import { useColorPrimitives } from '../theme/colorSystem'; 5 | import TextRoleNucleon, { 6 | TextRoleNucleonProps 7 | } from './nucleons/TextRoleNucleon'; 8 | 9 | export type UISnackbarAtomProps = Omit< 10 | ComponentProps, 11 | 'style' | 'children' 12 | > & { 13 | children?: string | Falsy; 14 | textProps?: TextRoleNucleonProps; 15 | }; 16 | 17 | export default function UISnackbarAtom({ 18 | children, 19 | textProps, 20 | role = 'uiLabel', 21 | ...props 22 | }: UISnackbarAtomProps & Pick, 'role'>) { 23 | const { surface } = useColorPrimitives(); 24 | return ( 25 | 26 | 27 | {children} 28 | 29 | 30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /apps/discovery/src/components/UISwitchControlAtom.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { SwitchProps, Switch, AccessibilityProps } from 'react-native'; 3 | import { NativeViewGestureHandler } from 'react-native-gesture-handler'; 4 | import { useColorRoles } from '../theme/colorSystem'; 5 | 6 | export type UISwitchControlAtomProps = Omit< 7 | SwitchProps, 8 | 'tintColor' | 'trackColor' | 'thumbColor' 9 | > & 10 | AccessibilityProps; 11 | 12 | export default function UISwitchControlAtom({ 13 | value, 14 | ...switchProps 15 | }: UISwitchControlAtomProps) { 16 | const { switchColor, trackColor } = useColorRoles(); 17 | return ( 18 | 19 | 28 | 29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /apps/discovery/src/components/UISwitchTideMolecule.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { StyleProp, ViewStyle } from 'react-native'; 3 | import UITideAtom, { UITideAtomProps } from './UITideAtom'; 4 | import UISwitchControlAtom, { 5 | UISwitchControlAtomProps 6 | } from './UISwitchControlAtom'; 7 | 8 | export type UISwitchTideMoleculeProps = Omit< 9 | UISwitchControlAtomProps, 10 | 'style' 11 | > & { 12 | style?: StyleProp; 13 | leftIconName: UITideAtomProps['leftIconName']; 14 | label: string; 15 | }; 16 | 17 | export default function UISwitchTideMolecule({ 18 | style, 19 | label, 20 | leftIconName, 21 | ...switchProps 22 | }: UISwitchTideMoleculeProps) { 23 | const right = () => ; 24 | const onPress = () => { 25 | switchProps.onValueChange?.(!switchProps.value); 26 | }; 27 | return ( 28 | 35 | ); 36 | } 37 | -------------------------------------------------------------------------------- /apps/discovery/src/components/UITTreeDisplayMolecule.tsx: -------------------------------------------------------------------------------- 1 | import React, { useMemo } from 'react'; 2 | import { TNode } from 'react-native-render-html'; 3 | import { HighlighterProps } from '../highlight/Highlighter'; 4 | import { TextRoleNucleonProps } from './nucleons/TextRoleNucleon'; 5 | import UISourceDisplayMolecule from './UISourceDisplayMolecule'; 6 | 7 | export default function UITTreeDisplayMolecule({ 8 | ttree, 9 | ...props 10 | }: { 11 | ttree?: TNode; 12 | style?: HighlighterProps['style']; 13 | language?: HighlighterProps['language']; 14 | paddingVertical?: number; 15 | clipLines?: boolean; 16 | textRole?: TextRoleNucleonProps['role']; 17 | }) { 18 | const xml = useMemo(() => ttree?.snapshot(), [ttree]); 19 | return xml ? ( 20 | 26 | ) : null; 27 | } 28 | -------------------------------------------------------------------------------- /apps/discovery/src/components/croles/CardColorRolesProvider.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { 3 | ColorPrimitivesDeclaration, 4 | ColorRoles, 5 | ColorRolesProvider 6 | } from '../../theme/colorSystem'; 7 | import { PropsWithChildren } from 'react'; 8 | import generateColorRoles from '../../theme/generateColorRoles'; 9 | 10 | function mapPrimitivesColorRoles( 11 | primitives: ColorPrimitivesDeclaration 12 | ): ColorRoles { 13 | const { card } = primitives; 14 | return generateColorRoles({ 15 | name: 'card', 16 | surfaceColor: card.color, 17 | surfaceContent: card.content, 18 | primitives 19 | }); 20 | } 21 | 22 | export default function CardColorRolesProvider({ 23 | children 24 | }: PropsWithChildren<{}>) { 25 | return ( 26 | 27 | {children} 28 | 29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /apps/discovery/src/components/croles/DefaultColorRolesProvider.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { 3 | ColorPrimitivesDeclaration, 4 | ColorRoles, 5 | ColorRolesProvider 6 | } from '../../theme/colorSystem'; 7 | import { PropsWithChildren } from 'react'; 8 | import generateColorRoles from '../../theme/generateColorRoles'; 9 | 10 | function mapPrimitivesColorRoles( 11 | primitives: ColorPrimitivesDeclaration 12 | ): ColorRoles { 13 | const { surface } = primitives; 14 | return generateColorRoles({ 15 | name: 'default', 16 | surfaceColor: surface.color, 17 | surfaceContent: surface.content, 18 | primitives 19 | }); 20 | } 21 | 22 | export default function DefaultColorRolesProvider({ 23 | children 24 | }: PropsWithChildren<{}>) { 25 | return ( 26 | 27 | {children} 28 | 29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /apps/discovery/src/components/croles/HeaderColorRolesProvider.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { 3 | ColorPrimitivesDeclaration, 4 | ColorRoles, 5 | ColorRolesProvider 6 | } from '../../theme/colorSystem'; 7 | import { PropsWithChildren } from 'react'; 8 | import generateColorRoles from '../../theme/generateColorRoles'; 9 | 10 | function mapPrimitivesColorRoles( 11 | primitives: ColorPrimitivesDeclaration 12 | ): ColorRoles { 13 | const { heading } = primitives; 14 | return generateColorRoles({ 15 | name: 'header', 16 | surfaceColor: heading.content, 17 | surfaceContent: heading.color, 18 | primitives 19 | }); 20 | } 21 | 22 | export default function HeaderColorRolesProvider({ 23 | children 24 | }: PropsWithChildren<{}>) { 25 | return ( 26 | 27 | {children} 28 | 29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /apps/discovery/src/components/nucleons/BoxNucleon.tsx: -------------------------------------------------------------------------------- 1 | import React, { useMemo } from 'react'; 2 | import { Box, BoxProps } from '@mobily/stacks'; 3 | import { StyleProp, ViewStyle } from 'react-native'; 4 | 5 | export type BoxNucleonProps = BoxProps & { 6 | backgroundColor?: string; 7 | grow?: boolean; 8 | }; 9 | 10 | export default function BoxNucleon({ 11 | style, 12 | backgroundColor, 13 | grow, 14 | ...props 15 | }: BoxNucleonProps) { 16 | const syntheticStyle: StyleProp[] = useMemo( 17 | () => [{ backgroundColor, flexGrow: grow ? 1 : 0 }, style], 18 | [backgroundColor, grow, style] 19 | ); 20 | const box = ; 21 | // if (typeof color === 'string') { 22 | // return ( 23 | // {box} 24 | // ); 25 | // } 26 | return box; 27 | } 28 | -------------------------------------------------------------------------------- /apps/discovery/src/components/nucleons/IconNucleon.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { MaterialCommunityIcons } from '@expo/vector-icons'; 3 | import { ComponentProps } from 'react'; 4 | import { useColorRoles } from '../../theme/colorSystem'; 5 | 6 | export type IconName = ComponentProps['name']; 7 | 8 | export type IconNucleonProps = ComponentProps; 9 | 10 | export default function IconNucleon(props: IconNucleonProps) { 11 | const { softIconColor } = useColorRoles(); 12 | return ; 13 | } 14 | -------------------------------------------------------------------------------- /apps/discovery/src/components/nucleons/TextRoleNucleon.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Text as NativeText } from 'react-native'; 3 | import { TextProps } from 'react-native'; 4 | import useTextRoleNucleon, { 5 | TextRoleNucleonProps as T 6 | } from './useTextRoleNucleon'; 7 | 8 | export type TextRoleNucleonProps = React.PropsWithChildren; 9 | 10 | export default function TextRoleNucleon({ 11 | style, 12 | ...props 13 | }: TextRoleNucleonProps) { 14 | const generatedStyle = useTextRoleNucleon(props); 15 | return ( 16 | 21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /apps/discovery/src/components/nucleons/contentWidthContextNucleon.ts: -------------------------------------------------------------------------------- 1 | import { createContext } from 'react'; 2 | 3 | const contentWidthContextNucleon = createContext(0); 4 | 5 | export default contentWidthContextNucleon; 6 | -------------------------------------------------------------------------------- /apps/discovery/src/components/nucleons/gestureHandlerContextNucleon.ts: -------------------------------------------------------------------------------- 1 | import { createContext } from 'react'; 2 | 3 | const gestureHandlerContextNucleon = createContext(false); 4 | 5 | export default gestureHandlerContextNucleon; 6 | -------------------------------------------------------------------------------- /apps/discovery/src/components/nucleons/types.ts: -------------------------------------------------------------------------------- 1 | import { StyleProp, TextProps, ViewStyle } from 'react-native'; 2 | import { TextRoleNucleonProps } from './useTextRoleNucleon'; 3 | 4 | export interface SelectorProps { 5 | selectedValue: V; 6 | onSelectedValueChange: (v: V) => void; 7 | } 8 | 9 | export type SelectorItem = { 10 | value: V; 11 | label?: string; 12 | }; 13 | 14 | export interface SelectorListProps 15 | extends SelectorProps { 16 | readonly items: ReadonlyArray> | ReadonlyArray; 17 | } 18 | 19 | export type PropsWithStyle

= { 20 | style?: StyleProp; 21 | } & P; 22 | 23 | export type PropsWithStringChild = Omit< 24 | Target, 25 | 'children' 26 | > & { children: string }; 27 | 28 | export type RefProps = Omit & { 29 | name: T; 30 | }; 31 | -------------------------------------------------------------------------------- /apps/discovery/src/components/nucleons/useContentWidthContext.ts: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import contentWidthContextNucleon from './contentWidthContextNucleon'; 3 | 4 | export function useNuclearContentWidth() { 5 | return React.useContext(contentWidthContextNucleon); 6 | } 7 | -------------------------------------------------------------------------------- /apps/discovery/src/components/nucleons/useSelectorPropsNucleon.ts: -------------------------------------------------------------------------------- 1 | import { useMemo } from 'react'; 2 | import { SelectorItem } from './types'; 3 | 4 | export default function useSelectorItemsNucleon( 5 | items: ReadonlyArray> | ReadonlyArray 6 | ) { 7 | return useMemo>>>( 8 | () => 9 | (items || []).map((item: SelectorItem | V) => 10 | typeof item !== 'string' && typeof item !== 'number' 11 | ? { 12 | value: (item as any).value, 13 | label: String((item as any).label || (item as any).value) 14 | } 15 | : { value: item as V, label: String(item) } 16 | ), 17 | [items] 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /apps/discovery/src/components/nucleons/useSurfaceBackgroundStyleNucleon.ts: -------------------------------------------------------------------------------- 1 | import { useMemo } from 'react'; 2 | import { useColorRoles } from '../../theme/colorSystem'; 3 | 4 | export default function useSurfaceBackgroundStyleNucleon() { 5 | const { surface } = useColorRoles(); 6 | return useMemo( 7 | () => ({ 8 | backgroundColor: surface.background 9 | }), 10 | [surface.background] 11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /apps/discovery/src/components/screens/HomeDrawerScreen/DrawerPlaygroundHeader.tsx: -------------------------------------------------------------------------------- 1 | import { DrawerHeaderProps } from '@react-navigation/drawer/lib/typescript/src/types'; 2 | import React from 'react'; 3 | import UIAppbarActionAtom from '../../UIAppbarActionAtom'; 4 | import UIAppbarContentAtom from '../../UIAppbarContentAtom'; 5 | import UIHeaderAtom from '../../UIHeaderAtom'; 6 | 7 | export type StandardHeaderOrganismProps = DrawerHeaderProps; 8 | 9 | export default function DrawerPlaygroundHeader({ 10 | scene 11 | }: StandardHeaderOrganismProps) { 12 | const { 13 | descriptor: { options, navigation } 14 | } = scene; 15 | const onMenuPress = React.useCallback( 16 | () => (navigation as any).openDrawer(), 17 | [navigation] 18 | ); 19 | return ( 20 | 21 | 22 | 23 | 24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /apps/discovery/src/components/screens/HomeDrawerScreen/VersionDisplay.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable react-native/no-inline-styles */ 2 | import * as React from 'react'; 3 | import { View } from 'react-native'; 4 | import { useSafeAreaInsets } from 'react-native-safe-area-context'; 5 | // eslint-disable-next-line @typescript-eslint/ban-ts-comment 6 | // @ts-ignore 7 | import version from '../../../../version'; 8 | import TextRoleNucleon from '../../nucleons/TextRoleNucleon'; 9 | import { useColorRoles } from '../../../theme/colorSystem'; 10 | 11 | export default function VersionDisplay() { 12 | const { bottom, left, right } = useSafeAreaInsets(); 13 | const { surface } = useColorRoles(); 14 | return ( 15 | 23 | 24 | Discovery {version.demo} 25 | {'\n'} 26 | rnrh {version.lib} 27 | 28 | 29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /apps/discovery/src/components/screens/HomeDrawerScreen/groupBy.ts: -------------------------------------------------------------------------------- 1 | export default function groupBy(xs: Array, key: K) { 2 | return xs.reduce(function (rv, x) { 3 | // eslint-disable-next-line @typescript-eslint/ban-ts-comment 4 | //@ts-ignore 5 | (rv[x[key]] = rv[x[key]] || []).push(x); 6 | return rv; 7 | }, {} as T[K] extends string ? Record> : never); 8 | } 9 | -------------------------------------------------------------------------------- /apps/discovery/src/components/selectedRadioItemContextAtom.ts: -------------------------------------------------------------------------------- 1 | import { createContext } from 'react'; 2 | 3 | const selectedRadioItemContextAtom = createContext(''); 4 | 5 | export default selectedRadioItemContextAtom; 6 | -------------------------------------------------------------------------------- /apps/discovery/src/components/templates/ArticleTemplate/AnimatedContextProvider.tsx: -------------------------------------------------------------------------------- 1 | import React, { PropsWithChildren, useContext } from 'react'; 2 | import Animated, { 3 | useAnimatedScrollHandler, 4 | useSharedValue 5 | } from 'react-native-reanimated'; 6 | 7 | const animatedContext = React.createContext<{ 8 | scrollAnim: Animated.SharedValue; 9 | onScroll: (...args: any[]) => void; 10 | }>(null as any); 11 | 12 | export function useAnimatedContext() { 13 | return useContext(animatedContext); 14 | } 15 | 16 | export default function AnimatedContextProvider({ 17 | children 18 | }: PropsWithChildren<{}>) { 19 | const scrollAnim = useSharedValue(0); 20 | const onScroll = useAnimatedScrollHandler((event) => { 21 | scrollAnim.value = event.contentOffset.y; 22 | }); 23 | return ( 24 | 25 | {children} 26 | 27 | ); 28 | } 29 | -------------------------------------------------------------------------------- /apps/discovery/src/components/templates/ArticleTemplate/ScrollerProvider.tsx: -------------------------------------------------------------------------------- 1 | import React, { 2 | PropsWithChildren, 3 | RefObject, 4 | useContext, 5 | useMemo 6 | } from 'react'; 7 | import Animated from 'react-native-reanimated'; 8 | import Scroller from './Scroller'; 9 | 10 | const scrollerContext = React.createContext(null as any); 11 | 12 | export function useScroller() { 13 | return useContext(scrollerContext); 14 | } 15 | 16 | export default function ScrollerProvider({ 17 | children, 18 | scrollRef 19 | }: PropsWithChildren<{ 20 | scrollRef: RefObject; 21 | }>) { 22 | const index = useMemo(() => new Scroller(scrollRef), [scrollRef]); 23 | return ( 24 | 25 | {children} 26 | 27 | ); 28 | } 29 | -------------------------------------------------------------------------------- /apps/discovery/src/components/templates/PlaygroundTemplate/Playground.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { TNode } from 'react-native-render-html'; 3 | import PlaygroundStoreProvider, { 4 | PlaygroundInitParams 5 | } from './playgroundStore'; 6 | import Sheet, { SheetProps } from './Sheet'; 7 | import PlaygroundDisplay from './PlaygroundDisplay'; 8 | import CardColorRolesProvider from '../../croles/CardColorRolesProvider'; 9 | 10 | export interface PlaygroundTemplateProps 11 | extends PlaygroundInitParams { 12 | children: SheetProps['children']; 13 | } 14 | 15 | export default function PlaygroundTemplate({ 16 | children, 17 | ...storeInitParams 18 | }: PlaygroundTemplateProps) { 19 | const [ttree, setTtree] = useState(); 20 | return ( 21 | <> 22 | 23 | 24 | 25 | {children} 26 | 27 | 28 | 29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /apps/discovery/src/components/templates/PlaygroundTemplate/SheetControlsPortal.ts: -------------------------------------------------------------------------------- 1 | import { demoControlsContext } from './contexts'; 2 | import createPortal from './createPortal'; 3 | 4 | const SheetControlsPortal = createPortal( 5 | demoControlsContext, 6 | 'SheetControlsPortal' 7 | ); 8 | 9 | export default SheetControlsPortal; 10 | -------------------------------------------------------------------------------- /apps/discovery/src/components/templates/PlaygroundTemplate/SheetDescriptionPortal.ts: -------------------------------------------------------------------------------- 1 | import { demoDescriptionContext } from './contexts'; 2 | import createPortal from './createPortal'; 3 | 4 | const SheetDescriptionPortal = createPortal( 5 | demoDescriptionContext, 6 | 'SheetDescriptionPortal' 7 | ); 8 | 9 | export default SheetDescriptionPortal; 10 | -------------------------------------------------------------------------------- /apps/discovery/src/components/templates/PlaygroundTemplate/SheetNavigatorPortal.ts: -------------------------------------------------------------------------------- 1 | import { demoNavigatorContext } from './contexts'; 2 | import createPortal from './createPortal'; 3 | 4 | const SheetNavigatorPortal = createPortal( 5 | demoNavigatorContext, 6 | 'SheetNavigatorPortal' 7 | ); 8 | 9 | export default SheetNavigatorPortal; 10 | -------------------------------------------------------------------------------- /apps/discovery/src/components/templates/PlaygroundTemplate/SheetRouteColor.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useColorRoles } from '../../../theme/colorSystem'; 3 | import UIColorPickerControlAtom from '../../UIColorPickerControlAtom'; 4 | import { 5 | usePlaygroundStateSlice, 6 | usePlaygroundSetter 7 | } from './playgroundStore'; 8 | 9 | export default function SheetRouteColor() { 10 | const color = usePlaygroundStateSlice('color'); 11 | const { surface } = useColorRoles(); 12 | const setColor = usePlaygroundSetter('color'); 13 | return ( 14 | 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /apps/discovery/src/components/templates/PlaygroundTemplate/SheetRouteContainer.tsx: -------------------------------------------------------------------------------- 1 | import { BottomSheetScrollView } from '@gorhom/bottom-sheet'; 2 | import React, { PropsWithChildren } from 'react'; 3 | import { StyleSheet } from 'react-native'; 4 | 5 | const styles = StyleSheet.create({ 6 | flex: { flexGrow: 1 } 7 | }); 8 | 9 | export default function SheetRouteContainer({ 10 | children 11 | }: PropsWithChildren<{}>) { 12 | return ( 13 | 16 | {children} 17 | 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /apps/discovery/src/components/templates/PlaygroundTemplate/SheetRouteControls.tsx: -------------------------------------------------------------------------------- 1 | import React, { useContext } from 'react'; 2 | import { demoControlsContext } from './contexts'; 3 | import SheetRouteContainer from './SheetRouteContainer'; 4 | 5 | export default function SheetRouteControls() { 6 | const controls = useContext(demoControlsContext); 7 | return {controls}; 8 | } 9 | -------------------------------------------------------------------------------- /apps/discovery/src/components/templates/PlaygroundTemplate/SheetRouteFontFamily.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import UIRadioListControlMolecule, { 3 | RadioListControlProps 4 | } from '../../UIRadioListControlMolecule'; 5 | import { 6 | usePlaygroundStateSlice, 7 | usePlaygroundSetter 8 | } from './playgroundStore'; 9 | import { SYSTEM_FONTS } from '../../../constants'; 10 | 11 | const getLabelStyle: RadioListControlProps['labelStyle'] = ({ 12 | value 13 | }) => ({ 14 | fontFamily: value 15 | }); 16 | 17 | export default function SheetRouteFontFamily() { 18 | const fontFamily = usePlaygroundStateSlice('fontFamily'); 19 | const setFontFamily = usePlaygroundSetter('fontFamily'); 20 | return ( 21 | 27 | ); 28 | } 29 | -------------------------------------------------------------------------------- /apps/discovery/src/components/templates/PlaygroundTemplate/SheetRouteOlListType.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import UIRadioListControlMolecule from '../../UIRadioListControlMolecule'; 3 | import { 4 | usePlaygroundStateSlice, 5 | usePlaygroundSetter, 6 | olListTypes 7 | } from './playgroundStore'; 8 | 9 | export default function SheetRouteOlListType() { 10 | const olListType = usePlaygroundStateSlice('olListType'); 11 | const setListType = usePlaygroundSetter('olListType'); 12 | return ( 13 | 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /apps/discovery/src/components/templates/PlaygroundTemplate/SheetRouteUlListType.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import UIRadioListControlMolecule from '../../UIRadioListControlMolecule'; 3 | import { 4 | usePlaygroundStateSlice, 5 | usePlaygroundSetter, 6 | ulListTypes 7 | } from './playgroundStore'; 8 | 9 | export default function SheetRouteUlListType() { 10 | const ulListType = usePlaygroundStateSlice('ulListType'); 11 | const setUlListType = usePlaygroundSetter('ulListType'); 12 | return ( 13 | 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /apps/discovery/src/components/templates/PlaygroundTemplate/SheetStack.tsx: -------------------------------------------------------------------------------- 1 | import { createStackNavigator } from '@react-navigation/stack'; 2 | 3 | const SheetStack = createStackNavigator(); 4 | 5 | export default SheetStack; 6 | -------------------------------------------------------------------------------- /apps/discovery/src/components/templates/PlaygroundTemplate/contexts.ts: -------------------------------------------------------------------------------- 1 | import { createContext, ReactNode } from 'react'; 2 | import { TNode } from 'react-native-render-html'; 3 | 4 | export const demoDescriptionContext = createContext(null); 5 | export const demoControlsContext = createContext(null); 6 | export const demoNavigatorContext = createContext(null); 7 | export const demoStateContext = createContext<{ html: string; ttree?: TNode }>({ 8 | html: '', 9 | ttree: undefined 10 | }); 11 | -------------------------------------------------------------------------------- /apps/discovery/src/components/templates/PlaygroundTemplate/createPortal.ts: -------------------------------------------------------------------------------- 1 | import React, { Context, PropsWithChildren, ReactNode } from 'react'; 2 | 3 | type PortalProps = PropsWithChildren<{ _tpChildren?: ReactNode }>; 4 | 5 | export default function createPortal( 6 | context: Context, 7 | name: string 8 | ) { 9 | const Portal = function ({ children, _tpChildren }: PortalProps) { 10 | return React.createElement( 11 | context.Provider, 12 | { 13 | value: children 14 | }, 15 | _tpChildren 16 | ); 17 | }; 18 | Portal.displayName = name; 19 | Portal.portalId = Symbol(name); 20 | return Portal; 21 | } 22 | -------------------------------------------------------------------------------- /apps/discovery/src/components/templates/PlaygroundTemplate/index.tsx: -------------------------------------------------------------------------------- 1 | import PlaygroundTemplate from './Playground'; 2 | import SheetControlsPortal from './SheetControlsPortal'; 3 | import SheetDescriptionPortal from './SheetDescriptionPortal'; 4 | import SheetNavigatorPortal from './SheetNavigatorPortal'; 5 | import SheetRouteContainer from './SheetRouteContainer'; 6 | import SheetStack from './SheetStack'; 7 | 8 | const PlaygroundScreen = SheetStack.Screen; 9 | 10 | export { 11 | SheetControlsPortal as PlaygroundControls, 12 | SheetDescriptionPortal as PlaygroundDescription, 13 | SheetNavigatorPortal as PlaygroundNavigator, 14 | SheetRouteContainer as PlaygroundRouteContainer, 15 | PlaygroundScreen 16 | }; 17 | 18 | export default PlaygroundTemplate; 19 | -------------------------------------------------------------------------------- /apps/discovery/src/components/templates/PlaygroundTemplate/sheetSnapPoints.ts: -------------------------------------------------------------------------------- 1 | const sheetSnapPoints = [240, '50%', '65%']; 2 | 3 | export default sheetSnapPoints; 4 | -------------------------------------------------------------------------------- /apps/discovery/src/constants/index.ts: -------------------------------------------------------------------------------- 1 | import Constants from 'expo-constants'; 2 | 3 | import uniq from 'ramda/es/uniq'; 4 | import pipe from 'ramda/es/pipe'; 5 | import filter from 'ramda/es/filter'; 6 | 7 | const normalizeFonts = pipe( 8 | uniq, 9 | filter( 10 | (c: string) => 11 | !c.match( 12 | /bold|italic|semi|regular|extra|ultra|light|black|medium|thin|-/i 13 | ) 14 | ) 15 | ); 16 | 17 | const SYSTEM_FONTS = normalizeFonts([...Constants.systemFonts, 'space-mono']); 18 | const BODY_CHAPTER_SPACING = 16; 19 | const BODY_VERTICAL_SPACING = 12; 20 | const BODY_HZ_SPACING = 2; 21 | const BODY_PARAGRAPH_SPACING = 8; 22 | const HEADER_COLL_HEIGHT = 54; 23 | export { 24 | SYSTEM_FONTS, 25 | BODY_CHAPTER_SPACING, 26 | BODY_PARAGRAPH_SPACING, 27 | BODY_HZ_SPACING, 28 | BODY_VERTICAL_SPACING, 29 | HEADER_COLL_HEIGHT 30 | }; 31 | -------------------------------------------------------------------------------- /apps/discovery/src/highlight/StylesheetsProvider.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { PropsWithChildren } from 'react'; 3 | import highlighterStylesheetsContext from './highlighterStylesheetsContext'; 4 | import lazyStylesheetRegistry, { 5 | HighlightJsStyles 6 | } from './lazyStylesheetRegistry'; 7 | 8 | export { HighlightJsStyles }; 9 | 10 | export default function StylesheetsProvider({ 11 | children, 12 | style 13 | }: PropsWithChildren<{ 14 | style: HighlightJsStyles; 15 | }>) { 16 | const value = lazyStylesheetRegistry[style]; 17 | if (!value) { 18 | console.warn( 19 | `There is no corresponding highlight.js style with name "${style}".` 20 | ); 21 | return null; 22 | } 23 | return ( 24 | 25 | {children} 26 | 27 | ); 28 | } 29 | -------------------------------------------------------------------------------- /apps/discovery/src/highlight/createStylesheets.ts: -------------------------------------------------------------------------------- 1 | import { CSSProperties } from 'react'; 2 | import { StyleSheet, TextStyle } from 'react-native'; 3 | import normalizeStylesheet from './normalizeStylesheet'; 4 | 5 | type HljsStylsheet = Record; 6 | 7 | export type HighlighterStylesheets = ReturnType; 8 | 9 | export default function createStylesheets(hljsStylesheet: HljsStylsheet) { 10 | const normalizedStyles = normalizeStylesheet(hljsStylesheet); 11 | const { backgroundColor, color } = normalizedStyles.hljs; 12 | const containerStylesheet = StyleSheet.create({ 13 | container: { backgroundColor: backgroundColor as string }, 14 | text: { color } 15 | }); 16 | const contentStylesheet = StyleSheet.create( 17 | normalizedStyles as Record 18 | ); 19 | return { 20 | containerStylesheet, 21 | contentStylesheet 22 | }; 23 | } 24 | -------------------------------------------------------------------------------- /apps/discovery/src/highlight/highlighterStylesheetsContext.ts: -------------------------------------------------------------------------------- 1 | import { createContext } from 'react'; 2 | import { HighlighterStylesheets } from './createStylesheets'; 3 | 4 | const highlighterStylesheetsContext = createContext( 5 | {} as any 6 | ); 7 | 8 | export default highlighterStylesheetsContext; 9 | -------------------------------------------------------------------------------- /apps/discovery/src/highlight/lazyStylesheetRegistry.ts: -------------------------------------------------------------------------------- 1 | import createStylesheets, { HighlighterStylesheets } from './createStylesheets'; 2 | import * as styles from 'react-syntax-highlighter/dist/esm/styles/hljs'; 3 | 4 | export type HighlightJsStyles = keyof typeof styles; 5 | 6 | const lazyStylesheetRegistry = new Proxy< 7 | Record 8 | >({} as any, { 9 | get(target, prop: HighlightJsStyles) { 10 | if (prop in target) { 11 | return target[prop]; 12 | } 13 | const hlstylesheet = styles[prop]; 14 | if (!hlstylesheet) { 15 | return; 16 | } 17 | target[prop] = createStylesheets(hlstylesheet); 18 | return target[prop]; 19 | } 20 | }); 21 | 22 | export default lazyStylesheetRegistry; 23 | -------------------------------------------------------------------------------- /apps/discovery/src/highlight/normalizeStylesheet.ts: -------------------------------------------------------------------------------- 1 | import { CSSProperties } from 'react'; 2 | 3 | type HljsStylsheet = Record; 4 | 5 | export default function normalizeStylesheet(hljsStylesheet: HljsStylsheet) { 6 | return Object.fromEntries( 7 | Object.entries(hljsStylesheet).map( 8 | ([ 9 | className, 10 | { color, background, backgroundColor, fontWeight, fontStyle } 11 | ]) => [ 12 | className, 13 | { 14 | color, 15 | backgroundColor: background ?? backgroundColor, 16 | fontWeight, 17 | fontStyle 18 | } 19 | ] 20 | ) 21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /apps/discovery/src/hooks/useOnLinkPress.ts: -------------------------------------------------------------------------------- 1 | import React, { useCallback } from 'react'; 2 | import onLinkPressContext from '../state/onLinkPressContext'; 3 | 4 | function useOnLinkPress(uri: string): () => void; 5 | function useOnLinkPress(): (uri: string) => void; 6 | function useOnLinkPress(uri?: string) { 7 | const onLinkPress = React.useContext(onLinkPressContext); 8 | if (uri) { 9 | // eslint-disable-next-line react-hooks/rules-of-hooks 10 | return useCallback(() => onLinkPress(uri), [onLinkPress, uri]); 11 | } 12 | return onLinkPress; 13 | } 14 | 15 | export default useOnLinkPress; 16 | -------------------------------------------------------------------------------- /apps/discovery/src/navigation/RootNavigator.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { 3 | createStackNavigator, 4 | StackNavigationOptions 5 | } from '@react-navigation/stack'; 6 | import HomeScreen from '../components/screens/HomeDrawerScreen'; 7 | import StackHeader from './StackHeader'; 8 | import useSurfaceBackgroundStyleNucleon from '../components/nucleons/useSurfaceBackgroundStyleNucleon'; 9 | 10 | const Stack = createStackNavigator(); 11 | 12 | export default function RootNavigator() { 13 | const cardStyle = useSurfaceBackgroundStyleNucleon(); 14 | const screenOptions = React.useMemo( 15 | () => ({ 16 | animationTypeForReplace: 'pop', 17 | animationEnabled: false, 18 | header: (props) => , 19 | cardStyle: cardStyle 20 | }), 21 | [cardStyle] 22 | ); 23 | return ( 24 | 25 | 30 | 31 | ); 32 | } 33 | -------------------------------------------------------------------------------- /apps/discovery/src/navigation/StackHeader.tsx: -------------------------------------------------------------------------------- 1 | import { StackHeaderProps } from '@react-navigation/stack'; 2 | import React from 'react'; 3 | import UIAppbarActionAtom from '../components/UIAppbarActionAtom'; 4 | import UIAppbarContentAtom from '../components/UIAppbarContentAtom'; 5 | import UIHeaderAtom from '../components/UIHeaderAtom'; 6 | 7 | export { StackHeaderProps }; 8 | 9 | export default function StackHeader(props: StackHeaderProps) { 10 | const { scene } = props; 11 | const { 12 | descriptor: { options, navigation } 13 | } = scene; 14 | const onMenuPress = React.useCallback( 15 | () => navigation.goBack(), 16 | [navigation] 17 | ); 18 | return ( 19 | 20 | 21 | 22 | 23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /apps/discovery/src/navigation/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { NavigationContainer } from '@react-navigation/native'; 3 | import { View } from 'react-native'; 4 | import RootNavigator from './RootNavigator'; 5 | import { useTheme } from '../theme/ThemeProvider'; 6 | 7 | const linking = { 8 | prefixes: ['https://meliorence.github.io/react-native-render-html'] 9 | }; 10 | 11 | export default function Navigation() { 12 | const theme = useTheme(); 13 | return ( 14 | 15 | 16 | 17 | 18 | 19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /apps/discovery/src/state/ColorSchemeProvider.tsx: -------------------------------------------------------------------------------- 1 | import React, { PropsWithChildren, useState } from 'react'; 2 | 3 | export interface ColorSchemeState { 4 | colorScheme: 'light' | 'dark'; 5 | setColorScheme: (colorScheme: 'light' | 'dark') => void; 6 | } 7 | 8 | const ColorSchemeContext = React.createContext({ 9 | colorScheme: 'light', 10 | setColorScheme: () => {} 11 | }); 12 | 13 | export function useColorScheme() { 14 | return React.useContext(ColorSchemeContext).colorScheme; 15 | } 16 | 17 | export function useColorSchemeSetter() { 18 | return React.useContext(ColorSchemeContext).setColorScheme; 19 | } 20 | 21 | export default function ColorSchemeProvider({ 22 | initialColorScheme, 23 | children 24 | }: PropsWithChildren<{ 25 | initialColorScheme: ColorSchemeState['colorScheme']; 26 | }>) { 27 | const [colorScheme, setColorScheme] = 28 | useState(initialColorScheme); 29 | return ( 30 | 31 | {children} 32 | 33 | ); 34 | } 35 | -------------------------------------------------------------------------------- /apps/discovery/src/state/onLinkPressContext.ts: -------------------------------------------------------------------------------- 1 | import { createContext } from 'react'; 2 | 3 | const onLinkPressContext = createContext<(uri: string) => void>( 4 | () => undefined as void 5 | ); 6 | 7 | export default onLinkPressContext; 8 | -------------------------------------------------------------------------------- /apps/discovery/src/state/store.tsx: -------------------------------------------------------------------------------- 1 | import create from 'zustand'; 2 | 3 | interface State extends Record { 4 | legacyMode: boolean; 5 | // Actions 6 | toggleLegacyMode: () => void; 7 | } 8 | 9 | const useStore = create((set) => { 10 | return { 11 | legacyMode: false, 12 | // Actions 13 | toggleLegacyMode: () => set((s) => ({ legacyMode: !s.legacyMode })) 14 | }; 15 | }); 16 | 17 | const legacyMode = (s: State) => s.legacyMode; 18 | const toggleLegacyMode = (s: State) => s.toggleLegacyMode; 19 | 20 | export const useLegacyMode = () => useStore(legacyMode); 21 | export const useToggleLegacyMode = () => useStore(toggleLegacyMode); 22 | -------------------------------------------------------------------------------- /apps/discovery/src/svg.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.svg' { 2 | import { SvgComponent } from '@doc/svg-component'; 3 | const content: SvgComponent; 4 | export default content; 5 | } 6 | -------------------------------------------------------------------------------- /apps/discovery/src/svgAssetsIndex.ts: -------------------------------------------------------------------------------- 1 | import { SvgAssetType } from '@doc/pages'; 2 | import DataFlow from '../assets/svg/data-flow.svg'; 3 | import Logo from '../assets/svg/logo.svg'; 4 | 5 | const svgAssetsIndex: Record = { 6 | 'data-flow': DataFlow, 7 | logo: Logo 8 | }; 9 | 10 | export default svgAssetsIndex; 11 | -------------------------------------------------------------------------------- /apps/discovery/src/theme/alphaMixColor.ts: -------------------------------------------------------------------------------- 1 | import Color from 'color'; 2 | 3 | export default function alphaMixColor( 4 | color: string, 5 | alpha: number, 6 | background = 'white' 7 | ) { 8 | return Color(color).alpha(alpha).mix(Color(background)).hex(); 9 | } 10 | -------------------------------------------------------------------------------- /apps/discovery/src/theme/shiftColor.ts: -------------------------------------------------------------------------------- 1 | import Color from 'color'; 2 | 3 | function shiftColor(color: string, ratio: number): string; 4 | function shiftColor( 5 | color: string, 6 | ratioDark: number, 7 | ratioLight: number 8 | ): string; 9 | 10 | function shiftColor( 11 | color: string, 12 | ratioDarkOrBoth: number, 13 | ratioLight?: number 14 | ) { 15 | const c = Color(color); 16 | if (c.isDark()) { 17 | return c.lighten(ratioDarkOrBoth).hex(); 18 | } 19 | return c.darken(ratioLight ?? ratioDarkOrBoth).hex(); 20 | } 21 | 22 | export default shiftColor; 23 | -------------------------------------------------------------------------------- /apps/discovery/svgr.config.js: -------------------------------------------------------------------------------- 1 | module.exports = require('@doc/svgr-conf'); 2 | -------------------------------------------------------------------------------- /apps/discovery/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowSyntheticDefaultImports": true, 4 | "jsx": "react-native", 5 | "lib": [ 6 | "dom", 7 | "esnext" 8 | ], 9 | "moduleResolution": "node", 10 | "noEmit": true, 11 | "skipLibCheck": true, 12 | "resolveJsonModule": true, 13 | "strict": true 14 | }, 15 | "extends": "expo/tsconfig.base" 16 | } 17 | -------------------------------------------------------------------------------- /apps/discovery/types.tsx: -------------------------------------------------------------------------------- 1 | import { RenderHTMLProps } from 'react-native-render-html'; 2 | 3 | export type RootStackParamList = { 4 | Root: undefined; 5 | NotFound: undefined; 6 | }; 7 | 8 | export type BottomTabParamList = { 9 | TabOne: undefined; 10 | TabTwo: undefined; 11 | }; 12 | 13 | export type TabOneParamList = { 14 | TabOneScreen: undefined; 15 | }; 16 | 17 | export type TabTwoParamList = { 18 | TabTwoScreen: undefined; 19 | }; 20 | 21 | export interface SnippetDeclaration { 22 | name: string; 23 | codeSource: string; 24 | supportsLegacy: boolean; 25 | props?: RenderHTMLProps; 26 | } 27 | -------------------------------------------------------------------------------- /apps/discovery/version.js: -------------------------------------------------------------------------------- 1 | const json = require('react-native-render-html/package.json'); 2 | 3 | module.exports = { 4 | demo: '4.0.1', 5 | lib: json.version 6 | }; 7 | -------------------------------------------------------------------------------- /apps/website/.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | /node_modules 3 | 4 | # Production 5 | /build 6 | 7 | # Generated files 8 | .docusaurus 9 | .cache-loader 10 | 11 | # Misc 12 | .DS_Store 13 | .env.local 14 | .env.development.local 15 | .env.test.local 16 | .env.production.local 17 | 18 | npm-debug.log* 19 | yarn-debug.log* 20 | yarn-error.log* 21 | 22 | # Typedoc 23 | /apisidebar.json 24 | 25 | /api/**/*.mdx 26 | -------------------------------------------------------------------------------- /apps/website/README.md: -------------------------------------------------------------------------------- 1 | # Website 2 | 3 | This website is built using [Docusaurus 2](https://docusaurus.io/), a modern static website generator. 4 | 5 | ## Installation 6 | 7 | ```console 8 | yarn install 9 | ``` 10 | 11 | ## Local Development 12 | 13 | ```console 14 | yarn start 15 | ``` 16 | 17 | This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server. 18 | 19 | ## Build 20 | 21 | ```console 22 | yarn build 23 | ``` 24 | 25 | This command generates static content into the `build` directory and can be served using any static contents hosting service. 26 | 27 | ## Deployment 28 | 29 | ```console 30 | GIT_USER= USE_SSH=true yarn deploy 31 | ``` 32 | 33 | If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch. 34 | -------------------------------------------------------------------------------- /apps/website/api/.gitignore: -------------------------------------------------------------------------------- 1 | *.mdx -------------------------------------------------------------------------------- /apps/website/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [require.resolve('@docusaurus/core/lib/babel/preset')] 3 | }; 4 | -------------------------------------------------------------------------------- /apps/website/docs/.gitignore: -------------------------------------------------------------------------------- 1 | **/*.mdx 2 | **/*.md 3 | !migration-guide.mdx -------------------------------------------------------------------------------- /apps/website/docs/content/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "Content", 3 | "position": 2 4 | } 5 | -------------------------------------------------------------------------------- /apps/website/docs/flow/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "Flow", 3 | "position": 3 4 | } 5 | -------------------------------------------------------------------------------- /apps/website/docs/guides/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "Guides", 3 | "position": 2 4 | } 5 | -------------------------------------------------------------------------------- /apps/website/sidebars.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Creating a sidebar enables you to: 3 | - create an ordered group of docs 4 | - render a sidebar for each doc of that group 5 | - provide next/previous navigation 6 | 7 | The sidebars can be generated from the filesystem, or explicitly defined here. 8 | 9 | Create as many sidebars as you want. 10 | */ 11 | 12 | module.exports = { 13 | // By default, Docusaurus generates a sidebar from the docs folder structure 14 | docSidebar: [ 15 | { type: 'autogenerated', dirName: '.' }, 16 | { 17 | type: 'link', 18 | label: 'Props', 19 | href: '/api/renderhtmlprops' 20 | } 21 | ] 22 | 23 | // But you can create a sidebar manually 24 | /* 25 | tutorialSidebar: [ 26 | { 27 | type: 'category', 28 | label: 'Tutorial', 29 | items: ['hello'], 30 | }, 31 | ], 32 | */ 33 | }; 34 | -------------------------------------------------------------------------------- /apps/website/src/components/APIReference.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Reference from './Reference'; 3 | import useBaseUrl from '@docusaurus/useBaseUrl'; 4 | 5 | export default function APIReference({ 6 | name, 7 | member, 8 | full = false 9 | }: { 10 | name: string; 11 | member?: string; 12 | full?: boolean; 13 | }) { 14 | const memberSuffix = member ? `#${member.toLowerCase()}` : ''; 15 | return ( 16 | 24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /apps/website/src/components/Badges.module.scss: -------------------------------------------------------------------------------- 1 | .badge { 2 | height: 20px; 3 | overflow: hidden; 4 | > img { 5 | display: block; 6 | } 7 | } 8 | 9 | .stats { 10 | display: flex; 11 | display: none; 12 | align-items: center; 13 | justify-content: flex-end; 14 | gap: 1rem; 15 | } 16 | 17 | .stats { 18 | @media screen and (max-width: 450px) { 19 | & { 20 | position: relative; 21 | flex-direction: column; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /apps/website/src/components/Badges.tsx: -------------------------------------------------------------------------------- 1 | import TwitterFollow from './TwitterFollow'; 2 | import React from 'react'; 3 | import classes from './Badges.module.scss'; 4 | 5 | export default function Badges() { 6 | return ( 7 |

8 | 9 |