Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 4 additions & 9 deletions .github/workflows/android-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,23 +36,18 @@ concurrency:

jobs:
build:
name: Build Android Example App (${{ matrix.arch }})
name: Build Android Example App
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
arch: [new, old]
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: '22.14.0'
- uses: oven-sh/setup-bun@v2

- name: Install dependencies (bun)
run: bun install

- name: Disable new architecture in gradle.properties
if: matrix.arch == 'old'
run: sed -i "s/newArchEnabled=true/newArchEnabled=false/g" example/android/gradle.properties

- name: Setup JDK 17
uses: actions/setup-java@v5
with:
Expand Down
13 changes: 4 additions & 9 deletions .github/workflows/ios-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,13 @@ concurrency:

jobs:
build:
name: Build iOS Example App (${{ matrix.arch }})
name: Build iOS Example App
runs-on: macOS-15
strategy:
fail-fast: false
matrix:
arch: [new, old]
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: '22.14.0'
- uses: oven-sh/setup-bun@v2
- name: Setup Xcode
uses: maxim-lobanov/setup-xcode@v1
Expand All @@ -64,10 +63,6 @@ jobs:
- name: Install dependencies (bun)
run: bun install

- name: Disable new architecture in Podfile
if: matrix.arch == 'old'
run: sed -i "" "s/ENV\['RCT_NEW_ARCH_ENABLED'\] = '1'/ENV['RCT_NEW_ARCH_ENABLED'] = '0'/g" example/ios/Podfile

- name: Setup Ruby (bundle)
uses: ruby/setup-ruby@v1
with:
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ jobs:
uses: actions/checkout@v6
with:
fetch-depth: 0
- uses: actions/setup-node@v6
with:
node-version: '22.14.0'
- name: Setup Bun.js
uses: oven-sh/setup-bun@v2
with:
Expand Down
62 changes: 62 additions & 0 deletions .github/workflows/validate.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
name: Validate

permissions:
contents: read

on:
push:
branches:
- main
paths:
- '.github/workflows/validate.yml'
- 'src/**'
- 'package.json'
- 'bun.lock'
- 'tsconfig.json'
- 'babel.config.js'
- 'jest.config.js'
- 'eslint.config.mjs'
pull_request:
paths:
- '.github/workflows/validate.yml'
- 'src/**'
- 'package.json'
- 'bun.lock'
- 'tsconfig.json'
- 'babel.config.js'
- 'jest.config.js'
- 'eslint.config.mjs'
workflow_dispatch:

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
validate:
name: Validate JavaScript
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: '22.14.0'
- uses: oven-sh/setup-bun@v2

- name: Install dependencies
run: bun install --frozen-lockfile

- name: Run tests
run: bun run test --runInBand --no-watchman

- name: Check types
run: bun run typecheck

- name: Lint
run: bun run lint

- name: Check formatting
run: bun run format:check

- name: Build package
run: bun run build
2,196 changes: 1,547 additions & 649 deletions bun.lock

Large diffs are not rendered by default.

14 changes: 9 additions & 5 deletions cpp/NitroTextComponentDescriptor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@
//

#include "NitroTextComponentDescriptor.hpp"
#include <NitroModules/RawPropsCompat.hpp>
#include <react/renderer/textlayoutmanager/TextLayoutManager.h>

using namespace facebook;
using namespace margelo::nitro::nitrotext::views;

NitroTextComponentDescriptor::NitroTextComponentDescriptor(const react::ComponentDescriptorParameters& parameters)
: ConcreteComponentDescriptor(parameters,
react::RawPropsParser(/* enableJsiParser */ true)) {}
margelo::nitro::RawPropsCompat::makePropsParser()) {}

