@scalewing/react 1.25.0 → 1.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/README.md +110 -1
  3. package/dist/components/ActionBar.d.ts +14 -0
  4. package/dist/components/ActionBar.js +5 -2
  5. package/dist/components/DenominationGrid.d.ts +1 -1
  6. package/dist/components/DenominationGrid.js +3 -0
  7. package/dist/components/DescriptionList.d.ts +28 -0
  8. package/dist/components/DescriptionList.js +15 -0
  9. package/dist/components/Stepper.d.ts +44 -0
  10. package/dist/components/Stepper.js +77 -0
  11. package/dist/components/Steps.d.ts +18 -0
  12. package/dist/components/Steps.js +34 -0
  13. package/dist/components/action-bar/ActionBarSummary.d.ts +22 -0
  14. package/dist/components/action-bar/ActionBarSummary.js +23 -0
  15. package/dist/components/denomination-grid/ColumnHeadText.d.ts +7 -0
  16. package/dist/components/denomination-grid/ColumnHeadText.js +13 -0
  17. package/dist/components/denomination-grid/DenominationStrip.js +3 -7
  18. package/dist/components/denomination-grid/DenominationTiles.js +2 -1
  19. package/dist/components/denomination-grid/EntryCell.d.ts +15 -0
  20. package/dist/components/denomination-grid/EntryCell.js +16 -0
  21. package/dist/components/denomination-grid/RowLabelLine.d.ts +3 -2
  22. package/dist/components/denomination-grid/RowLabelLine.js +5 -3
  23. package/dist/components/denomination-grid/StripRow.d.ts +13 -0
  24. package/dist/components/denomination-grid/StripRow.js +19 -0
  25. package/dist/components/denomination-grid/types.d.ts +44 -1
  26. package/dist/components/denomination-grid/validate-entry.d.ts +15 -0
  27. package/dist/components/denomination-grid/validate-entry.js +78 -0
  28. package/dist/components/steps/Step.d.ts +24 -0
  29. package/dist/components/steps/Step.js +62 -0
  30. package/dist/components/steps/step-status.d.ts +26 -0
  31. package/dist/components/steps/step-status.js +37 -0
  32. package/dist/components/steps/steps-context.d.ts +16 -0
  33. package/dist/components/steps/steps-context.js +3 -0
  34. package/dist/css/css-action-bar-summary.d.ts +2 -0
  35. package/dist/css/css-action-bar-summary.js +48 -0
  36. package/dist/css/css-action-bar.js +4 -0
  37. package/dist/css/css-components.js +10 -0
  38. package/dist/css/css-denomination-entry.d.ts +2 -0
  39. package/dist/css/css-denomination-entry.js +78 -0
  40. package/dist/css/css-denomination-grid.js +4 -0
  41. package/dist/css/css-description-list.d.ts +12 -0
  42. package/dist/css/css-description-list.js +63 -0
  43. package/dist/css/css-stepper.d.ts +10 -0
  44. package/dist/css/css-stepper.js +126 -0
  45. package/dist/css/css-steps.d.ts +9 -0
  46. package/dist/css/css-steps.js +203 -0
  47. package/dist/css/stylesheet.js +6 -0
  48. package/dist/index.d.ts +6 -2
  49. package/dist/index.js +4 -1
  50. package/dist/stepper-count.d.ts +37 -0
  51. package/dist/stepper-count.js +50 -0
  52. package/dist/styles.css +367 -0
  53. package/package.json +1 -1
