| import 'package:jaspr/dom.dart'; |
| import 'package:jaspr/jaspr.dart'; |
| import '../../../utils/styles.dart'; |
| |
| class ProjectStatusView extends StatelessComponent { |
| const ProjectStatusView({required this.steps, required this.completedSteps}); |
| |
| final List<String> steps; |
| final int completedSteps; |
| |
| @override |
| Component build(BuildContext context) { |
| return div( |
| classes: 'checklist-container', |
| [ |
| div(classes: 'checklist-card', [ |
| const h2(classes: 'checklist-title', [Component.text('Loading Project...')]), |
| div(classes: 'checklist-list', [ |
| for (int i = 0; i < steps.length; i++) |
| _ChecklistItem( |
| label: steps[i], |
| state: i < completedSteps |
| ? ChecklistItemState.done |
| : (i == completedSteps ? ChecklistItemState.loading : ChecklistItemState.pending), |
| ), |
| ]), |
| ]), |
| ], |
| ); |
| } |
| |
| @css |
| static List<StyleRule> get styles => [ |
| css('.checklist-container').styles( |
| display: .flex, |
| width: 100.percent, |
| height: 100.percent, |
| justifyContent: .center, |
| alignItems: .center, |
| backgroundColor: colorSurface, |
| ), |
| css('.checklist-card').styles( |
| display: .flex, |
| flexDirection: .column, |
| ), |
| css('.checklist-title').styles( |
| margin: Margin.only(bottom: 24.px), |
| color: Colors.white, |
| textAlign: .center, |
| fontSize: 18.px, |
| fontWeight: .w600, |
| letterSpacing: 0.5.px, |
| ), |
| css('.checklist-list').styles( |
| display: .flex, |
| flexDirection: .column, |
| gap: Gap.all(16.px), |
| ), |
| css('.checklist-item').styles( |
| display: .flex, |
| alignItems: .center, |
| gap: Gap.all(12.px), |
| ), |
| css('.checklist-icon-pending').styles( |
| width: 16.px, |
| height: 16.px, |
| border: Border.all(color: colorBorder, width: 2.px), |
| radius: .circular(99.px), |
| flex: const .shrink(0), |
| ), |
| css('.checklist-icon-loading').styles( |
| width: 16.px, |
| height: 16.px, |
| border: Border.all(color: colorBorder, width: 2.px), |
| radius: .circular(99.px), |
| flex: const .shrink(0), |
| raw: { |
| 'border-top-color': 'var(--color-primary)', |
| 'animation': 'checklist-spin 0.8s linear infinite', |
| }, |
| ), |
| css('.checklist-icon-done').styles( |
| display: .flex, |
| width: 16.px, |
| height: 16.px, |
| radius: .circular(99.px), |
| justifyContent: .center, |
| alignItems: .center, |
| flex: const .shrink(0), |
| color: colorOnPrimary, |
| backgroundColor: colorPrimary, |
| ), |
| css('.checklist-label-pending').styles( |
| color: colorOnSurfaceVariant, |
| fontSize: 14.px, |
| fontWeight: .w400, |
| ), |
| css('.checklist-label-loading').styles( |
| color: colorPrimary, |
| fontSize: 14.px, |
| fontWeight: .w500, |
| ), |
| css('.checklist-label-done').styles( |
| color: colorOnSurface, |
| fontSize: 14.px, |
| fontWeight: .w400, |
| ), |
| css.keyframes('checklist-spin', { |
| '0%': const Styles(raw: {'transform': 'rotate(0deg)'}), |
| '100%': const Styles(raw: {'transform': 'rotate(360deg)'}), |
| }), |
| ]; |
| } |
| |
| enum ChecklistItemState { pending, loading, done } |
| |
| class _ChecklistItem extends StatelessComponent { |
| const _ChecklistItem({required this.label, required this.state}); |
| |
| final String label; |
| final ChecklistItemState state; |
| |
| @override |
| Component build(BuildContext context) { |
| return div(classes: 'checklist-item', [ |
| switch (state) { |
| ChecklistItemState.pending => const div(classes: 'checklist-icon-pending', []), |
| ChecklistItemState.loading => const div(classes: 'checklist-icon-loading', []), |
| ChecklistItemState.done => const div(classes: 'checklist-icon-done', [ |
| svg( |
| attributes: {'width': '10', 'height': '8', 'viewBox': '0 0 10 8', 'fill': 'none'}, |
| [ |
| path( |
| attributes: { |
| 'd': 'M1 4L3.5 6.5L9 1', |
| 'stroke': 'currentColor', |
| 'stroke-width': '2', |
| 'stroke-linecap': 'round', |
| 'stroke-linejoin': 'round', |
| }, |
| [], |
| ), |
| ], |
| ), |
| ]), |
| }, |
| span( |
| classes: switch (state) { |
| ChecklistItemState.pending => 'checklist-label-pending', |
| ChecklistItemState.loading => 'checklist-label-loading', |
| ChecklistItemState.done => 'checklist-label-done', |
| }, |
| [Component.text(label)], |
| ), |
| ]); |
| } |
| } |