Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix/operators support 3 variants #155

Merged
merged 18 commits into from
Jan 15, 2024
Merged
Show file tree
Hide file tree
Changes from 7 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
4 changes: 4 additions & 0 deletions lib/src/attributes/variant_attribute.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ class VariantAttribute<T extends Variant> extends Attribute
return VariantAttribute(variant, _style.merge(other._style));
}

bool matches(Iterable<Variant> otherVariants) =>
otherVariants.contains(variant);

@override
Object get type => ObjectKey(variant);

Expand Down Expand Up @@ -73,6 +76,7 @@ class MultiVariantAttribute extends VariantAttribute<MultiVariant>
return VariantAttribute(variant, _style);
}

@override
bool matches(Iterable<Variant> otherVariants) =>
variant.matches(otherVariants);

Expand Down
5 changes: 3 additions & 2 deletions lib/src/factory/mix_provider_data.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,12 @@ class MixData with Comparable {
_tokenResolver = resolver;

factory MixData.create(BuildContext context, Style style) {
final styleMix = applyContextToVisualAttributes(context, style);
final attributeList = applyContextToVisualAttributes(context, style);

final resolver = MixTokenResolver(context);

return MixData._(resolver: resolver, attributes: AttributeMap(styleMix));
return MixData._(
resolver: resolver, attributes: AttributeMap(attributeList));
}

/// Getter for [MixTokenResolver].
Expand Down
19 changes: 4 additions & 15 deletions lib/src/factory/style_mix.dart
Original file line number Diff line number Diff line change
Expand Up @@ -288,23 +288,12 @@ class Style with Comparable {
/// Loop over all VariantAttributes in variants only once instead of a nested loop,
/// checking if each one matches with the selected variants.
/// If it does, add it to the matchedVariants, else add it to remainingVariants.

for (final attr in variants.values) {
if (attr is MultiVariantAttribute) {
if (attr.matches(selectedVariants)) {
// if all variants match, add it to the matchedVariants
matchedVariants.add(attr);
} else {
// Remove any matching variants and add it as a new MultiVariantAttribute
// This allows to multiple matching variants to be removed
// For multi pass matching
remainingVariants.add(attr.remove(selectedVariants));
}
if (attr.matches(selectedVariants)) {
matchedVariants.add(attr);
} else {
if (selectedVariantSet.contains(attr.variant)) {
matchedVariants.add(attr);
} else {
remainingVariants.add(attr);
}
remainingVariants.add(attr);
}
}

Expand Down
32 changes: 24 additions & 8 deletions lib/src/variants/variant.dart
Original file line number Diff line number Diff line change
Expand Up @@ -248,14 +248,20 @@ class MultiVariant extends Variant {
///
/// It initializes a `MultiVariant` with the given [variants] and sets the type to `MultiVariantType.and`.
factory MultiVariant.and(Iterable<Variant> variants) {
return MultiVariant(variants, type: MultiVariantOperator.and);
return MultiVariant(
variants,
type: MultiVariantOperator.and,
);
}

/// Factory constructor to create a `MultiVariant` where any one of the provided variants needs to be active (`MultiVariantType.or`).
///
/// It initializes a `MultiVariant` with the given [variants] and sets the type to `MultiVariantType.or`.
factory MultiVariant.or(Iterable<Variant> variants) {
return MultiVariant(variants, type: MultiVariantOperator.or);
return MultiVariant(
variants,
type: MultiVariantOperator.or,
);
}

/// Removes specified variants from this `MultiVariant`.
Expand Down Expand Up @@ -296,12 +302,22 @@ class MultiVariant extends Variant {
/// Here, `isMatched` will be true for `MultiVariantType.and` if both `variantA` and `variantB` are included in the provided list.
/// For `MultiVariantType.or`, `isMatched` would be true if either `variantA` or `variantB` is in the list.
bool matches(Iterable<Variant> matchVariants) {
final matchSet = matchVariants.toSet();
final variantSet = variants.toSet();

return operatorType == MultiVariantOperator.and
? variantSet.difference(matchSet).isEmpty
: variantSet.intersection(matchSet).isNotEmpty;
final list = variants.map((variant) {
if (variant is MultiVariant) {
return variant.matches(matchVariants);
} else {
final List<bool> x =
variants.map((e) => matchVariants.contains(variant)).toList();
return operatorType == MultiVariantOperator.and
? x.every((e) => e == true)
: x.contains(true);
}
}).toList();

final result = operatorType == MultiVariantOperator.and
? list.every((e) => e == true)
: list.contains(true);
return result;
}

/// Evaluates if the `MultiVariant` should be applied based on the build context.
Expand Down
126 changes: 120 additions & 6 deletions test/src/factory/style_mix_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ void main() {
});
});

group('Style.selectVariant', () {
group('Style.variant', () {
const attr1 = MockDoubleScalarAttribute(1.0);
const attr2 = MockIntScalarAttribute(2);
const attr3 = MockBooleanScalarAttribute(true);
Expand All @@ -184,25 +184,89 @@ void main() {
expect(style.variants.length, 1);
});

test('with matching multi variant', () {
test('with matching multi variant `and` ', () {
final multiVariant = MultiVariant.and(const [variantAttr1, variantAttr2]);
final style = Style(attr1, attr2, multiVariant(attr3));

final a = style.variant(variantAttr1);
final b = a.variant(variantAttr2);
final c = style.variant(variantAttr1, variantAttr2);

expect(a.styles.length, 2);
expect(a.variants.length, 1);

expect(b.styles.length, 2);
expect(b.variants.length, 1);

expect(c.styles.length, 3);
expect(c.variants.length, 0);

expect(b, a);
expect(c == b, false);
});

test('with matching multivariant `or` ', () {
final multiVariant = MultiVariant.or(const [variantAttr1, variantAttr2]);
final style = Style(attr1, attr2, multiVariant(attr3));

final firstStyle = style.variant(variantAttr1);
final secondStyle = firstStyle.variant(variantAttr2);
final secondStyle = style.variant(variantAttr2);
final thirdStyle = style.variant(variantAttr1, variantAttr2);

expect(firstStyle.styles.length, 2);
expect(firstStyle.variants.length, 1);
expect(firstStyle.styles.length, 3);
expect(firstStyle.variants.length, 0);

expect(secondStyle.styles.length, 3);
expect(secondStyle.variants.length, 0);

expect(thirdStyle.styles.length, 3);
expect(thirdStyle.variants.length, 0);

expect(secondStyle, firstStyle);
expect(secondStyle, thirdStyle);
});

test('with matching complex multivariant `or` ', () {
const variantAttr3 = Variant('variantAttr3');

final multiVariant = MultiVariant.and([
variantAttr3,
MultiVariant.or(const [variantAttr1, variantAttr2])
]);

final style = Style(attr1, attr2, multiVariant(attr3));

final a = style.variant(variantAttr1);

expect(a.styles.length, 2);
expect(a.variants.length, 1);

final b = style.variant(variantAttr2);

expect(b.styles.length, 2);
expect(b.variants.length, 1);

final c = style.variant(variantAttr3);

expect(c.styles.length, 2);
expect(c.variants.length, 1);

final d = style.variant(variantAttr1, variantAttr2);

expect(d.styles.length, 2);
expect(d.variants.length, 1);

final e = style.variant(variantAttr1, variantAttr3);

expect(e.styles.length, 3);
expect(e.variants.length, 0);

final f = style.variant(variantAttr2, variantAttr3);

expect(f.styles.length, 3);
expect(f.variants.length, 0);
});

test('with an Unmatched Variant', () {
final style = Style(attr1, attr2);
final updatedStyle = style.variant(variantAttr1);
Expand All @@ -211,7 +275,7 @@ void main() {
});
});

group('Style.selectVariantList', () {
group('Style.variantList', () {
const attr1 = MockDoubleScalarAttribute(1.0);
const attr2 = MockIntScalarAttribute(2);
const attr3 = MockBooleanScalarAttribute(true);
Expand Down Expand Up @@ -251,6 +315,56 @@ void main() {

expect(updatedMix, mix);
});

test(
'should return the same value if the parameter don`t satisfy the logic expression with 3 variants combined with `and` operator',
() {
const variantAttr3 = Variant('variantAttr3');

final style = Style(
box.color.red(),
(variantAttr1 & variantAttr2 & variantAttr3)(
icon.color.black(),
),
);

final a = style.variantList([variantAttr3]);
final b = style.variantList([variantAttr2]);
final c = style.variantList([variantAttr1]);
final d = style.variantList([variantAttr2, variantAttr3]);
final e = style.variantList([variantAttr1, variantAttr3]);
final f = style.variantList([variantAttr2, variantAttr1]);

expect(a, style);
expect(a, b);
expect(a, c);
expect(a, d);
expect(a, e);
expect(a, f);
});

test(
'should return the same value if the parameter don`t satisfy the logic expression with 3 variants combined with `or` operator',
() {
const variantAttr3 = Variant('variantAttr3');

const extraVariant1 = Variant('extraVariant1');
const extraVariant2 = Variant('extraVariant2');

final style = Style(
box.color.red(),
(variantAttr1 | variantAttr2 | variantAttr3)(
icon.color.black(),
),
);

final a = style.variantList([extraVariant1]);
final b = style.variantList([extraVariant2]);
final c = style.variantList([extraVariant1, extraVariant2]);

expect(a, b);
expect(a, c);
});
});

group('Style.pickVariants', () {
Expand Down
Loading
Loading