@visns-studio/visns-components 6.32.3 → 6.34.3
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.
- package/README.md +483 -0
- package/package.json +1 -1
- package/src/components/DataGrid.jsx +44 -2
- package/src/components/Field.jsx +32 -0
- package/src/components/Form.jsx +39 -2
- package/src/components/Navigation.jsx +274 -15
- package/src/components/Notification.jsx +13 -2
- package/src/components/TableFilter.jsx +14 -2
- package/src/components/auth/Login.jsx +11 -4
- package/src/components/auth/PasskeyEnrolPrompt.jsx +269 -0
- package/src/components/auth/Profile.jsx +422 -26
- package/src/components/auth/Reset.jsx +1 -1
- package/src/components/auth/TwoFactorAuth.jsx +1 -1
- package/src/components/auth/Verify.jsx +2 -2
- package/src/components/auth/authEndpoints.js +43 -0
- package/src/components/auth/passkeyClient.js +102 -0
- package/src/components/auth/passkeyPrompt.js +408 -0
- package/src/components/auth/profileLayout.js +45 -0
- package/src/components/controls/DataGridSearch.jsx +16 -2
- package/src/components/controls/DataGridSortSheet.jsx +2 -0
- package/src/components/emailCampaigns/BlockEditor.jsx +486 -0
- package/src/components/emailCampaigns/CampaignEditor.jsx +614 -0
- package/src/components/emailCampaigns/CampaignReport.jsx +98 -0
- package/src/components/emailCampaigns/EmailCampaigns.jsx +422 -0
- package/src/components/emailCampaigns/EmailLists.jsx +505 -0
- package/src/components/emailCampaigns/emailCampaignApi.js +135 -0
- package/src/components/generic/ActionButtons.jsx +1 -1
- package/src/components/generic/GenericAuth.jsx +197 -21
- package/src/components/generic/GenericDetail.jsx +95 -3
- package/src/components/generic/GenericMain.jsx +7 -0
- package/src/components/generic/StandardModal.jsx +71 -158
- package/src/components/sms/SmsCampaigns.jsx +2517 -0
- package/src/components/sms/SmsInbox.jsx +20 -5
- package/src/components/sms/SmsLineSettings.jsx +2 -5
- package/src/components/sms/SmsThreadPanel.jsx +20 -0
- package/src/components/sms/smsEndpoints.js +23 -0
- package/src/components/styles/DataGrid.module.scss +22 -0
- package/src/components/styles/EmailCampaigns.module.scss +922 -0
- package/src/components/styles/Form.module.scss +100 -2
- package/src/components/styles/Navigation.module.scss +322 -5
- package/src/components/styles/Notification.module.scss +140 -8
- package/src/components/styles/PasskeyEnrolPrompt.module.scss +105 -0
- package/src/components/styles/Profile.module.scss +157 -0
- package/src/components/styles/Sms.module.scss +484 -4
- package/src/components/styles/StandardModal.module.scss +273 -0
- package/src/components/styles/Vault.module.scss +5 -3
- package/src/components/styles/global.css +38 -7
- package/src/components/utils/navCollapsed.js +201 -0
- package/src/components/utils/rowActionSettings.js +76 -0
- package/src/components/utils/usePasskeysEnabled.js +84 -0
- package/src/components/utils/useShellLayout.js +131 -0
- package/src/index.js +107 -1
- package/src/utils/rememberTab.js +70 -0
|
@@ -1068,19 +1068,48 @@ $sms-mono: 'SFMono-Regular', 'Menlo', 'Consolas', monospace;
|
|
|
1068
1068
|
|
|
1069
1069
|
.pageTitle {
|
|
1070
1070
|
display: flex;
|
|
1071
|
-
|
|
1071
|
+
flex-wrap: wrap;
|
|
1072
|
+
column-gap: 0.3em;
|
|
1072
1073
|
align-items: center;
|
|
1073
1074
|
margin: 0;
|
|
1074
|
-
|
|
1075
|
-
|
|
1075
|
+
/* The same title every other screen of a host CRM draws: `.crmtitle h1` in
|
|
1076
|
+
GenericDetail/GenericIndex and PageHeader's `.title` all read
|
|
1077
|
+
--page-header-title-size and the heading colour. This was 1.5rem in the
|
|
1078
|
+
paragraph colour, a size larger than every page around it. */
|
|
1079
|
+
color: var(--heading-color, var(--s-paragraph));
|
|
1080
|
+
font-size: var(--page-header-title-size, 1.25rem);
|
|
1076
1081
|
font-weight: 700;
|
|
1077
|
-
line-height: 1.
|
|
1082
|
+
line-height: 1.25;
|
|
1083
|
+
letter-spacing: -0.02em;
|
|
1078
1084
|
|
|
1079
1085
|
svg {
|
|
1080
1086
|
color: var(--s-primary);
|
|
1081
1087
|
}
|
|
1082
1088
|
}
|
|
1083
1089
|
|
|
1090
|
+
/* A breadcrumb inside the title — `Messages › SMS campaigns`. The library's
|
|
1091
|
+
trail spec (`.crmtitle h1 a` / `h1 svg` in GenericDetail.module.scss):
|
|
1092
|
+
the parent muted at 500, a 16px chevron, the current page at 700. */
|
|
1093
|
+
.pageTitleParent {
|
|
1094
|
+
color: var(--muted-color, var(--s-muted));
|
|
1095
|
+
font-weight: 500;
|
|
1096
|
+
text-decoration: none;
|
|
1097
|
+
transition: color 0.15s ease;
|
|
1098
|
+
|
|
1099
|
+
&:hover,
|
|
1100
|
+
&:focus-visible {
|
|
1101
|
+
color: var(--paragraph-color, var(--s-paragraph));
|
|
1102
|
+
text-decoration: underline;
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
.pageTitle .pageTitleChevron {
|
|
1107
|
+
flex: 0 0 auto;
|
|
1108
|
+
max-width: 15px;
|
|
1109
|
+
margin: 0 0.4rem;
|
|
1110
|
+
color: var(--muted-color, var(--s-muted));
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1084
1113
|
.headActions {
|
|
1085
1114
|
display: flex;
|
|
1086
1115
|
flex-wrap: wrap;
|
|
@@ -1825,6 +1854,26 @@ $sms-mono: 'SFMono-Regular', 'Menlo', 'Consolas', monospace;
|
|
|
1825
1854
|
// One card: the text on top, every control in a bar along its bottom edge.
|
|
1826
1855
|
// Buttons beside a textarea line up with nothing once the box grows; a bar
|
|
1827
1856
|
// inside the same border always reads as part of the message being written.
|
|
1857
|
+
// The opted-out line, above the composer and nothing else.
|
|
1858
|
+
//
|
|
1859
|
+
// Muted on purpose. It is a fact worth knowing before typing, not a warning
|
|
1860
|
+
// about what is being typed — and everything below it stays live, because an
|
|
1861
|
+
// opt-out is about bulk sends and this is a person answering a person.
|
|
1862
|
+
.optedOutNote {
|
|
1863
|
+
display: flex;
|
|
1864
|
+
gap: 0.4rem;
|
|
1865
|
+
align-items: center;
|
|
1866
|
+
margin: 0 0 0.45rem;
|
|
1867
|
+
color: var(--s-muted);
|
|
1868
|
+
font-size: 0.76rem;
|
|
1869
|
+
line-height: 1.4;
|
|
1870
|
+
|
|
1871
|
+
svg {
|
|
1872
|
+
flex-shrink: 0;
|
|
1873
|
+
color: var(--s-muted);
|
|
1874
|
+
}
|
|
1875
|
+
}
|
|
1876
|
+
|
|
1828
1877
|
.composerBox {
|
|
1829
1878
|
display: flex;
|
|
1830
1879
|
flex-direction: column;
|
|
@@ -2776,3 +2825,434 @@ $sms-mono: 'SFMono-Regular', 'Menlo', 'Consolas', monospace;
|
|
|
2776
2825
|
border-radius: var(--s-br);
|
|
2777
2826
|
}
|
|
2778
2827
|
}
|
|
2828
|
+
|
|
2829
|
+
// ---------------------------------------------------------------------------
|
|
2830
|
+
// SmsCampaigns — the list, the three-step flow, one campaign, the opt-outs
|
|
2831
|
+
// ---------------------------------------------------------------------------
|
|
2832
|
+
//
|
|
2833
|
+
// The page mounts inside `.settings`, so it inherits the tokens and the
|
|
2834
|
+
// padding the messaging settings screen already has: a campaign list is a
|
|
2835
|
+
// table of records with an action bar over it, which is exactly what that
|
|
2836
|
+
// screen is. Everything below is the parts a settings screen has no vocabulary
|
|
2837
|
+
// for — a progress bar, a step rail, a drop target and a preview column.
|
|
2838
|
+
|
|
2839
|
+
// "Sending" is not a warning.
|
|
2840
|
+
//
|
|
2841
|
+
// A campaign doing precisely what it was told to do drawn in amber teaches
|
|
2842
|
+
// people to stop reading the chips, and then the paused one — the state where
|
|
2843
|
+
// nothing is going out and somebody has to act — reads the same as the healthy
|
|
2844
|
+
// one. So it takes the brand tint: visibly live, and visibly not a problem.
|
|
2845
|
+
.campaignPillLive {
|
|
2846
|
+
background: var(--s-tint);
|
|
2847
|
+
color: var(--s-primary);
|
|
2848
|
+
}
|
|
2849
|
+
|
|
2850
|
+
// The flag on a number the browser cannot read as one. A pill inside a cell,
|
|
2851
|
+
// so it needs the gap the cell does not give it.
|
|
2852
|
+
.campaignFlag {
|
|
2853
|
+
margin-left: 0.4rem;
|
|
2854
|
+
}
|
|
2855
|
+
|
|
2856
|
+
/* ------------------------------------------------------------- progress */
|
|
2857
|
+
|
|
2858
|
+
.campaignProgress {
|
|
2859
|
+
display: flex;
|
|
2860
|
+
gap: 0.5rem;
|
|
2861
|
+
align-items: center;
|
|
2862
|
+
min-width: 0;
|
|
2863
|
+
}
|
|
2864
|
+
|
|
2865
|
+
// The track is fixed and the fill is a percentage of it. Both are needed: a
|
|
2866
|
+
// bar that shrinks to its own fill has no "how much is left" in it, which is
|
|
2867
|
+
// the only question a progress bar answers.
|
|
2868
|
+
.campaignBar {
|
|
2869
|
+
flex: 1 1 auto;
|
|
2870
|
+
box-sizing: border-box;
|
|
2871
|
+
min-width: 90px;
|
|
2872
|
+
height: 6px;
|
|
2873
|
+
border-radius: 999px;
|
|
2874
|
+
background: color-mix(in srgb, var(--s-paragraph) 12%, var(--s-background));
|
|
2875
|
+
overflow: hidden;
|
|
2876
|
+
}
|
|
2877
|
+
|
|
2878
|
+
.campaignBarFill {
|
|
2879
|
+
display: block;
|
|
2880
|
+
height: 100%;
|
|
2881
|
+
border-radius: 999px;
|
|
2882
|
+
background: var(--s-primary);
|
|
2883
|
+
// Short, and only ever forward: the number behind it moves a few percent
|
|
2884
|
+
// every poll, and an unanimated jump reads as the page reloading.
|
|
2885
|
+
transition: width 0.3s ease;
|
|
2886
|
+
}
|
|
2887
|
+
|
|
2888
|
+
.campaignProgressCount {
|
|
2889
|
+
flex: 0 0 auto;
|
|
2890
|
+
color: var(--s-muted);
|
|
2891
|
+
font-size: 0.78rem;
|
|
2892
|
+
font-variant-numeric: tabular-nums;
|
|
2893
|
+
white-space: nowrap;
|
|
2894
|
+
}
|
|
2895
|
+
|
|
2896
|
+
// The bar needs room in a table cell that would otherwise size to its text.
|
|
2897
|
+
.campaignProgressCell {
|
|
2898
|
+
min-width: 170px;
|
|
2899
|
+
}
|
|
2900
|
+
|
|
2901
|
+
/* --------------------------------------------------------------- wizard */
|
|
2902
|
+
|
|
2903
|
+
.campaignWizard,
|
|
2904
|
+
.campaignDetail {
|
|
2905
|
+
display: flex;
|
|
2906
|
+
flex-direction: column;
|
|
2907
|
+
gap: 1rem;
|
|
2908
|
+
}
|
|
2909
|
+
|
|
2910
|
+
// The step rail. Numbered, because "Message" on its own does not say whether
|
|
2911
|
+
// it comes before or after the list — and the numbers are what the Back button
|
|
2912
|
+
// is moving between.
|
|
2913
|
+
.campaignSteps {
|
|
2914
|
+
display: flex;
|
|
2915
|
+
flex-wrap: wrap;
|
|
2916
|
+
gap: 0.4rem 1.25rem;
|
|
2917
|
+
margin: 0;
|
|
2918
|
+
padding: 0;
|
|
2919
|
+
list-style: none;
|
|
2920
|
+
}
|
|
2921
|
+
|
|
2922
|
+
.campaignStep {
|
|
2923
|
+
display: inline-flex;
|
|
2924
|
+
gap: 0.45rem;
|
|
2925
|
+
align-items: center;
|
|
2926
|
+
color: var(--s-muted);
|
|
2927
|
+
font-size: 0.82rem;
|
|
2928
|
+
font-weight: 600;
|
|
2929
|
+
}
|
|
2930
|
+
|
|
2931
|
+
.campaignStepOn {
|
|
2932
|
+
color: var(--s-paragraph);
|
|
2933
|
+
|
|
2934
|
+
.campaignStepNumber {
|
|
2935
|
+
border-color: var(--s-primary);
|
|
2936
|
+
background: var(--s-primary);
|
|
2937
|
+
color: #fff;
|
|
2938
|
+
}
|
|
2939
|
+
}
|
|
2940
|
+
|
|
2941
|
+
.campaignStepNumber {
|
|
2942
|
+
display: inline-flex;
|
|
2943
|
+
flex: 0 0 auto;
|
|
2944
|
+
align-items: center;
|
|
2945
|
+
justify-content: center;
|
|
2946
|
+
box-sizing: border-box;
|
|
2947
|
+
width: 22px;
|
|
2948
|
+
height: 22px;
|
|
2949
|
+
border: 1px solid var(--s-line);
|
|
2950
|
+
border-radius: 50%;
|
|
2951
|
+
background: var(--s-background);
|
|
2952
|
+
color: var(--s-muted);
|
|
2953
|
+
font-size: 0.74rem;
|
|
2954
|
+
font-weight: 700;
|
|
2955
|
+
font-variant-numeric: tabular-nums;
|
|
2956
|
+
}
|
|
2957
|
+
|
|
2958
|
+
// Two ways in, side by side and of equal weight — neither is the "proper" one.
|
|
2959
|
+
// They stack below the tablet breakpoint, where two columns of this leave the
|
|
2960
|
+
// paste box too narrow to see a line of `Name, number` in.
|
|
2961
|
+
.campaignSources {
|
|
2962
|
+
display: grid;
|
|
2963
|
+
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
|
2964
|
+
gap: 1rem;
|
|
2965
|
+
}
|
|
2966
|
+
|
|
2967
|
+
.campaignSource {
|
|
2968
|
+
display: flex;
|
|
2969
|
+
flex-direction: column;
|
|
2970
|
+
gap: 0.6rem;
|
|
2971
|
+
box-sizing: border-box;
|
|
2972
|
+
padding: 0.85rem 1rem;
|
|
2973
|
+
border: 1px solid var(--s-line);
|
|
2974
|
+
border-radius: var(--s-br);
|
|
2975
|
+
background: var(--s-background);
|
|
2976
|
+
}
|
|
2977
|
+
|
|
2978
|
+
.campaignSourceTitle {
|
|
2979
|
+
display: flex;
|
|
2980
|
+
gap: 0.4rem;
|
|
2981
|
+
align-items: center;
|
|
2982
|
+
margin: 0;
|
|
2983
|
+
color: var(--s-paragraph);
|
|
2984
|
+
font-size: 0.92rem;
|
|
2985
|
+
font-weight: 700;
|
|
2986
|
+
|
|
2987
|
+
svg {
|
|
2988
|
+
color: var(--s-primary);
|
|
2989
|
+
}
|
|
2990
|
+
}
|
|
2991
|
+
|
|
2992
|
+
.campaignDrop {
|
|
2993
|
+
@include sms-focus;
|
|
2994
|
+
|
|
2995
|
+
display: flex;
|
|
2996
|
+
flex-direction: column;
|
|
2997
|
+
gap: 0.5rem;
|
|
2998
|
+
align-items: center;
|
|
2999
|
+
justify-content: center;
|
|
3000
|
+
box-sizing: border-box;
|
|
3001
|
+
min-height: 120px;
|
|
3002
|
+
padding: 1rem;
|
|
3003
|
+
border: 1px dashed var(--s-line);
|
|
3004
|
+
border-radius: var(--s-br);
|
|
3005
|
+
background: var(--s-surface);
|
|
3006
|
+
color: var(--s-muted);
|
|
3007
|
+
text-align: center;
|
|
3008
|
+
cursor: pointer;
|
|
3009
|
+
|
|
3010
|
+
p {
|
|
3011
|
+
max-width: 34ch;
|
|
3012
|
+
text-align: center;
|
|
3013
|
+
}
|
|
3014
|
+
|
|
3015
|
+
&:hover {
|
|
3016
|
+
border-color: var(--s-primary);
|
|
3017
|
+
}
|
|
3018
|
+
}
|
|
3019
|
+
|
|
3020
|
+
.campaignDropOn {
|
|
3021
|
+
border-color: var(--s-primary);
|
|
3022
|
+
background: var(--s-tint);
|
|
3023
|
+
color: var(--s-primary);
|
|
3024
|
+
}
|
|
3025
|
+
|
|
3026
|
+
// Two selects and the button that takes the rows, on one line where there is
|
|
3027
|
+
// room. The button's own width is its label, which grows with the count.
|
|
3028
|
+
.campaignColumns {
|
|
3029
|
+
display: flex;
|
|
3030
|
+
flex-wrap: wrap;
|
|
3031
|
+
gap: 0.6rem;
|
|
3032
|
+
align-items: flex-end;
|
|
3033
|
+
|
|
3034
|
+
> .field {
|
|
3035
|
+
flex: 1 1 140px;
|
|
3036
|
+
min-width: 0;
|
|
3037
|
+
}
|
|
3038
|
+
|
|
3039
|
+
> .help {
|
|
3040
|
+
flex: 1 1 100%;
|
|
3041
|
+
}
|
|
3042
|
+
}
|
|
3043
|
+
|
|
3044
|
+
.campaignCount {
|
|
3045
|
+
color: var(--s-paragraph);
|
|
3046
|
+
font-size: 0.88rem;
|
|
3047
|
+
|
|
3048
|
+
strong {
|
|
3049
|
+
font-weight: 700;
|
|
3050
|
+
}
|
|
3051
|
+
}
|
|
3052
|
+
|
|
3053
|
+
/* -------------------------------------------------------------- compose */
|
|
3054
|
+
|
|
3055
|
+
// The message on the left, what it becomes on the right. Side by side rather
|
|
3056
|
+
// than stacked because the point of the preview is watching it change as the
|
|
3057
|
+
// message is typed, and a panel below the fold is a panel nobody sees move.
|
|
3058
|
+
.campaignCompose {
|
|
3059
|
+
display: grid;
|
|
3060
|
+
grid-template-columns: minmax(0, 1fr) minmax(0, 22rem);
|
|
3061
|
+
gap: 1rem;
|
|
3062
|
+
align-items: start;
|
|
3063
|
+
}
|
|
3064
|
+
|
|
3065
|
+
.campaignComposeMain {
|
|
3066
|
+
display: flex;
|
|
3067
|
+
flex-direction: column;
|
|
3068
|
+
gap: 0.85rem;
|
|
3069
|
+
min-width: 0;
|
|
3070
|
+
}
|
|
3071
|
+
|
|
3072
|
+
.campaignPreview {
|
|
3073
|
+
display: flex;
|
|
3074
|
+
flex-direction: column;
|
|
3075
|
+
gap: 0.6rem;
|
|
3076
|
+
box-sizing: border-box;
|
|
3077
|
+
padding: 0.85rem 1rem;
|
|
3078
|
+
border: 1px solid var(--s-line);
|
|
3079
|
+
border-radius: var(--s-br);
|
|
3080
|
+
background: var(--s-surface);
|
|
3081
|
+
}
|
|
3082
|
+
|
|
3083
|
+
.campaignPreviewList {
|
|
3084
|
+
display: flex;
|
|
3085
|
+
flex-direction: column;
|
|
3086
|
+
gap: 0.6rem;
|
|
3087
|
+
margin: 0;
|
|
3088
|
+
padding: 0;
|
|
3089
|
+
list-style: none;
|
|
3090
|
+
}
|
|
3091
|
+
|
|
3092
|
+
.campaignPreviewItem {
|
|
3093
|
+
display: flex;
|
|
3094
|
+
flex-direction: column;
|
|
3095
|
+
gap: 0.35rem;
|
|
3096
|
+
box-sizing: border-box;
|
|
3097
|
+
padding: 0.6rem 0.7rem;
|
|
3098
|
+
border: 1px solid var(--s-line);
|
|
3099
|
+
border-radius: var(--s-br);
|
|
3100
|
+
background: var(--s-background);
|
|
3101
|
+
}
|
|
3102
|
+
|
|
3103
|
+
.campaignPreviewWho {
|
|
3104
|
+
display: flex;
|
|
3105
|
+
gap: 0.5rem;
|
|
3106
|
+
align-items: center;
|
|
3107
|
+
min-width: 0;
|
|
3108
|
+
|
|
3109
|
+
strong {
|
|
3110
|
+
display: block;
|
|
3111
|
+
overflow: hidden;
|
|
3112
|
+
color: var(--s-paragraph);
|
|
3113
|
+
font-size: 0.84rem;
|
|
3114
|
+
font-weight: 700;
|
|
3115
|
+
text-overflow: ellipsis;
|
|
3116
|
+
white-space: nowrap;
|
|
3117
|
+
}
|
|
3118
|
+
|
|
3119
|
+
small {
|
|
3120
|
+
color: var(--s-muted);
|
|
3121
|
+
font-size: 0.74rem;
|
|
3122
|
+
font-variant-numeric: tabular-nums;
|
|
3123
|
+
}
|
|
3124
|
+
}
|
|
3125
|
+
|
|
3126
|
+
// The rendered message. `pre-wrap` because a campaign body carries the line
|
|
3127
|
+
// breaks somebody typed, and a preview that folded them away would not be one.
|
|
3128
|
+
.campaignPreviewBody {
|
|
3129
|
+
margin: 0;
|
|
3130
|
+
color: var(--s-paragraph);
|
|
3131
|
+
font-size: 0.84rem;
|
|
3132
|
+
line-height: 1.5;
|
|
3133
|
+
white-space: pre-wrap;
|
|
3134
|
+
overflow-wrap: anywhere;
|
|
3135
|
+
}
|
|
3136
|
+
|
|
3137
|
+
.campaignFooter {
|
|
3138
|
+
color: var(--s-paragraph);
|
|
3139
|
+
font-style: italic;
|
|
3140
|
+
}
|
|
3141
|
+
|
|
3142
|
+
/* --------------------------------------------------------------- review */
|
|
3143
|
+
|
|
3144
|
+
.campaignReview,
|
|
3145
|
+
.campaignReport {
|
|
3146
|
+
display: flex;
|
|
3147
|
+
flex-direction: column;
|
|
3148
|
+
gap: 1rem;
|
|
3149
|
+
}
|
|
3150
|
+
|
|
3151
|
+
// The same card the settings status block is, and deliberately so: a row of
|
|
3152
|
+
// labelled figures is a row of labelled figures, and a second design for one
|
|
3153
|
+
// would be a second thing to keep in step.
|
|
3154
|
+
.campaignFacts {
|
|
3155
|
+
display: flex;
|
|
3156
|
+
flex-wrap: wrap;
|
|
3157
|
+
gap: 0.75rem 1.5rem;
|
|
3158
|
+
box-sizing: border-box;
|
|
3159
|
+
padding: 0.85rem 1rem;
|
|
3160
|
+
border: 1px solid var(--s-line);
|
|
3161
|
+
border-radius: var(--s-br);
|
|
3162
|
+
background: var(--s-surface);
|
|
3163
|
+
}
|
|
3164
|
+
|
|
3165
|
+
// The step's own buttons, always at the foot of it, always right-aligned —
|
|
3166
|
+
// Back on the left of the pair because that is the order they are read in.
|
|
3167
|
+
.campaignFoot {
|
|
3168
|
+
display: flex;
|
|
3169
|
+
gap: 0.6rem;
|
|
3170
|
+
align-items: center;
|
|
3171
|
+
justify-content: flex-end;
|
|
3172
|
+
padding-top: 0.75rem;
|
|
3173
|
+
border-top: 1px solid var(--s-line);
|
|
3174
|
+
}
|
|
3175
|
+
|
|
3176
|
+
/* --------------------------------------------------------------- detail */
|
|
3177
|
+
|
|
3178
|
+
.campaignHeading {
|
|
3179
|
+
display: flex;
|
|
3180
|
+
flex-direction: column;
|
|
3181
|
+
gap: 0.2rem;
|
|
3182
|
+
min-width: 0;
|
|
3183
|
+
}
|
|
3184
|
+
|
|
3185
|
+
// The message, folded. `details` rather than a button and a piece of state:
|
|
3186
|
+
// the browser owns the open/closed, it is keyboard operable for nothing, and
|
|
3187
|
+
// the summary is a real heading when it is printed.
|
|
3188
|
+
.campaignFold {
|
|
3189
|
+
box-sizing: border-box;
|
|
3190
|
+
padding: 0.6rem 0.85rem;
|
|
3191
|
+
border: 1px solid var(--s-line);
|
|
3192
|
+
border-radius: var(--s-br);
|
|
3193
|
+
background: var(--s-surface);
|
|
3194
|
+
font-size: 0.84rem;
|
|
3195
|
+
|
|
3196
|
+
> summary {
|
|
3197
|
+
color: var(--s-paragraph);
|
|
3198
|
+
font-weight: 600;
|
|
3199
|
+
cursor: pointer;
|
|
3200
|
+
}
|
|
3201
|
+
}
|
|
3202
|
+
|
|
3203
|
+
.campaignFoldBody {
|
|
3204
|
+
margin: 0.6rem 0 0;
|
|
3205
|
+
color: var(--s-paragraph);
|
|
3206
|
+
line-height: 1.5;
|
|
3207
|
+
white-space: pre-wrap;
|
|
3208
|
+
overflow-wrap: anywhere;
|
|
3209
|
+
}
|
|
3210
|
+
|
|
3211
|
+
.campaignPager {
|
|
3212
|
+
display: flex;
|
|
3213
|
+
gap: 0.75rem;
|
|
3214
|
+
align-items: center;
|
|
3215
|
+
justify-content: flex-end;
|
|
3216
|
+
}
|
|
3217
|
+
|
|
3218
|
+
/* ------------------------------------------------------------- opt-outs */
|
|
3219
|
+
|
|
3220
|
+
// Number, note, and the button, on one row. The note is given the slack
|
|
3221
|
+
// because "asked to stop, 4 September" is the part somebody actually types.
|
|
3222
|
+
.optOutAdd {
|
|
3223
|
+
display: flex;
|
|
3224
|
+
flex-wrap: wrap;
|
|
3225
|
+
gap: 0.6rem;
|
|
3226
|
+
align-items: flex-end;
|
|
3227
|
+
box-sizing: border-box;
|
|
3228
|
+
padding: 0.75rem 0.85rem;
|
|
3229
|
+
border: 1px solid var(--s-line);
|
|
3230
|
+
border-radius: var(--s-br);
|
|
3231
|
+
background: var(--s-surface);
|
|
3232
|
+
|
|
3233
|
+
> .field:first-child {
|
|
3234
|
+
flex: 0 1 12rem;
|
|
3235
|
+
}
|
|
3236
|
+
|
|
3237
|
+
> .field {
|
|
3238
|
+
flex: 1 1 14rem;
|
|
3239
|
+
min-width: 0;
|
|
3240
|
+
}
|
|
3241
|
+
}
|
|
3242
|
+
|
|
3243
|
+
@media (max-width: 900px) {
|
|
3244
|
+
// The preview stops being a column and becomes a panel under the message.
|
|
3245
|
+
// Below this width the two side by side leave the textarea about forty
|
|
3246
|
+
// characters wide, which is narrower than the message being written in it.
|
|
3247
|
+
.campaignCompose {
|
|
3248
|
+
grid-template-columns: minmax(0, 1fr);
|
|
3249
|
+
}
|
|
3250
|
+
|
|
3251
|
+
.campaignFoot {
|
|
3252
|
+
flex-wrap: wrap;
|
|
3253
|
+
}
|
|
3254
|
+
|
|
3255
|
+
.campaignPager {
|
|
3256
|
+
justify-content: space-between;
|
|
3257
|
+
}
|
|
3258
|
+
}
|