Skip to content

Commit 4131a5e

Browse files
Merge pull request #105 from KLEAK-Development/feat/css-scroll-behavior-text-overflow-aspect-ratio-place-items
feat: add CssScrollBehavior, CssTextOverflow, CssAspectRatio, and CssPlaceItems
2 parents 21d6626 + 8084ede commit 4131a5e

11 files changed

Lines changed: 388 additions & 0 deletions

packages/spark_css/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@
2121
- **Feat**: Added `CssObjectFit` sealed class for `object-fit` property with `.fill`, `.contain`, `.cover`, `.none`, `.scaleDown` keywords, plus `.variable()`, `.raw()`, and `.global()` escape hatches.
2222
- **Feat**: Added `CssPointerEvents` sealed class for `pointer-events` property with `.auto`, `.none`, `.visiblePainted`, `.visibleFill`, `.visibleStroke`, `.visible`, `.painted`, `.fill`, `.stroke`, `.all` keywords, plus `.variable()`, `.raw()`, and `.global()` escape hatches.
2323
- **Feat**: Added `CssResize` sealed class for `resize` property with `.none`, `.both`, `.horizontal`, `.vertical`, `.block`, `.inline` keywords, plus `.variable()`, `.raw()`, and `.global()` escape hatches.
24+
- **Feat**: Added `CssScrollBehavior` sealed class for `scroll-behavior` property with `.auto`, `.smooth` keywords, plus `.variable()`, `.raw()`, and `.global()` escape hatches.
25+
- **Feat**: Added `CssTextOverflow` sealed class for `text-overflow` property with `.clip`, `.ellipsis` keywords, `.value()` for custom strings, plus `.variable()`, `.raw()`, and `.global()` escape hatches.
26+
- **Feat**: Added `CssAspectRatio` sealed class for `aspect-ratio` property with `.auto` keyword, `.ratio()`, `.number()`, plus `.variable()`, `.raw()`, and `.global()` escape hatches.
27+
- **Feat**: Added `CssPlaceItems` sealed class for `place-items` shorthand property with align and optional justify parameters, plus `.variable()`, `.raw()`, and `.global()` escape hatches.
2428

