A component that renders a Bootstrap Progress element using OverReact’s statically-typed React prop API.
Tags scale to match the size of the immediate parent element by using relative font sizing
and em
units.
import 'package:over_react/over_react.dart';
import '../../demo_components.dart';
ReactElement progressBasicDemo() => Fragment()(
(Progress()
..showCaption = true
..captionProps = (domProps()..className = 'text-xs-center')
..caption = 'Reticulating splines...'
)(),
(Progress()
..value = 25.0
..showCaption = true
..captionProps = (domProps()..className = 'text-xs-center')
..caption = 'Reticulating splines...'
)(),
(Progress()
..value = 50.0
..showCaption = true
..captionProps = (domProps()..className = 'text-xs-center')
..caption = 'Reticulating splines...'
)(),
(Progress()
..value = 75.0
..showCaption = true
..captionProps = (domProps()..className = 'text-xs-center')
..caption = 'Reticulating splines...'
)(),
(Progress()
..value = 100.0
..showCaption = true
..captionProps = (domProps()..className = 'text-xs-center')
..caption = 'Reticulating splines...'
)()
);
import 'dart:math';
import 'package:over_react/over_react.dart';
part 'progress.over_react.g.dart';
UiFactory<ProgressProps> Progress = _$Progress;
mixin ProgressProps on UiProps {
/// The current value of the [Progress] component.
///
/// This value should be between the [min] and [max] values.
///
/// Default: `0.0`
double value;
/// The min value of the [Progress] component.
///
/// Default: `0.0`
double min;
/// The max value of the [Progress] component.
///
/// Default: `100.0`
double max;
/// The skin / "context" for the [Progress] component.
///
/// See: <https://getbootstrap.com/docs/4.4/components/progress/#contextual-alternatives>.
///
/// Default: [ProgressSkin.DEFAULT]
ProgressSkin skin;
/// Whether to render a "Barber Pole" gradient stripe effect in the [Progress] component.
///
/// Default: false
bool isStriped;
/// Whether to animate the "Barber Pole" gradient stripe effect in the [Progress] component.
///
/// __Note:__ Has no effect if [isStriped] is `false`.
///
/// Default: false
bool isAnimated;
/// Optionally add a caption that describes the context of the [Progress] component.
///
/// See: <https://getbootstrap.com/docs/4.4/components/progress/#example>.
///
/// Default: [ProgressComponent._getPercentComplete]%
String caption;
/// Additional props to be added to the [caption] element _(if specified)_.
Map captionProps;
/// Whether the [caption] should be visible.
///
/// Default: false
bool showCaption;
/// Whether the [caption] should be appended with the value of [value].
///
/// Default: true
bool showPercentComplete;
/// Additional props to be added to the [Dom.div] that wraps around the [caption] element and `<progress>` element.
Map rootNodeProps;
}
mixin ProgressState on UiState {
/// An autogenerated GUID, used as a fallback when [ProgressProps.id] is unspecified, and
/// saved on the state so it will persist across remounts.
///
/// HTML id attributes are needed on `<progress>` elements for proper accessibility support,
/// so this state value ensures there's always a valid ID value to use.
String id;
}
class ProgressComponent extends UiStatefulComponent2<ProgressProps, ProgressState> {
@override
get defaultProps => (newProps()
..value = 0.0
..min = 0.0
..max = 100.0
..skin = ProgressSkin.DEFAULT
..isStriped = false
..isAnimated = false
..showCaption = false
..showPercentComplete = true
);
@override
get initialState => (newState()
..id = 'progress_' + generateGuid(4)
);
@override
render() {
return (Dom.div()..addProps(props.rootNodeProps))(
renderCaptionNode(),
renderProgressNode(),
props.children
);
}
ReactElement renderProgressNode() {
return (Dom.progress()
..modifyProps(addUnconsumedDomProps)
..aria.labelledby = captionId
..className = _getProgressNodeClasses().toClassName()
..id = id
..value = currentValue
..max = props.max
)();
}
ReactElement renderCaptionNode() {
var captionClasses = ClassNameBuilder.fromProps(props.captionProps)
..add('sr-only', !props.showCaption);
var captionText = props.caption ?? '';
if (props.showPercentComplete) {
captionText += ' ${_getPercentComplete()}%';
}
return (Dom.div()
..addProps(props.captionProps)
..id = captionId
..className = captionClasses.toClassName()
)(captionText);
}
ClassNameBuilder _getProgressNodeClasses() {
return ClassNameBuilder()
..add('progress')
..add('progress-striped', props.isStriped)
..add('progress-animated', props.isAnimated)
..add(props.skin.className);
}
/// Get the percentage complete based on [ProgressProps.min], [ProgressProps.max] and [ProgressProps.value].
double _getPercentComplete() {
return (props.value - props.min) / (props.max - props.min) * 100;
}
/// Returns the value that determines the width of the progress bar
/// within the rendered [Progress] component via [DomPropsMixin.value].
///
/// Note that the value of the HTML `<progress>` element's value
/// attribute will never be less than the value of [ProgressProps.min].
double get currentValue => max(props.min, props.value);
String get id => props.id ?? state.id;
String get captionId => '${id}_caption';
}
class ProgressSkin extends ClassNameConstant {
const ProgressSkin._(String name, String className) : super(name, className);
/// [className] value: ''
static const ProgressSkin DEFAULT =
ProgressSkin._('DEFAULT', '');
/// [className] value: 'progress-danger'
static const ProgressSkin DANGER =
ProgressSkin._('DANGER', 'progress-danger');
/// [className] value: 'progress-success'
static const ProgressSkin SUCCESS =
ProgressSkin._('SUCCESS', 'progress-success');
/// [className] value: 'progress-warning'
static const ProgressSkin WARNING =
ProgressSkin._('WARNING', 'progress-warning');
/// [className] value: 'progress-info'
static const ProgressSkin INFO =
ProgressSkin._('INFO', 'progress-info');
}
Set props.skin
to style a Progress
using contextual colors.
import 'package:over_react/over_react.dart';
import '../../demo_components.dart';
ReactElement progressContextualDemo() => Dom.div()(
(Progress()
..value = 25.0
..skin = ProgressSkin.SUCCESS
)(),
(Progress()
..value = 50.0
..skin = ProgressSkin.INFO
)(),
(Progress()
..value = 75.0
..skin = ProgressSkin.WARNING
)(),
(Progress()
..value = 100.0
..skin = ProgressSkin.DANGER
)()
);
Set props.isStriped
to render a "Barber Pole" gradient stripe effect in a
Progress
component.
import 'package:over_react/over_react.dart';
import '../../demo_components.dart';
ReactElement progressStripedDemo() => Dom.div()(
(Progress()
..value = 10.0
..isStriped = true
)(),
(Progress()
..value = 25.0
..skin = ProgressSkin.SUCCESS
..isStriped = true
)(),
(Progress()
..value = 50.0
..skin = ProgressSkin.INFO
..isStriped = true
)(),
(Progress()
..value = 75.0
..skin = ProgressSkin.WARNING
..isStriped = true
)(),
(Progress()
..value = 100.0
..skin = ProgressSkin.DANGER
..isStriped = true
)()
);
Set props.isAnimated
to animate the stripes.
import 'package:over_react/over_react.dart';
import '../../demo_components.dart';
ReactElement progressAnimatedStripesDemo() =>
(Progress()
..value = 25.0
..isStriped = true
..isAnimated = true
)();