diff options
| author | guicamest <guicamest@gmail.com> | 2023-08-15 17:32:18 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-08-15 08:32:18 -0700 |
| commit | f1d79a17f615a9cd34b15dd7b96764e4bb99486a (patch) | |
| tree | adbaf3522176ad3240a47183388d5d8127abd3a8 | |
| parent | 7f2413d3d08b5da7a30c17ecfc1586f5bc7b1219 (diff) | |
Allow setting additional cards to print (#275)
* Add setting for additional cards to print
* Render additional cards to print
* Do not render additional cards if amount is less than 1
* Render additional cards when printing
* Improve layout for portrait printing
* Render eap and identity textfields conditionally so that they dont use space
* Use all width in print-area
* Change additionalCards setting to Input and react to changes
* Allow hiding tip (legend) on card
* Print only full cards
* Move print-area - not-displayed - to the bottom of the page
* Set default ssid back to empty, additional cards to 0
* Lower marginBottom of password field to 5 instead of 24 (default)
* Use conditional rendering instead of class to hide password
* Set marginBottom to QR code only on portrait mode
* Set row-gap to 0 to allow up to 6 cards to fit in portrait mode in A4
* Move hideTip setting right after hiddenSSID
| -rw-r--r-- | src/App.js | 48 | ||||
| -rw-r--r-- | src/components/Settings.js | 21 | ||||
| -rw-r--r-- | src/components/WifiCard.js | 181 | ||||
| -rw-r--r-- | src/components/style.css | 24 | ||||
| -rw-r--r-- | src/translations.js | 2 |
5 files changed, 168 insertions, 108 deletions
| @@ -28,6 +28,10 @@ function App() { | |||
| 28 | hiddenSSID: false, | 28 | hiddenSSID: false, |
| 29 | // Settings: Portrait orientation | 29 | // Settings: Portrait orientation |
| 30 | portrait: false, | 30 | portrait: false, |
| 31 | // Settings: Additional cards | ||
| 32 | additionalCards: 0, | ||
| 33 | // Settings: Show tip (legend) on card | ||
| 34 | hideTip: false, | ||
| 31 | }); | 35 | }); |
| 32 | const [errors, setErrors] = useState({ | 36 | const [errors, setErrors] = useState({ |
| 33 | ssidError: '', | 37 | ssidError: '', |
| @@ -120,6 +124,13 @@ function App() { | |||
| 120 | const onHiddenSSIDChange = (hiddenSSID) => { | 124 | const onHiddenSSIDChange = (hiddenSSID) => { |
| 121 | setSettings({ ...settings, hiddenSSID }); | 125 | setSettings({ ...settings, hiddenSSID }); |
| 122 | }; | 126 | }; |
| 127 | const onAdditionalCardsChange = (additionalCardsStr) => { | ||
| 128 | const amount = parseInt(additionalCardsStr); | ||
| 129 | amount >= 0 && setSettings({ ...settings, additionalCards: amount }); | ||
| 130 | }; | ||
| 131 | const onHideTipChange = (hideTip) => { | ||
| 132 | setSettings({ ...settings, hideTip }); | ||
| 133 | }; | ||
| 123 | const onFirstLoad = () => { | 134 | const onFirstLoad = () => { |
| 124 | html.style.direction = htmlDirection(); | 135 | html.style.direction = htmlDirection(); |
| 125 | firstLoad.current = false; | 136 | firstLoad.current = false; |
| @@ -153,15 +164,17 @@ function App() { | |||
| 153 | </Paragraph> | 164 | </Paragraph> |
| 154 | </Pane> | 165 | </Pane> |
| 155 | 166 | ||
| 156 | <WifiCard | 167 | <Pane> |
| 157 | settings={settings} | 168 | <WifiCard |
| 158 | ssidError={errors.ssidError} | 169 | settings={settings} |
| 159 | passwordError={errors.passwordError} | 170 | ssidError={errors.ssidError} |
| 160 | eapIdentityError={errors.eapIdentityError} | 171 | passwordError={errors.passwordError} |
| 161 | onSSIDChange={onSSIDChange} | 172 | eapIdentityError={errors.eapIdentityError} |
| 162 | onEapIdentityChange={onEapIdentityChange} | 173 | onSSIDChange={onSSIDChange} |
| 163 | onPasswordChange={onPasswordChange} | 174 | onEapIdentityChange={onEapIdentityChange} |
| 164 | /> | 175 | onPasswordChange={onPasswordChange} |
| 176 | /> | ||
| 177 | </Pane> | ||
| 165 | 178 | ||
| 166 | <Settings | 179 | <Settings |
| 167 | settings={settings} | 180 | settings={settings} |
| @@ -173,6 +186,8 @@ function App() { | |||
| 173 | onOrientationChange={onOrientationChange} | 186 | onOrientationChange={onOrientationChange} |
| 174 | onHidePasswordChange={onHidePasswordChange} | 187 | onHidePasswordChange={onHidePasswordChange} |
| 175 | onHiddenSSIDChange={onHiddenSSIDChange} | 188 | onHiddenSSIDChange={onHiddenSSIDChange} |
| 189 | onAdditionalCardsChange={onAdditionalCardsChange} | ||
| 190 | onHideTipChange={onHideTipChange} | ||
| 176 | /> | 191 | /> |
| 177 | 192 | ||
| 178 | <Button | 193 | <Button |
| @@ -184,6 +199,21 @@ function App() { | |||
| 184 | > | 199 | > |
| 185 | {t('button.print')} | 200 | {t('button.print')} |
| 186 | </Button> | 201 | </Button> |
| 202 | <Pane id="print-area"> | ||
| 203 | {settings.additionalCards >= 0 && | ||
| 204 | [...Array(settings.additionalCards + 1)].map((el, idx) => ( | ||
| 205 | <WifiCard | ||
| 206 | key={`card-nr-${idx}`} | ||
| 207 | settings={settings} | ||
| 208 | ssidError={errors.ssidError} | ||
| 209 | passwordError={errors.passwordError} | ||
| 210 | eapIdentityError={errors.eapIdentityError} | ||
| 211 | onSSIDChange={onSSIDChange} | ||
| 212 | onEapIdentityChange={onEapIdentityChange} | ||
| 213 | onPasswordChange={onPasswordChange} | ||
| 214 | /> | ||
| 215 | ))} | ||
| 216 | </Pane> | ||
| 187 | </Pane> | 217 | </Pane> |
| 188 | ); | 218 | ); |
| 189 | } | 219 | } |
diff --git a/src/components/Settings.js b/src/components/Settings.js index a0dbab5..837b4f0 100644 --- a/src/components/Settings.js +++ b/src/components/Settings.js | |||
| @@ -1,4 +1,10 @@ | |||
| 1 | import { Checkbox, Pane, RadioGroup, SelectField } from 'evergreen-ui'; | 1 | import { |
| 2 | Checkbox, | ||
| 3 | Pane, | ||
| 4 | RadioGroup, | ||
| 5 | SelectField, | ||
| 6 | TextInputField, | ||
| 7 | } from 'evergreen-ui'; | ||
| 2 | import { useEffect } from 'react'; | 8 | import { useEffect } from 'react'; |
| 3 | import { useTranslation } from 'react-i18next'; | 9 | import { useTranslation } from 'react-i18next'; |
| 4 | import i18n from '../i18n'; | 10 | import i18n from '../i18n'; |
| @@ -62,6 +68,19 @@ export const Settings = (props) => { | |||
| 62 | checked={props.settings.hiddenSSID} | 68 | checked={props.settings.hiddenSSID} |
| 63 | onChange={() => props.onHiddenSSIDChange(!props.settings.hiddenSSID)} | 69 | onChange={() => props.onHiddenSSIDChange(!props.settings.hiddenSSID)} |
| 64 | /> | 70 | /> |
| 71 | |||
| 72 | <Checkbox | ||
| 73 | label={t('cards.tip.hide')} | ||
| 74 | checked={props.settings.hideTip} | ||
| 75 | onChange={() => props.onHideTipChange(!props.settings.hideTip)} | ||
| 76 | /> | ||
| 77 | <TextInputField | ||
| 78 | type="number" | ||
| 79 | width={300} | ||
| 80 | label={t('cards.additional')} | ||
| 81 | value={props.settings.additionalCards} | ||
| 82 | onChange={(e) => props.onAdditionalCardsChange(e.target.value)} | ||
| 83 | /> | ||
| 65 | <RadioGroup | 84 | <RadioGroup |
| 66 | label={t('wifi.password.encryption')} | 85 | label={t('wifi.password.encryption')} |
| 67 | size={16} | 86 | size={16} |
diff --git a/src/components/WifiCard.js b/src/components/WifiCard.js index 6e91be0..70e1189 100644 --- a/src/components/WifiCard.js +++ b/src/components/WifiCard.js | |||
| @@ -72,84 +72,83 @@ export const WifiCard = (props) => { | |||
| 72 | }; | 72 | }; |
| 73 | 73 | ||
| 74 | return ( | 74 | return ( |
| 75 | <Pane> | 75 | <Card |
| 76 | <Card | 76 | className="card-print" |
| 77 | id="print-area" | 77 | elevation={3} |
| 78 | elevation={3} | 78 | style={{ maxWidth: props.settings.portrait ? portraitWidth() : '100%' }} |
| 79 | style={{ maxWidth: props.settings.portrait ? portraitWidth() : '100%' }} | 79 | > |
| 80 | <Pane display="flex" paddingBottom={12}> | ||
| 81 | <img alt="icon" src={logo} width="24" height="24" /> | ||
| 82 | <Heading | ||
| 83 | size={700} | ||
| 84 | paddingRight={10} | ||
| 85 | paddingLeft={10} | ||
| 86 | textAlign={props.settings.portrait ? 'center' : 'unset'} | ||
| 87 | > | ||
| 88 | {t('wifi.login')} | ||
| 89 | </Heading> | ||
| 90 | </Pane> | ||
| 91 | |||
| 92 | <Pane | ||
| 93 | className="details" | ||
| 94 | style={{ flexDirection: props.settings.portrait ? 'column' : 'row' }} | ||
| 80 | > | 95 | > |
| 81 | <Pane display="flex" paddingBottom={12}> | 96 | <QRCode |
| 82 | <img alt="icon" src={logo} width="24" height="24" /> | 97 | className="qrcode" |
| 83 | <Heading | 98 | style={{ marginBottom: props.settings.portrait ? '1em' : '0' }} |
| 84 | size={700} | 99 | value={qrvalue} |
| 85 | paddingRight={10} | 100 | size={150} |
| 86 | paddingLeft={10} | 101 | /> |
| 87 | textAlign={props.settings.portrait ? 'center' : 'unset'} | ||
| 88 | > | ||
| 89 | {t('wifi.login')} | ||
| 90 | </Heading> | ||
| 91 | </Pane> | ||
| 92 | 102 | ||
| 93 | <Pane | 103 | <Pane width={'100%'}> |
| 94 | className="details" | 104 | <TextareaField |
| 95 | style={{ flexDirection: props.settings.portrait ? 'column' : 'row' }} | 105 | id="ssid" |
| 96 | > | 106 | type="text" |
| 97 | <QRCode | 107 | marginBottom={5} |
| 98 | className="qrcode" | 108 | autoComplete="off" |
| 99 | style={{ padding: '1em' }} | 109 | autoCorrect="off" |
| 100 | value={qrvalue} | 110 | autoCapitalize="none" |
| 101 | size={150} | 111 | spellCheck={false} |
| 112 | maxLength="32" | ||
| 113 | label={t('wifi.name')} | ||
| 114 | placeholder={t('wifi.name.placeholder')} | ||
| 115 | value={props.settings.ssid} | ||
| 116 | onChange={(e) => props.onSSIDChange(e.target.value)} | ||
| 117 | isInvalid={!!props.ssidError} | ||
| 118 | validationMessage={!!props.ssidError && props.ssidError} | ||
| 102 | /> | 119 | /> |
| 120 | {props.settings.encryptionMode === 'WPA2-EAP' && ( | ||
| 121 | <> | ||
| 122 | <TextareaField | ||
| 123 | id="eapmethod" | ||
| 124 | type="text" | ||
| 125 | marginBottom={5} | ||
| 126 | readOnly={true} | ||
| 127 | spellCheck={false} | ||
| 128 | label={eapMethodFieldLabel()} | ||
| 129 | value={props.settings.eapMethod} | ||
| 130 | /> | ||
| 103 | 131 | ||
| 104 | <Pane width={'100%'}> | 132 | <TextareaField |
| 105 | <TextareaField | 133 | id="identity" |
| 106 | id="ssid" | 134 | type="text" |
| 107 | type="text" | 135 | marginBottom={5} |
| 108 | marginBottom={5} | 136 | autoComplete="off" |
| 109 | autoComplete="off" | 137 | autoCorrect="off" |
| 110 | autoCorrect="off" | 138 | autoCapitalize="none" |
| 111 | autoCapitalize="none" | 139 | spellCheck={false} |
| 112 | spellCheck={false} | 140 | label={eapIdentityFieldLabel()} |
| 113 | maxLength="32" | 141 | placeholder={t('wifi.identity.placeholder')} |
| 114 | label={t('wifi.name')} | 142 | value={props.settings.eapIdentity} |
| 115 | placeholder={t('wifi.name.placeholder')} | 143 | onChange={(e) => props.onEapIdentityChange(e.target.value)} |
| 116 | value={props.settings.ssid} | 144 | isInvalid={!!props.eapIdentityError} |
| 117 | onChange={(e) => props.onSSIDChange(e.target.value)} | 145 | validationMessage={ |
| 118 | isInvalid={!!props.ssidError} | 146 | !!props.eapIdentityError && props.eapIdentityError |
| 119 | validationMessage={!!props.ssidError && props.ssidError} | 147 | } |
| 120 | /> | 148 | /> |
| 121 | <TextareaField | 149 | </> |
| 122 | id="eapmethod" | 150 | )} |
| 123 | type="text" | 151 | {!(props.settings.hidePassword || !props.settings.encryptionMode) && ( |
| 124 | marginBottom={5} | ||
| 125 | readOnly={true} | ||
| 126 | spellCheck={false} | ||
| 127 | className={` | ||
| 128 | ${props.settings.encryptionMode !== 'WPA2-EAP' && 'hidden'} | ||
| 129 | `} | ||
| 130 | label={eapMethodFieldLabel()} | ||
| 131 | value={props.settings.eapMethod} | ||
| 132 | /> | ||
| 133 | <TextareaField | ||
| 134 | id="identity" | ||
| 135 | type="text" | ||
| 136 | marginBottom={5} | ||
| 137 | autoComplete="off" | ||
| 138 | autoCorrect="off" | ||
| 139 | autoCapitalize="none" | ||
| 140 | spellCheck={false} | ||
| 141 | className={` | ||
| 142 | ${props.settings.encryptionMode !== 'WPA2-EAP' && 'hidden'} | ||
| 143 | `} | ||
| 144 | label={eapIdentityFieldLabel()} | ||
| 145 | placeholder={t('wifi.identity.placeholder')} | ||
| 146 | value={props.settings.eapIdentity} | ||
| 147 | onChange={(e) => props.onEapIdentityChange(e.target.value)} | ||
| 148 | isInvalid={!!props.eapIdentityError} | ||
| 149 | validationMessage={ | ||
| 150 | !!props.eapIdentityError && props.eapIdentityError | ||
| 151 | } | ||
| 152 | /> | ||
| 153 | <TextareaField | 152 | <TextareaField |
| 154 | id="password" | 153 | id="password" |
| 155 | type="text" | 154 | type="text" |
| @@ -158,18 +157,12 @@ export const WifiCard = (props) => { | |||
| 158 | autoCorrect="off" | 157 | autoCorrect="off" |
| 159 | autoCapitalize="none" | 158 | autoCapitalize="none" |
| 160 | spellCheck={false} | 159 | spellCheck={false} |
| 161 | className={` | ||
| 162 | ${ | ||
| 163 | (props.settings.hidePassword || | ||
| 164 | !props.settings.encryptionMode) && | ||
| 165 | 'hidden' | ||
| 166 | } | ||
| 167 | `} | ||
| 168 | height={ | 160 | height={ |
| 169 | props.settings.portrait && props.settings.password.length > 40 | 161 | props.settings.portrait && props.settings.password.length > 40 |
| 170 | ? '5em' | 162 | ? '5em' |
| 171 | : 'auto' | 163 | : 'auto' |
| 172 | } | 164 | } |
| 165 | marginBottom={5} | ||
| 173 | label={passwordFieldLabel()} | 166 | label={passwordFieldLabel()} |
| 174 | placeholder={t('wifi.password.placeholder')} | 167 | placeholder={t('wifi.password.placeholder')} |
| 175 | value={props.settings.password} | 168 | value={props.settings.password} |
| @@ -177,17 +170,21 @@ export const WifiCard = (props) => { | |||
| 177 | isInvalid={!!props.passwordError} | 170 | isInvalid={!!props.passwordError} |
| 178 | validationMessage={!!props.passwordError && props.passwordError} | 171 | validationMessage={!!props.passwordError && props.passwordError} |
| 179 | /> | 172 | /> |
| 180 | </Pane> | 173 | )} |
| 181 | </Pane> | 174 | </Pane> |
| 182 | <hr /> | 175 | </Pane> |
| 183 | <Paragraph> | 176 | {!props.settings.hideTip && ( |
| 184 | <CameraIcon /> | 177 | <> |
| 185 | <MobilePhoneIcon /> | 178 | <hr /> |
| 186 | <Text size={300} paddingRight={8} paddingLeft={8}> | 179 | <Paragraph> |
| 187 | {t('wifi.tip')} | 180 | <CameraIcon /> |
| 188 | </Text> | 181 | <MobilePhoneIcon /> |
| 189 | </Paragraph> | 182 | <Text size={300} paddingRight={8} paddingLeft={8}> |
| 190 | </Card> | 183 | {t('wifi.tip')} |
| 191 | </Pane> | 184 | </Text> |
| 185 | </Paragraph> | ||
| 186 | </> | ||
| 187 | )} | ||
| 188 | </Card> | ||
| 192 | ); | 189 | ); |
| 193 | }; | 190 | }; |
diff --git a/src/components/style.css b/src/components/style.css index 5275770..10eac9b 100644 --- a/src/components/style.css +++ b/src/components/style.css | |||
| @@ -3,21 +3,25 @@ | |||
| 3 | src: url(./SourceSerif4-Semibold.otf); | 3 | src: url(./SourceSerif4-Semibold.otf); |
| 4 | } | 4 | } |
| 5 | 5 | ||
| 6 | #print-area { | 6 | .card-print { |
| 7 | border-color: #aaa; | 7 | border-color: #aaa; |
| 8 | margin-bottom: 1em; | 8 | margin-bottom: 1em; |
| 9 | margin-top: 2em; | 9 | margin-top: 2em; |
| 10 | padding: 1em; | 10 | padding: 1em; |
| 11 | } | 11 | } |
| 12 | 12 | ||
| 13 | #print-area { | ||
| 14 | display: none; | ||
| 15 | } | ||
| 16 | |||
| 13 | .details { | 17 | .details { |
| 14 | display: flex; | 18 | display: flex; |
| 15 | align-items: center; | 19 | align-items: center; |
| 16 | } | 20 | } |
| 17 | 21 | ||
| 18 | .qrcode { | 22 | .qrcode { |
| 19 | margin-bottom: 1em; | ||
| 20 | max-width: 175px; | 23 | max-width: 175px; |
| 24 | padding: 1em; | ||
| 21 | } | 25 | } |
| 22 | 26 | ||
| 23 | .hidden { | 27 | .hidden { |
| @@ -65,12 +69,20 @@ button { | |||
| 65 | visibility: visible; | 69 | visibility: visible; |
| 66 | } | 70 | } |
| 67 | #print-area { | 71 | #print-area { |
| 68 | border-style: dashed; | ||
| 69 | box-shadow: none; | ||
| 70 | } | ||
| 71 | #print-area { | ||
| 72 | position: absolute; | 72 | position: absolute; |
| 73 | left: 0; | 73 | left: 0; |
| 74 | top: 0; | 74 | top: 0; |
| 75 | display: flex; | ||
| 76 | flex-wrap: wrap; | ||
| 77 | justify-content: space-evenly; | ||
| 78 | row-gap: 0em; | ||
| 79 | } | ||
| 80 | .card-print { | ||
| 81 | border-style: dashed; | ||
| 82 | box-shadow: none; | ||
| 83 | margin-bottom: 0; | ||
| 84 | margin-top: 0; | ||
| 85 | break-inside: avoid; | ||
| 86 | page-break-inside: avoid; | ||
| 75 | } | 87 | } |
| 76 | } | 88 | } |
diff --git a/src/translations.js b/src/translations.js index d2c0a89..d3dd9af 100644 --- a/src/translations.js +++ b/src/translations.js | |||
| @@ -14,6 +14,8 @@ export const Translations = [ | |||
| 14 | 'wifi.login': 'WiFi Login', | 14 | 'wifi.login': 'WiFi Login', |
| 15 | 'wifi.name': 'Network name', | 15 | 'wifi.name': 'Network name', |
| 16 | 'wifi.name.hiddenSSID': 'Hidden SSID', | 16 | 'wifi.name.hiddenSSID': 'Hidden SSID', |
| 17 | 'cards.additional': 'Additional cards to print', | ||
| 18 | 'cards.tip.hide': 'Hide tip (legend)', | ||
| 17 | 'wifi.name.placeholder': 'WiFi Network name', | 19 | 'wifi.name.placeholder': 'WiFi Network name', |
| 18 | 'wifi.password': 'Password', | 20 | 'wifi.password': 'Password', |
| 19 | 'wifi.password.placeholder': 'Password', | 21 | 'wifi.password.placeholder': 'Password', |