std::shared_ptr<const react::Props> NitroTextComponentDescriptor::cloneProps(const react::PropsParserContext& context,
const std::shared_ptr<const react::Props>& props,
Expand All @@ -30,10 +31,13 @@ NitroTextComponentDescriptor::NitroTextComponentDescriptor(const react::Componen

#ifdef ANDROID
// On Android, wrap props into state for JNI roundtrip.
const HybridNitroTextProps& props = concreteShadowNode.getConcreteProps();
HybridNitroTextState state;
state.setProps(props);
concreteShadowNode.setStateData(std::move(state));
auto constBaseProps = concreteShadowNode.getProps();
auto constProps = std::static_pointer_cast<const HybridNitroTextProps>(constBaseProps);
const auto& previousProps = concreteShadowNode.getStateData().getProps();
if (previousProps == nullptr || !constProps->hasSameProps(*previousProps)) {
HybridNitroTextState state{std::move(constProps)};
concreteShadowNode.setStateData(std::move(state));
}
#endif

// Inject TextLayoutManager so measurement works on Fabric (iOS/macOS/etc.).
Expand Down
106 changes: 53 additions & 53 deletions cpp/NitroTextShadowNode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,19 +37,19 @@ std::optional<NitroTextLayoutInputs> prepareTextLayoutInputs(
[&](const std::optional<margelo::nitro::nitrotext::Fragment> &fragOpt) {
auto a = react::TextAttributes::defaultTextAttributes();

if (props.allowFontScaling.value.has_value()) {
bool allowFontScaling = props.allowFontScaling.value.value();
if (props.allowFontScaling.get().has_value()) {
bool allowFontScaling = props.allowFontScaling.get().value();
a.allowFontScaling = allowFontScaling;
a.fontSizeMultiplier =
allowFontScaling ? layoutContext.fontSizeMultiplier : 1.0f;
} else {
a.fontSizeMultiplier = layoutContext.fontSizeMultiplier;
}

if (props.dynamicTypeRamp.value.has_value()) {
if (props.dynamicTypeRamp.get().has_value()) {
using NitroDTR = margelo::nitro::nitrotext::DynamicTypeRamp;
using RNDTR = facebook::react::DynamicTypeRamp;
switch (props.dynamicTypeRamp.value.value()) {
switch (props.dynamicTypeRamp.get().value()) {
case NitroDTR::CAPTION2:
a.dynamicTypeRamp = RNDTR::Caption2;
break;
Expand Down Expand Up @@ -88,8 +88,8 @@ std::optional<NitroTextLayoutInputs> prepareTextLayoutInputs(

if (fragOpt.has_value() && fragOpt->fontSize.has_value()) {
a.fontSize = fragOpt->fontSize.value();
} else if (props.fontSize.value.has_value()) {
a.fontSize = props.fontSize.value.value();
} else if (props.fontSize.get().has_value()) {
a.fontSize = props.fontSize.get().value();
}

auto applyFontStyle =
Expand All @@ -111,14 +111,14 @@ std::optional<NitroTextLayoutInputs> prepareTextLayoutInputs(

if (fragOpt.has_value() && fragOpt->fontStyle.has_value()) {
applyFontStyle(fragOpt->fontStyle.value());
} else if (props.fontStyle.value.has_value()) {
applyFontStyle(props.fontStyle.value.value());
} else if (props.fontStyle.get().has_value()) {
applyFontStyle(props.fontStyle.get().value());
}

if (fragOpt.has_value() && fragOpt->fontFamily.has_value()) {
a.fontFamily = fragOpt->fontFamily.value();
} else if (props.fontFamily.value.has_value()) {
a.fontFamily = props.fontFamily.value.value();
} else if (props.fontFamily.get().has_value()) {
a.fontFamily = props.fontFamily.get().value();
}

auto applyFontWeight =
Expand Down Expand Up @@ -161,20 +161,20 @@ std::optional<NitroTextLayoutInputs> prepareTextLayoutInputs(

if (fragOpt.has_value() && fragOpt->fontWeight.has_value()) {
applyFontWeight(fragOpt->fontWeight.value());
} else if (props.fontWeight.value.has_value()) {
applyFontWeight(props.fontWeight.value.value());
} else if (props.fontWeight.get().has_value()) {
applyFontWeight(props.fontWeight.get().value());
}

if (fragOpt.has_value() && fragOpt->lineHeight.has_value()) {
a.lineHeight = fragOpt->lineHeight.value();
} else if (props.lineHeight.value.has_value()) {
a.lineHeight = props.lineHeight.value.value();
} else if (props.lineHeight.get().has_value()) {
a.lineHeight = props.lineHeight.get().value();
}

if (fragOpt.has_value() && fragOpt->letterSpacing.has_value()) {
a.letterSpacing = fragOpt->letterSpacing.value();
} else if (props.letterSpacing.value.has_value()) {
a.letterSpacing = props.letterSpacing.value.value();
} else if (props.letterSpacing.get().has_value()) {
a.letterSpacing = props.letterSpacing.get().value();
}

auto applyAlign = [&](margelo::nitro::nitrotext::TextAlign al) {
Expand Down Expand Up @@ -204,8 +204,8 @@ std::optional<NitroTextLayoutInputs> prepareTextLayoutInputs(

if (fragOpt.has_value() && fragOpt->textAlign.has_value()) {
applyAlign(fragOpt->textAlign.value());
} else if (props.textAlign.value.has_value()) {
applyAlign(props.textAlign.value.value());
} else if (props.textAlign.get().has_value()) {
applyAlign(props.textAlign.get().value());
}

auto applyTransform =
Expand Down Expand Up @@ -233,14 +233,14 @@ std::optional<NitroTextLayoutInputs> prepareTextLayoutInputs(

if (fragOpt.has_value() && fragOpt->textTransform.has_value()) {
applyTransform(fragOpt->textTransform.value());
} else if (props.textTransform.value.has_value()) {
applyTransform(props.textTransform.value.value());
} else if (props.textTransform.get().has_value()) {
applyTransform(props.textTransform.get().value());
}

if (props.lineBreakStrategyIOS.value.has_value()) {
if (props.lineBreakStrategyIOS.get().has_value()) {
using RNLineBreakStrategy = facebook::react::LineBreakStrategy;
using NitroLBS = margelo::nitro::nitrotext::LineBreakStrategyIOS;
switch (props.lineBreakStrategyIOS.value.value()) {
switch (props.lineBreakStrategyIOS.get().value()) {
case NitroLBS::NONE:
a.lineBreakStrategy = RNLineBreakStrategy::None;
break;
Expand All @@ -257,9 +257,9 @@ std::optional<NitroTextLayoutInputs> prepareTextLayoutInputs(
}
}

if (props.maxFontSizeMultiplier.value.has_value()) {
if (props.maxFontSizeMultiplier.get().has_value()) {
a.maxFontSizeMultiplier =
props.maxFontSizeMultiplier.value.value();
props.maxFontSizeMultiplier.get().value();
}

a.layoutDirection = layoutConstraints.layoutDirection;
Expand All @@ -269,8 +269,8 @@ std::optional<NitroTextLayoutInputs> prepareTextLayoutInputs(

react::AttributedString attributedString;

if (props.fragments.value.has_value()) {
const auto &frags = props.fragments.value.value();
if (props.fragments.get().has_value()) {
const auto &frags = props.fragments.get().value();
size_t lastNonEmptyIndex = SIZE_MAX;

for (size_t i = frags.size(); i > 0; i--) {
Expand Down Expand Up @@ -315,7 +315,7 @@ std::optional<NitroTextLayoutInputs> prepareTextLayoutInputs(
}
} else {
const std::string textToMeasure =
props.text.value.has_value() ? props.text.value.value()
props.text.get().has_value() ? props.text.get().value()
: std::string("");

if (textToMeasure.empty()) {
Expand All @@ -324,20 +324,20 @@ std::optional<NitroTextLayoutInputs> prepareTextLayoutInputs(

const react::ShadowView shadowView(node);

if (!props.fontSize.value.has_value() &&
!props.fontWeight.value.has_value() &&
!props.fontStyle.value.has_value() &&
!props.fontFamily.value.has_value() &&
!props.fontColor.value.has_value() &&
!props.textAlign.value.has_value() &&
!props.textTransform.value.has_value() &&
!props.lineHeight.value.has_value() &&
!props.letterSpacing.value.has_value() &&
!props.textDecorationLine.value.has_value() &&
!props.textDecorationColor.value.has_value() &&
!props.textDecorationStyle.value.has_value() &&
!props.dynamicTypeRamp.value.has_value() &&
!props.allowFontScaling.value.has_value()) {
if (!props.fontSize.get().has_value() &&
!props.fontWeight.get().has_value() &&
!props.fontStyle.get().has_value() &&
!props.fontFamily.get().has_value() &&
!props.fontColor.get().has_value() &&
!props.textAlign.get().has_value() &&
!props.textTransform.get().has_value() &&
!props.lineHeight.get().has_value() &&
!props.letterSpacing.get().has_value() &&
!props.textDecorationLine.get().has_value() &&
!props.textDecorationColor.get().has_value() &&
!props.textDecorationStyle.get().has_value() &&
!props.dynamicTypeRamp.get().has_value() &&
!props.allowFontScaling.get().has_value()) {
auto attrs = react::TextAttributes::defaultTextAttributes();
attrs.layoutDirection = layoutConstraints.layoutDirection;
attrs.fontSizeMultiplier = layoutContext.fontSizeMultiplier;
Expand All @@ -357,37 +357,37 @@ std::optional<NitroTextLayoutInputs> prepareTextLayoutInputs(
react::ParagraphAttributes paragraphAttributes;

bool needsParagraphAttrs =
props.numberOfLines.value.has_value() ||
props.adjustsFontSizeToFit.value.has_value() ||
props.minimumFontScale.value.has_value() ||
props.ellipsizeMode.value.has_value();
props.numberOfLines.get().has_value() ||
props.adjustsFontSizeToFit.get().has_value() ||
props.minimumFontScale.get().has_value() ||
props.ellipsizeMode.get().has_value();

if (needsParagraphAttrs) {
if (props.numberOfLines.value.has_value()) {
if (props.numberOfLines.get().has_value()) {
auto n =
static_cast<int>(std::round(props.numberOfLines.value.value()));
static_cast<int>(std::round(props.numberOfLines.get().value()));
if (n > 0) {
paragraphAttributes.maximumNumberOfLines = n;
}
}

if (props.adjustsFontSizeToFit.value.has_value()) {
if (props.adjustsFontSizeToFit.get().has_value()) {
paragraphAttributes.adjustsFontSizeToFit =
props.adjustsFontSizeToFit.value.value();
props.adjustsFontSizeToFit.get().value();
}

if (props.minimumFontScale.value.has_value()) {
if (props.minimumFontScale.get().has_value()) {
#if RN_VERSION_AT_LEAST(0, 81)
paragraphAttributes.minimumFontScale =
props.minimumFontScale.value.value();
props.minimumFontScale.get().value();
#endif
}

if (props.ellipsizeMode.value.has_value()) {
if (props.ellipsizeMode.get().has_value()) {
using NitroEllipsizeMode =
margelo::nitro::nitrotext::EllipsizeMode;
using RNEllipsizeMode = facebook::react::EllipsizeMode;
switch (props.ellipsizeMode.value.value()) {
switch (props.ellipsizeMode.get().value()) {
case NitroEllipsizeMode::CLIP:
paragraphAttributes.ellipsizeMode = RNEllipsizeMode::Clip;
break;
Expand Down
Loading