@@ -23,8 +23,22 @@ function init() {
2323 const resetBtns = container . querySelectorAll ( "[data-taxation-reset]" ) ;
2424
2525 const ctx = { container, config, i18n, state } ;
26+ const mobileMq = window . matchMedia ( "(max-width: 992px)" ) ;
27+
28+ function scrollCalculatorToTopOnMobile ( ) {
29+ if ( ! mobileMq . matches ) return ;
30+ const MOBILE_HEADER_OFFSET = 88 ;
31+ const targetTop = window . scrollY + container . getBoundingClientRect ( ) . top ;
32+ window . scrollTo ( {
33+ top : Math . max ( 0 , targetTop - MOBILE_HEADER_OFFSET ) ,
34+ behavior : "smooth" ,
35+ } ) ;
36+ }
37+
38+ ctx . scrollCalculatorToTopOnMobile = scrollCalculatorToTopOnMobile ;
2639
2740 function rerenderAll ( ) {
41+ renderPersonLimitControlInput ( ctx , saveState ) ;
2842 operatorsWrapper . innerHTML = "" ;
2943 nationalWrapper . innerHTML = "" ;
3044 const otherWrapper = container . querySelector ( "[data-taxation-other]" ) ;
@@ -94,17 +108,72 @@ function init() {
94108 updateSummary ( container , config , i18n , state ) ;
95109 }
96110
111+ function onPersonLimitChange ( ) {
112+ for ( const op of config . operators ) {
113+ const opState = state . operators [ op . slug ] ;
114+ if ( ! hasActiveState ( opState ) ) continue ;
115+ const row = operatorsWrapper . querySelector (
116+ '[data-taxation-row="' + op . slug + '"]' ,
117+ ) ;
118+ if ( ! row ) continue ;
119+ updateRowState (
120+ row ,
121+ opState ,
122+ op . firstClass || 0 ,
123+ op . secondClass ,
124+ state . personLimit ,
125+ true ,
126+ op . singlePersonOnly || false ,
127+ ) ;
128+ updateRowMultipliers (
129+ row ,
130+ op . singlePersonOnly ? 1 : state . personLimit ,
131+ i18n ,
132+ ) ;
133+ updateRowWarning (
134+ row ,
135+ op . singleClassOnly || false ,
136+ op . singlePersonOnly || false ,
137+ state . personLimit ,
138+ i18n ,
139+ ) ;
140+ }
141+
142+ for ( const nat of config . national ) {
143+ const natState = state . national [ nat . key ] ;
144+ if ( ! hasActiveState ( natState ) ) continue ;
145+ const row = nationalWrapper . querySelector (
146+ '[data-taxation-row="' + nat . key + '"]' ,
147+ ) ;
148+ if ( ! row ) continue ;
149+ updateRowState (
150+ row ,
151+ natState ,
152+ nat . firstClass ,
153+ nat . secondClass ,
154+ state . personLimit ,
155+ false ,
156+ false ,
157+ ) ;
158+ }
159+
160+ runPlanning ( ctx ) ;
161+ updateSummary ( container , config , i18n , state ) ;
162+ }
163+
164+ ctx . onPersonLimitChange = onPersonLimitChange ;
165+
97166 ctx . rerenderAll = rerenderAll ;
98167 ctx . runOptimization = runOptimization ;
99168
100- renderPersonLimitControlInput ( ctx , saveState ) ;
101169 rerenderAll ( ) ;
102170
103171 if ( resetBtns . length > 0 && operatorsWrapper && nationalWrapper ) {
104172 const resetCalculator = function ( ) {
105173 state . operators = { } ;
106174 state . national = { } ;
107175 state . other = 0 ;
176+ state . personLimit = 1 ;
108177 state . planningAssignments = { } ;
109178 state . planningManualAssignments = { } ;
110179 state . planningMonthCount = 0 ;
@@ -130,35 +199,19 @@ function getI18n(container) {
130199 addOperator : container . dataset . i18nAddOperator ,
131200 addNational : container . dataset . i18nAddNational ,
132201 remove : container . dataset . i18nRemove ,
133- perField : container . dataset . i18nPerField ,
134- field : container . dataset . i18nField ,
135- fields : container . dataset . i18nFields ,
136202 increase : container . dataset . i18nIncrease ,
137203 decrease : container . dataset . i18nDecrease ,
138- sum : container . dataset . i18nSum ,
139- categoryInternational : container . dataset . i18nCategoryInternational ,
140- categoryNational : container . dataset . i18nCategoryNational ,
141204 categoryOther : container . dataset . i18nCategoryOther ,
142205 otherPlaceholder : container . dataset . i18nOtherPlaceholder ,
143- classMixedWarning : container . dataset . i18nClassMixedWarning ,
144- secondPersonHint : container . dataset . i18nSecondPersonHint ,
145206 highlightImportant : container . dataset . i18nHighlightImportant ,
146- highlightTip : container . dataset . i18nHighlightTip ,
147- thresholdAbove : container . dataset . i18nThresholdAbove ,
148- thresholdBelow : container . dataset . i18nThresholdBelow ,
149- thresholdExcess : container . dataset . i18nThresholdExcess ,
150- disclaimer : container . dataset . i18nDisclaimer ,
151- personsRequired : container . dataset . i18nPersonsRequired ,
152- personLimit : container . dataset . i18nPersonLimit ,
153207 noRelativesWarning : container . dataset . i18nNoRelativesWarning ,
154- reset : container . dataset . i18nReset ,
155208 planning : container . dataset . i18nPlanning ,
209+ planningDescription : container . dataset . i18nPlanningDescription ,
210+ planningFocus : container . dataset . i18nPlanningFocus ,
156211 optimize : container . dataset . i18nOptimize ,
157- manualMode : container . dataset . i18nManualMode ,
158- optimizationTitle : container . dataset . i18nOptimizationTitle ,
212+ addMonth : container . dataset . i18nAddMonth ,
159213 optimizationMonth : container . dataset . i18nOptimizationMonth ,
160- optimizationItems : container . dataset . i18nOptimizationItems ,
161- optimizationImpossible : container . dataset . i18nOptimizationImpossible ,
214+ personLimit : container . dataset . i18nPersonLimit ,
162215 unassigned : container . dataset . i18nUnassigned ,
163216 person : container . dataset . i18nPerson ,
164217 persons : container . dataset . i18nPersons ,
@@ -736,6 +789,7 @@ function createSearchSelect(options) {
736789 list . className = "o-taxation-calculator__search-list" ;
737790 list . setAttribute ( "role" , "listbox" ) ;
738791 list . setAttribute ( "aria-hidden" , "true" ) ;
792+ list . inert = true ;
739793
740794 const activeKeys = new Set ( ) ;
741795
@@ -755,6 +809,7 @@ function createSearchSelect(options) {
755809 const btn = document . createElement ( "button" ) ;
756810 btn . type = "button" ;
757811 btn . className = "o-taxation-calculator__search-item-btn" ;
812+ btn . setAttribute ( "aria-label" , item . label ) ;
758813
759814 const addIcon = createIcon ( "add_circle" ) ;
760815 addIcon . classList . add ( "o-taxation-calculator__search-add-icon" ) ;
@@ -785,34 +840,24 @@ function createSearchSelect(options) {
785840
786841 function ensureDropdownSpace ( ) {
787842 const wrapperRect = wrapper . getBoundingClientRect ( ) ;
788- const maxHeight = parseFloat ( window . getComputedStyle ( list ) . maxHeight ) || 0 ;
789- const listHeight = Math . min (
790- list . scrollHeight ,
791- maxHeight || list . scrollHeight ,
792- ) ;
793- const requiredBottom = wrapperRect . bottom + listHeight ;
794-
795- const calculator = wrapper . closest ( "[data-taxation-calculator]" ) ;
796- const sheet = calculator
797- ? calculator . querySelector ( "[data-taxation-sheet]" )
798- : null ;
799- const hasVisibleSheet =
800- sheet && window . getComputedStyle ( sheet ) . display !== "none" ;
801- const bottomLimit = hasVisibleSheet
802- ? sheet . getBoundingClientRect ( ) . top - 28
803- : window . innerHeight - 28 ;
804-
805- const delta = requiredBottom - bottomLimit ;
806- if ( hasVisibleSheet ) {
807- window . scrollTo ( window . scrollX , window . scrollY + delta ) ;
808- } else if ( delta > 0 ) {
809- window . scrollTo ( window . scrollX , window . scrollY + delta ) ;
843+ const header = document . getElementById ( "header" ) ;
844+ const headerHeight = header ? header . getBoundingClientRect ( ) . height : 60 ;
845+ const extraTopGap = 12 ;
846+ const targetTop =
847+ window . scrollY + wrapperRect . top - ( headerHeight + extraTopGap ) ;
848+ const currentTop = window . scrollY ;
849+ if ( Math . abs ( targetTop - currentTop ) > 2 ) {
850+ window . scrollTo ( {
851+ top : Math . max ( 0 , targetTop ) ,
852+ behavior : "smooth" ,
853+ } ) ;
810854 }
811855 }
812856
813857 function openList ( ) {
814858 const count = buildItems ( ) ;
815859 if ( count > 0 ) {
860+ list . inert = false ;
816861 list . setAttribute ( "aria-hidden" , "false" ) ;
817862 input . setAttribute ( "aria-expanded" , "true" ) ;
818863 wrapper . classList . add ( "o-taxation-calculator__search-select--open" ) ;
@@ -821,6 +866,10 @@ function createSearchSelect(options) {
821866 }
822867
823868 function closeList ( ) {
869+ if ( list . contains ( document . activeElement ) ) {
870+ input . focus ( { preventScroll : true } ) ;
871+ }
872+ list . inert = true ;
824873 list . setAttribute ( "aria-hidden" , "true" ) ;
825874 input . setAttribute ( "aria-expanded" , "false" ) ;
826875 wrapper . classList . remove ( "o-taxation-calculator__search-select--open" ) ;
@@ -998,6 +1047,7 @@ function renderOperators(ctx, wrapper) {
9981047 items : selectItems ,
9991048 onSelect ( item ) {
10001049 addOperatorCard ( ctx , wrapper , searchSelect , item . data ) ;
1050+ ctx . scrollCalculatorToTopOnMobile ( ) ;
10011051 } ,
10021052 } ) ;
10031053
@@ -1030,6 +1080,7 @@ function renderNational(ctx, wrapper) {
10301080 items : selectItems ,
10311081 onSelect ( item ) {
10321082 addNationalCard ( ctx , wrapper , searchSelect , item . data ) ;
1083+ ctx . scrollCalculatorToTopOnMobile ( ) ;
10331084 } ,
10341085 } ) ;
10351086
@@ -1138,18 +1189,6 @@ function updateSummary(container, config, i18n, state) {
11381189 summaryHint ,
11391190 ) ;
11401191
1141- updateSummaryBlock (
1142- container ,
1143- {
1144- totalEl : container . querySelector ( "[data-taxation-bottom-total]" ) ,
1145- personsEl : container . querySelector ( "[data-taxation-bottom-persons]" ) ,
1146- thresholdEl : container . querySelector ( "[data-taxation-bottom-threshold]" ) ,
1147- } ,
1148- total ,
1149- totalPersons ,
1150- summaryHint ,
1151- ) ;
1152-
11531192 updateThresholdBlock (
11541193 container . querySelector ( "[data-taxation-mobile-threshold-detail]" ) ,
11551194 summaryHint ,
@@ -1185,6 +1224,20 @@ function runPlanning(ctx) {
11851224 }
11861225}
11871226
1227+ function updateRowMultipliers ( row , effectivePersonLimit , i18n ) {
1228+ var multiplierEls = row . querySelectorAll (
1229+ ".o-taxation-calculator__multiplier" ,
1230+ ) ;
1231+ for ( const multiplierEl of multiplierEls ) {
1232+ if ( effectivePersonLimit === 1 ) {
1233+ multiplierEl . textContent = "x 1 " + i18n . person ;
1234+ } else {
1235+ multiplierEl . textContent =
1236+ "x " + effectivePersonLimit + " " + i18n . persons ;
1237+ }
1238+ }
1239+ }
1240+
11881241function updateSummaryBlock ( container , els , total , totalPersons , summaryHint ) {
11891242 if ( ! els . totalEl ) return ;
11901243
0 commit comments