2529
### Changed
2630

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import 'css_value.dart';
2+
3+
/// CSS aspect-ratio property values.
4+
sealed class CssAspectRatio implements CssValue {
5+
const CssAspectRatio._();
6+
7+
static const CssAspectRatio auto = _CssAspectRatioKeyword('auto');
8+
9+
/// Ratio value (e.g., `16 / 9`).
10+
factory CssAspectRatio.ratio(num width, num height) = _CssAspectRatioRatio;
11+
12+
/// Single number value (e.g., `1` or `0.5`).
13+
factory CssAspectRatio.number(num value) = _CssAspectRatioNumber;
14+
15+
/// CSS variable reference.
16+
factory CssAspectRatio.variable(String varName) = _CssAspectRatioVariable;
17+
18+
/// Raw CSS value escape hatch.
19+
factory CssAspectRatio.raw(String value) = _CssAspectRatioRaw;
20+
21+
/// Global keyword (inherit, initial, unset, revert).
22+
factory CssAspectRatio.global(CssGlobal global) = _CssAspectRatioGlobal;
23+
}
24+
25+
final class _CssAspectRatioKeyword extends CssAspectRatio {
26+
final String keyword;
27+
const _CssAspectRatioKeyword(this.keyword) : super._();
28+
29+
@override
30+
String toCss() => keyword;
31+
}
32+
33+
final class _CssAspectRatioRatio extends CssAspectRatio {
34+
final num width;
35+
final num height;
36+
const _CssAspectRatioRatio(this.width, this.height) : super._();
37+
38+
@override
39+
String toCss() => '$width / $height';
40+
}
41+
42+
final class _CssAspectRatioNumber extends CssAspectRatio {
43+
final num value;
44+
const _CssAspectRatioNumber(this.value) : super._();
45+
46+
@override
47+
String toCss() => '$value';
48+
}
49+
50+
final class _CssAspectRatioVariable extends CssAspectRatio {
51+
final String varName;
52+
const _CssAspectRatioVariable(this.varName) : super._();
53+
54+
@override
55+
String toCss() => 'var(--$varName)';
56+
}
57+
58+
final class _CssAspectRatioRaw extends CssAspectRatio {
59+
final String value;
60+
const _CssAspectRatioRaw(this.value) : super._();
61+
62+
@override
63+
String toCss() => value;
64+
}
65+
66+
final class _CssAspectRatioGlobal extends CssAspectRatio {
67+
final CssGlobal global;
68+
const _CssAspectRatioGlobal(this.global) : super._();
69+
70+
@override
71+
String toCss() => global.toCss();
72+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import 'css_flex.dart';
2+
import 'css_justify_items.dart';
3+
import 'css_value.dart';
4+
5+
/// CSS place-items shorthand property values.
6+
sealed class CssPlaceItems implements CssValue {
7+
const CssPlaceItems._();
8+
9+
/// Shorthand with align and optional justify.
10+
factory CssPlaceItems(CssAlignItems align, [CssJustifyItems? justify]) =
11+
_CssPlaceItemsShorthand;
12+
13+
/// CSS variable reference.
14+
factory CssPlaceItems.variable(String varName) = _CssPlaceItemsVariable;
15+
16+
/// Raw CSS value escape hatch.
17+
factory CssPlaceItems.raw(String value) = _CssPlaceItemsRaw;
18+
19+
/// Global keyword (inherit, initial, unset, revert).
20+
factory CssPlaceItems.global(CssGlobal global) = _CssPlaceItemsGlobal;
21+
}
22+
23+
final class _CssPlaceItemsShorthand extends CssPlaceItems {
24+
final CssAlignItems align;
25+
final CssJustifyItems? justify;
26+
const _CssPlaceItemsShorthand(this.align, [this.justify]) : super._();
27+
28+
@override
29+
String toCss() {
30+
if (justify == null) return align.toCss();
31+
return '${align.toCss()} ${justify!.toCss()}';
32+
}
33+
}
34+
35+
final class _CssPlaceItemsVariable extends CssPlaceItems {
36+
final String varName;
37+
const _CssPlaceItemsVariable(this.varName) : super._();
38+
39+
@override
40+
String toCss() => 'var(--$varName)';
41+
}
42+
43+
final class _CssPlaceItemsRaw extends CssPlaceItems {
44+
final String value;
45+
const _CssPlaceItemsRaw(this.value) : super._();
46+
47+
@override
48+
String toCss() => value;
49+
}
50+
51+
final class _CssPlaceItemsGlobal extends CssPlaceItems {
52+
final CssGlobal global;
53+
const _CssPlaceItemsGlobal(this.global) : super._();
54+
55+
@override
56+
String toCss() => global.toCss();
57+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import 'css_value.dart';
2+
3+
/// CSS scroll-behavior property values.
4+
sealed class CssScrollBehavior implements CssValue {
5+
const CssScrollBehavior._();
6+
7+
static const CssScrollBehavior auto = _CssScrollBehaviorKeyword('auto');
8+
static const CssScrollBehavior smooth = _CssScrollBehaviorKeyword('smooth');
9+
10+
/// CSS variable reference.
11+
factory CssScrollBehavior.variable(String varName) =
12+
_CssScrollBehaviorVariable;
13+
14+
/// Raw CSS value escape hatch.
15+
factory CssScrollBehavior.raw(String value) = _CssScrollBehaviorRaw;
16+
17+
/// Global keyword (inherit, initial, unset, revert).
18+
factory CssScrollBehavior.global(CssGlobal global) = _CssScrollBehaviorGlobal;
19+
}
20+
21+
final class _CssScrollBehaviorKeyword extends CssScrollBehavior {
22+
final String keyword;
23+
const _CssScrollBehaviorKeyword(this.keyword) : super._();
24+
25+
@override
26+
String toCss() => keyword;
27+
}
28+
29+
final class _CssScrollBehaviorVariable extends CssScrollBehavior {
30+
final String varName;
31+
const _CssScrollBehaviorVariable(this.varName) : super._();
32+
33+
@override
34+
String toCss() => 'var(--$varName)';
35+
}
36+
37+
final class _CssScrollBehaviorRaw extends CssScrollBehavior {
38+
final String value;
39+
const _CssScrollBehaviorRaw(this.value) : super._();
40+
41+
@override
42+
String toCss() => value;
43+
}
44+
45+
final class _CssScrollBehaviorGlobal extends CssScrollBehavior {
46+
final CssGlobal global;
47+
const _CssScrollBehaviorGlobal(this.global) : super._();
48+
49+
@override
50+
String toCss() => global.toCss();
51+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import 'css_value.dart';
2+
3+
/// CSS text-overflow property values.
4+
sealed class CssTextOverflow implements CssValue {
5+
const CssTextOverflow._();
6+
7+
static const CssTextOverflow clip = _CssTextOverflowKeyword('clip');
8+
static const CssTextOverflow ellipsis = _CssTextOverflowKeyword('ellipsis');
9+
10+
/// Custom string value (outputs with quotes, e.g., `"…"`).
11+
factory CssTextOverflow.value(String value) = _CssTextOverflowValue;
12+
13+
/// CSS variable reference.
14+
factory CssTextOverflow.variable(String varName) = _CssTextOverflowVariable;
15+
16+
/// Raw CSS value escape hatch.
17+
factory CssTextOverflow.raw(String value) = _CssTextOverflowRaw;
18+
19+
/// Global keyword (inherit, initial, unset, revert).
20+
factory CssTextOverflow.global(CssGlobal global) = _CssTextOverflowGlobal;
21+
}
22+
23+
final class _CssTextOverflowKeyword extends CssTextOverflow {
24+
final String keyword;
25+
const _CssTextOverflowKeyword(this.keyword) : super._();
26+
27+
@override
28+
String toCss() => keyword;
29+
}
30+
31+
final class _CssTextOverflowValue extends CssTextOverflow {
32+
final String value;
33+
const _CssTextOverflowValue(this.value) : super._();
34+
35+
@override
36+
String toCss() => '"$value"';
37+
}
38+
39+
final class _CssTextOverflowVariable extends CssTextOverflow {
40+
final String varName;
41+
const _CssTextOverflowVariable(this.varName) : super._();
42+
43+
@override
44+
String toCss() => 'var(--$varName)';
45+
}
46+
47+
final class _CssTextOverflowRaw extends CssTextOverflow {
48+
final String value;
49+
const _CssTextOverflowRaw(this.value) : super._();
50+
51+
@override
52+
String toCss() => value;
53+
}
54+
55+
final class _CssTextOverflowGlobal extends CssTextOverflow {
56+
final CssGlobal global;
57+
const _CssTextOverflowGlobal(this.global) : super._();
58+
59+
@override
60+
String toCss() => global.toCss();
61+
}

packages/spark_css/lib/src/css_types/css_types.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ library;
33

44
export 'css_animation.dart';
55
export 'css_angle.dart';
6+
export 'css_aspect_ratio.dart';
67
export 'css_background.dart';
78
export 'css_background_attachment.dart';
89
export 'css_background_clip.dart';
@@ -33,13 +34,16 @@ export 'css_number.dart';
3334
export 'css_object_fit.dart';
3435
export 'css_outline.dart';
3536
export 'css_overflow.dart';
37+
export 'css_place_items.dart';
3638
export 'css_pointer_events.dart';
3739
export 'css_position.dart';
3840
export 'css_radial_shape.dart';
3941
export 'css_radial_size.dart';
4042
export 'css_resize.dart';
43+
export 'css_scroll_behavior.dart';
4144
export 'css_spacing.dart';
4245
export 'css_text.dart';
46+
export 'css_text_overflow.dart';
4347
export 'css_text_shadow.dart';
4448
export 'css_transform.dart';
4549
export 'css_transition.dart';

packages/spark_css/lib/src/style.dart

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,7 @@ class Style implements CssStyle {
237237
// Layout
238238
CssDisplay? display,
239239
CssPosition? position,
240+
CssAspectRatio? aspectRatio,
240241
// Sizing
241242
CssLength? width,
242243
CssLength? height,
@@ -271,6 +272,7 @@ class Style implements CssStyle {
271272
CssNumber? flexShrink,
272273
CssLength? gap,
273274
CssFlexShorthand? flex,
275+
CssPlaceItems? placeItems,
274276
// Typography
275277
CssLength? fontSize,
276278
CssFontWeight? fontWeight,
@@ -284,6 +286,7 @@ class Style implements CssStyle {
284286
CssNumber? lineHeight,
285287
CssLength? letterSpacing,
286288
CssTextShadow? textShadow,
289+
CssTextOverflow? textOverflow,
287290
// Borders
288291
CssBorder? border,
289292
CssBorder? borderTop,
@@ -310,6 +313,7 @@ class Style implements CssStyle {
310313
CssObjectFit? objectFit,
311314
CssPointerEvents? pointerEvents,
312315
CssResize? resize,
316+
CssScrollBehavior? scrollBehavior,
313317
CssFilter? filter,
314318
CssFilter? backdropFilter,
315319
// Backgrounds
@@ -348,6 +352,9 @@ class Style implements CssStyle {
348352
// Layout
349353
if (display != null) _properties['display'] = display.toCss();
350354
if (position != null) _properties['position'] = position.toCss();
355+
if (aspectRatio != null) {
356+
_properties['aspect-ratio'] = aspectRatio.toCss();
357+
}
351358

352359
// Sizing
353360
if (width != null) _properties['width'] = width.toCss();
@@ -398,6 +405,7 @@ class Style implements CssStyle {
398405
if (flexShrink != null) _properties['flex-shrink'] = flexShrink.toCss();
399406
if (gap != null) _properties['gap'] = gap.toCss();
400407
if (flex != null) _properties['flex'] = flex.toCss();
408+
if (placeItems != null) _properties['place-items'] = placeItems.toCss();
401409

402410
// Typography
403411
if (fontSize != null) _properties['font-size'] = fontSize.toCss();
@@ -418,6 +426,9 @@ class Style implements CssStyle {
418426
_properties['letter-spacing'] = letterSpacing.toCss();
419427
}
420428
if (textShadow != null) _properties['text-shadow'] = textShadow.toCss();
429+
if (textOverflow != null) {
430+
_properties['text-overflow'] = textOverflow.toCss();
431+
}
421432

422433
// Borders
423434
if (border != null) _properties['border'] = border.toCss();
@@ -464,6 +475,9 @@ class Style implements CssStyle {
464475
_properties['pointer-events'] = pointerEvents.toCss();
465476
}
466477
if (resize != null) _properties['resize'] = resize.toCss();
478+
if (scrollBehavior != null) {
479+
_properties['scroll-behavior'] = scrollBehavior.toCss();
480+
}
467481
if (filter != null) _properties['filter'] = filter.toCss();
468482
if (backdropFilter != null) {
469483
_properties['backdrop-filter'] = backdropFilter.toCss();
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import 'package:spark_css/spark_css.dart';
2+
import 'package:test/test.dart';
3+
4+
void main() {
5+
group('CssAspectRatio', () {
6+
test('auto keyword outputs correct CSS', () {
7+
expect(CssAspectRatio.auto.toCss(), equals('auto'));
8+
});
9+
test('ratio outputs correct CSS', () {
10+
expect(CssAspectRatio.ratio(16, 9).toCss(), equals('16 / 9'));
11+
expect(CssAspectRatio.ratio(4, 3).toCss(), equals('4 / 3'));
12+
});
13+
test('number outputs correct CSS', () {
14+
expect(CssAspectRatio.number(1).toCss(), equals('1'));
15+
expect(CssAspectRatio.number(0.5).toCss(), equals('0.5'));
16+
});
17+
test('variable outputs correct CSS', () {
18+
expect(CssAspectRatio.variable('ar').toCss(), equals('var(--ar)'));
19+
});
20+
test('raw outputs value as-is', () {
21+
expect(CssAspectRatio.raw('16 / 9').toCss(), equals('16 / 9'));
22+
});
23+
test('global outputs correct CSS', () {
24+
expect(
25+
CssAspectRatio.global(CssGlobal.inherit).toCss(),
26+
equals('inherit'),
27+
);
28+
});
29+
test('Style.typed integration', () {
30+
final style = Style.typed(aspectRatio: CssAspectRatio.ratio(16, 9));
31+
expect(style.toCss(), contains('aspect-ratio: 16 / 9;'));
32+
});
33+
});
34+
}

0 commit comments

Comments
 (0)