feat: stepper de durée ±5 min sur l'ajout (sync design)Unverified
Le champ minutes devient un stepper en pilule (− / champ step=5 / +), libellés minutes + durée en clair à droite ; liens zéro-JS bornés [5, 1440]. Spinners natifs retirés aussi sous Firefox.
2dd9b43 parent: bb1fbb6 modified
assets/style.css +23 -7 | @@ -16,6 +16,7 @@ | ||
| 16 | 16 | --color-neutral-900: #2e2b25; |
| 17 | 17 | |
| 18 | 18 | --color-accent-100: #fff2eb; |
| 19 | + --color-accent-200: #ffe1d0; | |
| 19 | 20 | --color-accent-300: #ffc6a5; |
| 20 | 21 | --color-accent-600: #b2622d; |
| 21 | 22 | --color-accent-700: #8c491a; |
| @@ -59,7 +60,8 @@ a { color: var(--color-accent); text-underline-offset: 3px; } | ||
| 59 | 60 | ::selection { background: color-mix(in srgb, var(--color-accent) 30%, transparent); } |
| 60 | 61 | button { font-family: inherit; } |
| 61 | 62 | input[type=number]::-webkit-outer-spin-button, |
| 62 | -input[type=number]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; } | |
| 63 | +input[type=number]::-webkit-inner-spin-button { -webkit-appearance: none; appearance: none; margin: 0; } | |
| 64 | +input[type=number] { -moz-appearance: textfield; appearance: textfield; } | |
| 63 | 65 | |
| 64 | 66 | .text-muted { color: color-mix(in srgb, var(--color-text) 55%, transparent); } |
| 65 | 67 | |
| @@ -179,13 +181,27 @@ input[type=number]::-webkit-inner-spin-button { -webkit-appearance: none; margin | ||
| 179 | 181 | .picked { display: flex; align-items: center; gap: var(--space-2); margin-bottom: var(--space-6); } |
| 180 | 182 | .picked .pname { font-size: 20px; font-family: var(--font-heading); } |
| 181 | 183 | .mini-label { font-size: 12px; opacity: .7; margin: 0 0 var(--space-2); } |
| 182 | -.duration-row { display: flex; align-items: baseline; gap: 6px; margin-bottom: var(--space-3); } | |
| 183 | -.duration-row .minutes-input { | |
| 184 | - width: 130px; font-size: 34px; font-family: var(--font-heading); | |
| 185 | - min-height: 64px; text-align: center; padding: 0 10px; | |
| 184 | +.duration-row { display: flex; align-items: center; gap: var(--space-3); margin-bottom: var(--space-3); } | |
| 185 | +.stepper { | |
| 186 | + display: flex; align-items: center; gap: var(--space-1); | |
| 187 | + background: var(--color-surface); border: 1px solid var(--color-divider); | |
| 188 | + border-radius: 999px; padding: 5px 6px; | |
| 186 | 189 | } |
| 187 | -.duration-row .unit { font-size: 16px; opacity: .6; } | |
| 188 | -.duration-row .pretty { margin-left: auto; font-size: 14px; opacity: .55; } | |
| 190 | +.stepper-btn { | |
| 191 | + width: 40px; height: 40px; flex: none; | |
| 192 | + display: flex; align-items: center; justify-content: center; | |
| 193 | + border: none; border-radius: 999px; background: transparent; | |
| 194 | + color: var(--color-text); cursor: pointer; text-decoration: none; | |
| 195 | +} | |
| 196 | +.stepper-btn:hover { background: var(--color-accent-200); } | |
| 197 | +.stepper-btn:active { background: var(--color-accent-300); } | |
| 198 | +.stepper .minutes-input { | |
| 199 | + width: 92px; font-size: 32px; font-family: var(--font-heading); | |
| 200 | + min-height: 44px; text-align: center; padding: 0; | |
| 201 | + background: transparent; border-color: transparent; | |
| 202 | +} | |
| 203 | +.duration-row .unit { font-size: 15px; opacity: .7; } | |
| 204 | +.duration-row .pretty { font-size: 13px; opacity: .5; } | |
| 189 | 205 | .presets { display: flex; gap: var(--space-2); flex-wrap: wrap; margin-bottom: var(--space-6); } |
| 190 | 206 | .preset { |
| 191 | 207 | padding: 9px 16px; border-radius: 999px; font-size: 14px; |
| @@ -16,6 +16,7 @@ | |||
| 16 | --color-neutral-900: #2e2b25; | 16 | --color-neutral-900: #2e2b25; |
| 17 | 17 | ||
| 18 | --color-accent-100: #fff2eb; | 18 | --color-accent-100: #fff2eb; |
| 19 | + --color-accent-200: #ffe1d0; | ||
| 19 | --color-accent-300: #ffc6a5; | 20 | --color-accent-300: #ffc6a5; |
| 20 | --color-accent-600: #b2622d; | 21 | --color-accent-600: #b2622d; |
| 21 | --color-accent-700: #8c491a; | 22 | --color-accent-700: #8c491a; |
| @@ -59,7 +60,8 @@ a { color: var(--color-accent); text-underline-offset: 3px; } | |||
| 59 | ::selection { background: color-mix(in srgb, var(--color-accent) 30%, transparent); } | 60 | ::selection { background: color-mix(in srgb, var(--color-accent) 30%, transparent); } |
| 60 | button { font-family: inherit; } | 61 | button { font-family: inherit; } |
| 61 | input[type=number]::-webkit-outer-spin-button, | 62 | input[type=number]::-webkit-outer-spin-button, |
| 62 | -input[type=number]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; } | 63 | +input[type=number]::-webkit-inner-spin-button { -webkit-appearance: none; appearance: none; margin: 0; } |
| 64 | +input[type=number] { -moz-appearance: textfield; appearance: textfield; } | ||
| 63 | 65 | ||
| 64 | .text-muted { color: color-mix(in srgb, var(--color-text) 55%, transparent); } | 66 | .text-muted { color: color-mix(in srgb, var(--color-text) 55%, transparent); } |
| 65 | 67 | ||
| @@ -179,13 +181,27 @@ input[type=number]::-webkit-inner-spin-button { -webkit-appearance: none; margin | |||
| 179 | .picked { display: flex; align-items: center; gap: var(--space-2); margin-bottom: var(--space-6); } | 181 | .picked { display: flex; align-items: center; gap: var(--space-2); margin-bottom: var(--space-6); } |
| 180 | .picked .pname { font-size: 20px; font-family: var(--font-heading); } | 182 | .picked .pname { font-size: 20px; font-family: var(--font-heading); } |
| 181 | .mini-label { font-size: 12px; opacity: .7; margin: 0 0 var(--space-2); } | 183 | .mini-label { font-size: 12px; opacity: .7; margin: 0 0 var(--space-2); } |
| 182 | -.duration-row { display: flex; align-items: baseline; gap: 6px; margin-bottom: var(--space-3); } | 184 | +.duration-row { display: flex; align-items: center; gap: var(--space-3); margin-bottom: var(--space-3); } |
| 183 | -.duration-row .minutes-input { | 185 | +.stepper { |
| 184 | - width: 130px; font-size: 34px; font-family: var(--font-heading); | 186 | + display: flex; align-items: center; gap: var(--space-1); |
| 185 | - min-height: 64px; text-align: center; padding: 0 10px; | 187 | + background: var(--color-surface); border: 1px solid var(--color-divider); |
| 188 | + border-radius: 999px; padding: 5px 6px; | ||
| 186 | } | 189 | } |
| 187 | -.duration-row .unit { font-size: 16px; opacity: .6; } | 190 | +.stepper-btn { |
| 188 | -.duration-row .pretty { margin-left: auto; font-size: 14px; opacity: .55; } | 191 | + width: 40px; height: 40px; flex: none; |
| 192 | + display: flex; align-items: center; justify-content: center; | ||
| 193 | + border: none; border-radius: 999px; background: transparent; | ||
| 194 | + color: var(--color-text); cursor: pointer; text-decoration: none; | ||
| 195 | +} | ||
| 196 | +.stepper-btn:hover { background: var(--color-accent-200); } | ||
| 197 | +.stepper-btn:active { background: var(--color-accent-300); } | ||
| 198 | +.stepper .minutes-input { | ||
| 199 | + width: 92px; font-size: 32px; font-family: var(--font-heading); | ||
| 200 | + min-height: 44px; text-align: center; padding: 0; | ||
| 201 | + background: transparent; border-color: transparent; | ||
| 202 | +} | ||
| 203 | +.duration-row .unit { font-size: 15px; opacity: .7; } | ||
| 204 | +.duration-row .pretty { font-size: 13px; opacity: .5; } | ||
| 189 | .presets { display: flex; gap: var(--space-2); flex-wrap: wrap; margin-bottom: var(--space-6); } | 205 | .presets { display: flex; gap: var(--space-2); flex-wrap: wrap; margin-bottom: var(--space-6); } |
| 190 | .preset { | 206 | .preset { |
| 191 | padding: 9px 16px; border-radius: 999px; font-size: 14px; | 207 | padding: 9px 16px; border-radius: 999px; font-size: 14px; |
modified
src/routes/entries.rs +6 -0 | @@ -55,6 +55,9 @@ pub struct AddStep2Page { | ||
| 55 | 55 | pub picked_name: String, |
| 56 | 56 | pub minutes: i32, |
| 57 | 57 | pub minutes_pretty: String, |
| 58 | + /// Steppers ± 5 minutes (liens, zéro JS). | |
| 59 | + pub minus_href: String, | |
| 60 | + pub plus_href: String, | |
| 58 | 61 | pub presets: Vec<Preset>, |
| 59 | 62 | pub date: NaiveDate, |
| 60 | 63 | pub date_label: String, |
| @@ -108,6 +111,9 @@ pub async fn entry_new( | ||
| 108 | 111 | picked_name: picked.name.clone(), |
| 109 | 112 | minutes, |
| 110 | 113 | minutes_pretty: fmt_minutes(minutes as i64), |
| 114 | + // Comme la maquette : jamais sous 5 min via le stepper. | |
| 115 | + minus_href: step2_href(picked.id, (minutes - 5).max(5), date), | |
| 116 | + plus_href: step2_href(picked.id, (minutes + 5).min(1440), date), | |
| 111 | 117 | presets, |
| 112 | 118 | date, |
| 113 | 119 | date_label: day_label(date), |
| @@ -55,6 +55,9 @@ pub struct AddStep2Page { | |||
| 55 | pub picked_name: String, | 55 | pub picked_name: String, |
| 56 | pub minutes: i32, | 56 | pub minutes: i32, |
| 57 | pub minutes_pretty: String, | 57 | pub minutes_pretty: String, |
| 58 | + /// Steppers ± 5 minutes (liens, zéro JS). | ||
| 59 | + pub minus_href: String, | ||
| 60 | + pub plus_href: String, | ||
| 58 | pub presets: Vec<Preset>, | 61 | pub presets: Vec<Preset>, |
| 59 | pub date: NaiveDate, | 62 | pub date: NaiveDate, |
| 60 | pub date_label: String, | 63 | pub date_label: String, |
| @@ -108,6 +111,9 @@ pub async fn entry_new( | |||
| 108 | picked_name: picked.name.clone(), | 111 | picked_name: picked.name.clone(), |
| 109 | minutes, | 112 | minutes, |
| 110 | minutes_pretty: fmt_minutes(minutes as i64), | 113 | minutes_pretty: fmt_minutes(minutes as i64), |
| 114 | + // Comme la maquette : jamais sous 5 min via le stepper. | ||
| 115 | + minus_href: step2_href(picked.id, (minutes - 5).max(5), date), | ||
| 116 | + plus_href: step2_href(picked.id, (minutes + 5).min(1440), date), | ||
| 111 | presets, | 117 | presets, |
| 112 | date, | 118 | date, |
| 113 | date_label: day_label(date), | 119 | date_label: day_label(date), |
modified
templates/add_step2.html +14 -4 | @@ -12,10 +12,20 @@ | ||
| 12 | 12 | |
| 13 | 13 | <p class="mini-label">Durée</p> |
| 14 | 14 | <div class="duration-row"> |
| 15 | - <input class="input minutes-input" name="minutes" type="number" inputmode="numeric" | |
| 16 | - min="1" max="1440" required value="{{ minutes }}"> | |
| 17 | - <span class="unit">minutes</span> | |
| 18 | - <span class="pretty">{{ minutes_pretty }}</span> | |
| 15 | + <div class="stepper"> | |
| 16 | + <a class="stepper-btn" href="{{ minus_href }}" aria-label="Cinq minutes de moins"> | |
| 17 | + <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.75" stroke-linecap="round"><path d="M5 12h14"></path></svg> | |
| 18 | + </a> | |
| 19 | + <input class="input minutes-input" name="minutes" type="number" inputmode="numeric" | |
| 20 | + min="1" max="1440" step="5" required value="{{ minutes }}"> | |
| 21 | + <a class="stepper-btn" href="{{ plus_href }}" aria-label="Cinq minutes de plus"> | |
| 22 | + <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.75" stroke-linecap="round"><path d="M12 5v14M5 12h14"></path></svg> | |
| 23 | + </a> | |
| 24 | + </div> | |
| 25 | + <div> | |
| 26 | + <div class="unit">minutes</div> | |
| 27 | + <div class="pretty">{{ minutes_pretty }}</div> | |
| 28 | + </div> | |
| 19 | 29 | </div> |
| 20 | 30 | <div class="presets"> |
| 21 | 31 | {% for p in presets %} |
| @@ -12,10 +12,20 @@ | |||
| 12 | 12 | ||
| 13 | <p class="mini-label">Durée</p> | 13 | <p class="mini-label">Durée</p> |
| 14 | <div class="duration-row"> | 14 | <div class="duration-row"> |
| 15 | - <input class="input minutes-input" name="minutes" type="number" inputmode="numeric" | 15 | + <div class="stepper"> |
| 16 | - min="1" max="1440" required value="{{ minutes }}"> | 16 | + <a class="stepper-btn" href="{{ minus_href }}" aria-label="Cinq minutes de moins"> |
| 17 | - <span class="unit">minutes</span> | 17 | + <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.75" stroke-linecap="round"><path d="M5 12h14"></path></svg> |
| 18 | - <span class="pretty">{{ minutes_pretty }}</span> | 18 | + </a> |
| 19 | + <input class="input minutes-input" name="minutes" type="number" inputmode="numeric" | ||
| 20 | + min="1" max="1440" step="5" required value="{{ minutes }}"> | ||
| 21 | + <a class="stepper-btn" href="{{ plus_href }}" aria-label="Cinq minutes de plus"> | ||
| 22 | + <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.75" stroke-linecap="round"><path d="M12 5v14M5 12h14"></path></svg> | ||
| 23 | + </a> | ||
| 24 | + </div> | ||
| 25 | + <div> | ||
| 26 | + <div class="unit">minutes</div> | ||
| 27 | + <div class="pretty">{{ minutes_pretty }}</div> | ||
| 28 | + </div> | ||
| 19 | </div> | 29 | </div> |
| 20 | <div class="presets"> | 30 | <div class="presets"> |
| 21 | {% for p in presets %} | 31 | {% for p in presets %} |