@@ -0,0 +1,50 @@
1
+ /** A consumer bug, not a runtime state: the bounds must describe a range. */
2
+ export function assertStepperBounds(bounds) {
3
+ const { min, max, step } = bounds;
4
+ if (!Number.isSafeInteger(min) || !Number.isSafeInteger(step))
5
+ throw new RangeError('Stepper min and step must be whole numbers');
6
+ if (max !== Number.POSITIVE_INFINITY && !Number.isSafeInteger(max))
7
+ throw new RangeError('Stepper max must be a whole number');
8
+ if (min > max)
9
+ throw new RangeError(`Stepper min ${min} is greater than max ${max}`);
10
+ if (step <= 0)
11
+ throw new RangeError(`Stepper step ${step} must be greater than zero`);
12
+ }
13
+ function clamp(value, bounds) {
14
+ return Math.min(Math.max(value, bounds.min), bounds.max);
15
+ }
16
+ /**
17
+ * The counts − and + would move to. An empty box counts as `min`. A count
18
+ * outside the bounds (typed) moves back inside them; a bound stops its own
19
+ * direction, and a locked (disabled or read-only) stepper stops both.
20
+ */
21
+ export function stepperMoves(value, bounds, locked) {
22
+ if (locked)
23
+ return { down: null, up: null };
24
+ const base = value ?? bounds.min;
25
+ return {
26
+ down: base > bounds.min ? clamp(base - bounds.step, bounds) : null,
27
+ up: base < bounds.max ? clamp(base + bounds.step, bounds) : null,
28
+ };
29
+ }
30
+ /**
31
+ * Reads what the person typed. Digits make a count, with a leading minus
32
+ * only when the range goes below zero; an empty box, or a lone minus on its
33
+ * way to a negative count, is no count. Anything else, or a count too large
34
+ * to be exact, is refused and the box keeps its last text.
35
+ */
36
+ export function readStepperEntry(text, allowsNegative) {
37
+ if (text === '' || (allowsNegative && text === '-'))
38
+ return { kind: 'empty' };
39
+ const pattern = allowsNegative ? /^-?\d+$/ : /^\d+$/;
40
+ if (!pattern.test(text))
41
+ return { kind: 'refused' };
42
+ const value = Number(text);
43
+ if (!Number.isSafeInteger(value))
44
+ return { kind: 'refused' };
45
+ return { kind: 'count', value: Object.is(value, -0) ? 0 : value };
46
+ }
47
+ /** The box's text for a count: the plain number, or empty for none. */
48
+ export function stepperText(value) {
49
+ return value === null ? '' : String(value);
50
+ }
package/dist/styles.css CHANGED
@@ -2707,6 +2707,33 @@ body {
2707
2707
  .sw-action-bar-actions > * { flex: 1 1 auto; }
2708
2708
  }
2709
2709
 
2710
+ .sw-action-bar-summary {
2711
+ align-items: baseline;
2712
+ column-gap: var(--sw-space-2);
2713
+ display: inline-flex;
2714
+ flex-wrap: wrap;
2715
+ min-width: 0;
2716
+ }
2717
+
2718
+ .sw-action-bar-summary-label,
2719
+ .sw-action-bar-summary-detail {
2720
+ color: var(--sw-color-muted);
2721
+ font-size: 15px;
2722
+ letter-spacing: -0.2px;
2723
+ line-height: 20px;
2724
+ }
2725
+
2726
+ .sw-action-bar-summary-value {
2727
+ color: var(--sw-color-text);
2728
+ }
2729
+
2730
+ .sw-action-bar-summary-danger .sw-action-bar-summary-value { color: var(--sw-color-danger); }
2731
+ .sw-action-bar-summary-success .sw-action-bar-summary-value { color: var(--sw-color-success); }
2732
+
2733
+ @media (forced-colors: active) {
2734
+ .sw-action-bar-summary-value { color: CanvasText; }
2735
+ }
2736
+
2710
2737
  .sw-action-bar-sticky {
2711
2738
  bottom: calc(var(--sw-space-2) + env(safe-area-inset-bottom, 0px));
2712
2739
  position: sticky;
@@ -3039,6 +3066,49 @@ body {
3039
3066
  .sw-stat-tile-primary { background: Highlight; }
3040
3067
  }
3041
3068
 
3069
+ .sw-description-list {
3070
+ column-gap: var(--sw-space-4);
3071
+ display: grid;
3072
+ grid-template-columns: fit-content(40%) minmax(0, 1fr);
3073
+ margin: 0;
3074
+ min-width: 0;
3075
+ }
3076
+
3077
+ .sw-description-item {
3078
+ align-items: start;
3079
+ display: grid;
3080
+ grid-column: 1 / -1;
3081
+ grid-template-columns: subgrid;
3082
+ padding-block: var(--sw-space-2);
3083
+ row-gap: var(--sw-space-1);
3084
+ }
3085
+
3086
+ .sw-description-item:first-child { padding-top: 0; }
3087
+
3088
+ .sw-description-item:last-child { padding-bottom: 0; }
3089
+
3090
+ .sw-description-item + .sw-description-item {
3091
+ border-top: 1px solid var(--sw-color-border);
3092
+ }
3093
+
3094
+ .sw-description-term,
3095
+ .sw-description-detail {
3096
+ align-items: flex-start;
3097
+ display: flex;
3098
+ flex-direction: column;
3099
+ margin: 0;
3100
+ min-width: 0;
3101
+ overflow-wrap: anywhere;
3102
+ }
3103
+
3104
+ @media not all and (min-width: 48rem) {
3105
+ .sw-description-list { grid-template-columns: minmax(0, 1fr); }
3106
+ }
3107
+
3108
+ @media (forced-colors: active) {
3109
+ .sw-description-item + .sw-description-item { border-top-color: CanvasText; }
3110
+ }
3111
+
3042
3112
  .sw-tabs {
3043
3113
  border-bottom: 1px solid var(--sw-color-border);
3044
3114
  display: flex;
@@ -3565,6 +3635,57 @@ body {
3565
3635
  .sw-denomination-grid .sw-denomination-cell-toned .sw-denomination-cell-zero { color: GrayText; }
3566
3636
  }
3567
3637
 
3638
+ .sw-denomination-strip td.sw-denomination-entry {
3639
+ padding: var(--sw-space-1);
3640
+ }
3641
+
3642
+ .sw-denomination-entry .sw-stepper-full .sw-stepper-track {
3643
+ grid-template-columns: var(--sw-space-6) minmax(var(--sw-space-8), 1fr) var(--sw-space-6);
3644
+ }
3645
+
3646
+ /* A strip with entry rows centres its heads and read-only counts over the steppers. */
3647
+ .sw-denomination-strip-entry .sw-denomination-head:not(.sw-denomination-total-head),
3648
+ .sw-denomination-strip-entry .sw-denomination-cell {
3649
+ text-align: center;
3650
+ }
3651
+
3652
+ [data-theme] .sw-denomination-cell-toned .sw-stepper:not(.sw-stepper-invalid) input.sw-stepper-input:not(:disabled) {
3653
+ border-color: var(--sw-denomination-cell-tone);
3654
+ box-shadow: inset 0 0 0 1px var(--sw-denomination-cell-tone);
3655
+ }
3656
+
3657
+ .sw-denomination-label-words {
3658
+ display: flex;
3659
+ flex-direction: column;
3660
+ min-width: 0;
3661
+ }
3662
+
3663
+ .sw-denomination-label-caption,
3664
+ .sw-denomination-head-caption {
3665
+ color: var(--sw-color-muted);
3666
+ font-size: 13px;
3667
+ font-weight: 400;
3668
+ letter-spacing: -0.08px;
3669
+ line-height: 18px;
3670
+ }
3671
+
3672
+ .sw-denomination-row-quiet,
3673
+ .sw-denomination-strip .sw-denomination-row-quiet .sw-denomination-label {
3674
+ background: var(--sw-color-subtle);
3675
+ }
3676
+
3677
+ .sw-denomination-strip .sw-denomination-row-disabled .sw-denomination-label-body,
3678
+ .sw-denomination-strip .sw-denomination-row-disabled .sw-denomination-cell,
3679
+ .sw-denomination-strip .sw-denomination-row-disabled .sw-denomination-total {
3680
+ opacity: var(--sw-disabled-opacity);
3681
+ }
3682
+
3683
+ @media (forced-colors: active) {
3684
+ [data-theme] .sw-denomination-cell-toned .sw-stepper:not(.sw-stepper-invalid) input.sw-stepper-input:not(:disabled) { border-color: Highlight; box-shadow: inset 0 0 0 1px Highlight; }
3685
+ .sw-denomination-label-caption,
3686
+ .sw-denomination-head-caption { color: CanvasText; }
3687
+ }
3688
+
3568
3689
  .sw-denomination-scroll-aligned {
3569
3690
  container-type: inline-size;
3570
3691
  }
@@ -3748,6 +3869,251 @@ body {
3748
3869
  .sw-field-error, .sw-field-required { color: Mark; }
3749
3870
  }
3750
3871
 
3872
+ .sw-stepper {
3873
+ align-items: flex-start;
3874
+ display: inline-flex;
3875
+ flex-direction: column;
3876
+ gap: var(--sw-space-1);
3877
+ min-width: 0;
3878
+ /* Holds its hidden label and empty error region, which leave the flow,
3879
+ so inside a scroll region they never widen the page. */
3880
+ position: relative;
3881
+ }
3882
+
3883
+ .sw-stepper-full { align-items: stretch; display: flex; }
3884
+
3885
+ .sw-stepper-track {
3886
+ display: inline-grid;
3887
+ gap: var(--sw-space-1);
3888
+ grid-template-columns: var(--sw-space-6) auto var(--sw-space-6);
3889
+ }
3890
+
3891
+ .sw-stepper-full .sw-stepper-track {
3892
+ display: grid;
3893
+ grid-template-columns: var(--sw-space-6) minmax(0, 1fr) var(--sw-space-6);
3894
+ }
3895
+
3896
+ .sw-stepper-button {
3897
+ align-items: center;
3898
+ appearance: none;
3899
+ background: var(--sw-color-subtle);
3900
+ border: 1px solid transparent;
3901
+ border-radius: var(--sw-radius-sm);
3902
+ color: var(--sw-color-text);
3903
+ cursor: pointer;
3904
+ display: inline-flex;
3905
+ font-family: var(--sw-font-sans);
3906
+ font-size: 20px;
3907
+ font-weight: 600;
3908
+ justify-content: center;
3909
+ line-height: 28px;
3910
+ margin: 0;
3911
+ min-height: var(--sw-control-md-min-height);
3912
+ padding: 0;
3913
+ touch-action: manipulation;
3914
+ }
3915
+
3916
+ .sw-stepper-button:hover:not(:disabled) {
3917
+ background: var(--sw-color-border);
3918
+ }
3919
+
3920
+ .sw-stepper-button:disabled {
3921
+ cursor: not-allowed;
3922
+ opacity: var(--sw-disabled-opacity);
3923
+ }
3924
+
3925
+ [data-theme] input.sw-stepper-input {
3926
+ font-size: 17px;
3927
+ font-variant-numeric: tabular-nums;
3928
+ font-weight: 600;
3929
+ inline-size: calc(5ch + 2 * var(--sw-space-1));
3930
+ line-height: 25px;
3931
+ min-width: 0;
3932
+ padding-inline: var(--sw-space-1);
3933
+ text-align: center;
3934
+ }
3935
+
3936
+ .sw-stepper-full input.sw-stepper-input { inline-size: 100%; }
3937
+
3938
+ [data-theme] .sw-stepper-invalid input.sw-stepper-input {
3939
+ border-color: var(--sw-color-danger);
3940
+ }
3941
+
3942
+ [data-theme] .sw-stepper-changed input.sw-stepper-input {
3943
+ border-color: var(--sw-color-accent);
3944
+ box-shadow: inset 0 0 0 1px var(--sw-color-accent);
3945
+ }
3946
+
3947
+ [data-theme] .sw-stepper-changed input.sw-stepper-input:not(:placeholder-shown) {
3948
+ background-image: linear-gradient(color-mix(in srgb, var(--sw-color-accent) 12%, transparent), color-mix(in srgb, var(--sw-color-accent) 12%, transparent));
3949
+ }
3950
+
3951
+ /* Read-only: the buttons keep their room, so the count lines up with an editable one. */
3952
+ .sw-stepper-readonly .sw-stepper-button { visibility: hidden; }
3953
+
3954
+ @media (forced-colors: active) {
3955
+ .sw-stepper-button { border-color: ButtonText; }
3956
+ .sw-stepper-button:disabled { border-color: GrayText; color: GrayText; opacity: 1; }
3957
+ [data-theme] .sw-stepper-invalid input.sw-stepper-input { border-color: Mark; }
3958
+ [data-theme] .sw-stepper-changed input.sw-stepper-input { border-color: Highlight; box-shadow: inset 0 0 0 1px Highlight; background-image: none; }
3959
+ }
3960
+
3961
+ .sw-steps {
3962
+ display: flex;
3963
+ flex-direction: column;
3964
+ gap: var(--sw-space-2);
3965
+ list-style: none;
3966
+ margin: 0;
3967
+ padding: 0;
3968
+ }
3969
+
3970
+ .sw-steps-item {
3971
+ --sw-steps-padding: var(--sw-space-3);
3972
+ background: var(--sw-glass-fill);
3973
+ border: 1px solid var(--sw-glass-border);
3974
+ border-radius: var(--sw-radius-md);
3975
+ box-shadow: inset 0 1px 0 var(--sw-glass-specular);
3976
+ color: var(--sw-color-text);
3977
+ display: flex;
3978
+ flex-direction: column;
3979
+ gap: var(--sw-space-4);
3980
+ padding: var(--sw-steps-padding) var(--sw-space-4);
3981
+ position: relative;
3982
+ }
3983
+
3984
+ .sw-steps-item-current,
3985
+ .sw-steps-item-error {
3986
+ --sw-steps-padding: var(--sw-space-4);
3987
+ }
3988
+
3989
+ /* The marker sits at the top of the header, so the connector always starts
3990
+ under it; each line of the header is centred on the marker's height. */
3991
+ .sw-steps-header {
3992
+ align-items: flex-start;
3993
+ display: flex;
3994
+ gap: var(--sw-space-3);
3995
+ min-height: var(--sw-control-xs-min-height);
3996
+ min-width: 0;
3997
+ }
3998
+
3999
+ .sw-steps-marker {
4000
+ align-items: center;
4001
+ border-radius: var(--sw-radius-pill);
4002
+ box-sizing: border-box;
4003
+ display: inline-flex;
4004
+ flex: none;
4005
+ font-family: var(--sw-font-sans);
4006
+ font-size: 13px;
4007
+ font-variant-numeric: tabular-nums;
4008
+ font-weight: 700;
4009
+ height: var(--sw-control-xs-min-height);
4010
+ justify-content: center;
4011
+ line-height: 18px;
4012
+ width: var(--sw-control-xs-min-height);
4013
+ }
4014
+
4015
+ .sw-steps-title {
4016
+ font-family: var(--sw-font-sans);
4017
+ font-size: 17px;
4018
+ font-weight: 600;
4019
+ letter-spacing: -0.2px;
4020
+ line-height: 25px;
4021
+ margin: 0;
4022
+ min-width: 0;
4023
+ padding-block: calc((var(--sw-control-xs-min-height) - 25px) / 2);
4024
+ }
4025
+
4026
+ .sw-steps-item-current .sw-steps-title,
4027
+ .sw-steps-item-error .sw-steps-title {
4028
+ font-size: 20px;
4029
+ letter-spacing: -0.3px;
4030
+ line-height: 28px;
4031
+ padding-block: calc((var(--sw-control-xs-min-height) - 28px) / 2);
4032
+ }
4033
+
4034
+ .sw-steps-item-upcoming .sw-steps-title,
4035
+ .sw-steps-item-skipped .sw-steps-title,
4036
+ .sw-steps-reason {
4037
+ color: var(--sw-color-muted);
4038
+ }
4039
+
4040
+ .sw-steps-summary {
4041
+ flex: 1 1 0;
4042
+ font-size: 15px;
4043
+ letter-spacing: -0.2px;
4044
+ line-height: 20px;
4045
+ min-width: 25%;
4046
+ overflow: hidden;
4047
+ padding-block: calc((var(--sw-control-xs-min-height) - 20px) / 2);
4048
+ text-overflow: ellipsis;
4049
+ white-space: nowrap;
4050
+ }
4051
+
4052
+ .sw-button.sw-steps-edit {
4053
+ flex: none;
4054
+ margin-block: calc((var(--sw-control-xs-min-height) - var(--sw-control-sm-min-height)) / 2);
4055
+ margin-inline-start: auto;
4056
+ }
4057
+
4058
+ .sw-steps-body {
4059
+ margin-inline-start: calc(var(--sw-control-xs-min-height) + var(--sw-space-3));
4060
+ min-width: 0;
4061
+ }
4062
+
4063
+ .sw-steps-item-upcoming .sw-steps-marker,
4064
+ .sw-steps-item-skipped .sw-steps-marker {
4065
+ box-shadow: inset 0 0 0 2px var(--sw-color-border);
4066
+ color: var(--sw-color-muted);
4067
+ }
4068
+
4069
+ .sw-steps-item-current .sw-steps-marker {
4070
+ background: var(--sw-color-accent);
4071
+ color: var(--sw-color-onAccent);
4072
+ }
4073
+
4074
+ .sw-steps-item-error .sw-steps-marker {
4075
+ background: var(--sw-color-danger);
4076
+ color: var(--sw-color-onDanger);
4077
+ }
4078
+
4079
+ .sw-steps-item-done .sw-steps-marker {
4080
+ background: var(--sw-color-success);
4081
+ }
4082
+
4083
+ /* Private chrome: a check drawn with two borders, as Checkbox's. */
4084
+ .sw-steps-item-done .sw-steps-marker::after {
4085
+ border-bottom: 2px solid var(--sw-color-surface);
4086
+ border-right: 2px solid var(--sw-color-surface);
4087
+ content: '';
4088
+ height: var(--sw-space-3);
4089
+ margin-top: calc(-1 * var(--sw-space-1) / 2);
4090
+ transform: rotate(45deg);
4091
+ width: calc(var(--sw-space-3) / 2);
4092
+ }
4093
+
4094
+ .sw-steps-item:not(:last-child)::after {
4095
+ background: var(--sw-color-border);
4096
+ bottom: calc(-1 * var(--sw-space-2) - 1px);
4097
+ content: '';
4098
+ inset-inline-start: calc(var(--sw-space-4) + var(--sw-control-xs-min-height) / 2 - 1px);
4099
+ position: absolute;
4100
+ top: calc(var(--sw-steps-padding) + var(--sw-control-xs-min-height) + var(--sw-space-1));
4101
+ width: 2px;
4102
+ }
4103
+
4104
+ .sw-steps-item-done:not(:last-child)::after {
4105
+ background: var(--sw-color-success);
4106
+ }
4107
+
4108
+ @media (forced-colors: active) {
4109
+ .sw-steps-marker { border: 2px solid CanvasText; box-shadow: none; }
4110
+ .sw-steps-item-current .sw-steps-marker { border-color: Highlight; }
4111
+ .sw-steps-item-done .sw-steps-marker::after { border-color: CanvasText; }
4112
+ .sw-steps-item:not(:last-child)::after { background: CanvasText; }
4113
+ .sw-steps-item-upcoming .sw-steps-title,
4114
+ .sw-steps-item-skipped .sw-steps-title { color: GrayText; }
4115
+ }
4116
+
3751
4117
  .sw-split {
3752
4118
  align-items: stretch;
3753
4119
  align-self: start;
@@ -3949,6 +4315,7 @@ body {
3949
4315
  .sw-card-glass,
3950
4316
  .sw-app-header,
3951
4317
  .sw-action-bar,
4318
+ .sw-steps-item,
3952
4319
  .sw-badge-neutral,
3953
4320
  .sw-segmented,
3954
4321
  .sw-table-sticky thead th,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scalewing/react",
3
- "version": "1.25.0",
3
+ "version": "1.27.0",
4
4
  "description": "Scalewing DOM components and package-owned CSS utilities.",
5
5
  "type": "module",
6
6
  "license": "MIT",