@oslokommune/punkt-elements 14.0.2 → 14.0.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.
Files changed (30) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/calendar-BtShW7ER.cjs +90 -0
  3. package/dist/{calendar-Bz27nuTP.js → calendar-yxjSI4wd.js} +766 -682
  4. package/dist/datepicker-D0q75U1Z.js +1463 -0
  5. package/dist/datepicker-DDV382Uu.cjs +271 -0
  6. package/dist/index.d.ts +118 -83
  7. package/dist/pkt-calendar.cjs +1 -1
  8. package/dist/pkt-calendar.js +1 -1
  9. package/dist/pkt-datepicker.cjs +1 -1
  10. package/dist/pkt-datepicker.js +2 -2
  11. package/dist/pkt-index.cjs +1 -1
  12. package/dist/pkt-index.js +3 -3
  13. package/package.json +2 -2
  14. package/src/components/calendar/calendar.ts +372 -414
  15. package/src/components/calendar/helpers/calendar-grid.ts +93 -0
  16. package/src/components/calendar/helpers/date-validation.ts +86 -0
  17. package/src/components/calendar/helpers/index.ts +49 -0
  18. package/src/components/calendar/helpers/keyboard-navigation.ts +54 -0
  19. package/src/components/calendar/helpers/selection-manager.ts +184 -0
  20. package/src/components/datepicker/datepicker-base.ts +151 -0
  21. package/src/components/datepicker/datepicker-multiple.ts +7 -114
  22. package/src/components/datepicker/datepicker-range.ts +21 -141
  23. package/src/components/datepicker/datepicker-single.ts +7 -115
  24. package/src/components/datepicker/datepicker-types.ts +56 -0
  25. package/src/components/datepicker/datepicker-utils.test.ts +730 -0
  26. package/src/components/datepicker/datepicker-utils.ts +338 -9
  27. package/src/components/datepicker/datepicker.ts +25 -1
  28. package/dist/calendar-Dz1Cnzx5.cjs +0 -115
  29. package/dist/datepicker-CnCOXI2x.cjs +0 -289
  30. package/dist/datepicker-DsqM01iU.js +0 -1355
@@ -2,64 +2,23 @@ import { html } from 'lit'
2
2
  import { customElement, property } from 'lit/decorators.js'
3
3
  import { classMap } from 'lit/directives/class-map.js'
4
4
  import { ifDefined } from 'lit/directives/if-defined.js'
5
- import { Ref, createRef, ref } from 'lit/directives/ref.js'
6
- import { PktElement } from '@/base-elements/element'
7
- import { keyboardUtils, formUtils, cssUtils } from './datepicker-utils'
5
+ import { ref } from 'lit/directives/ref.js'
6
+ import { PktDatepickerBase } from './datepicker-base'
7
+ import { keyboardUtils, formUtils } from './datepicker-utils'
8
+ import { IDatepickerStrings, defaultSingleStrings } from './datepicker-types'
8
9
  import { isIOS } from 'shared-utils/device-utils'
9
10
  import '@/components/icon'
10
11
 
11
12
  @customElement('pkt-datepicker-multiple')
12
- export class PktDatepickerMultiple extends PktElement {
13
+ export class PktDatepickerMultiple extends PktDatepickerBase {
13
14
  @property({ type: Array })
14
15
  value: string[] = []
15
16
 
16
- @property({ type: String })
17
- inputType: string = 'date'
18
-
19
- @property({ type: String })
20
- id: string = ''
21
-
22
- @property({ type: String })
23
- min?: string
24
-
25
- @property({ type: String })
26
- max?: string
27
-
28
- @property({ type: String })
29
- placeholder?: string
30
-
31
- @property({ type: Boolean })
32
- readonly: boolean = false
33
-
34
- @property({ type: Boolean })
35
- disabled: boolean = false
36
-
37
17
  @property({ type: Number })
38
18
  maxlength?: number
39
19
 
40
20
  @property({ type: Object })
41
- inputClasses: Record<string, boolean> = {}
42
-
43
- @property({ type: Object })
44
- internals?: any
45
-
46
- @property({ type: Object })
47
- strings: any = { calendar: { buttonAltText: 'Åpne kalender' } }
48
-
49
- inputRef: Ref<HTMLInputElement> = createRef()
50
- btnRef: Ref<HTMLButtonElement> = createRef()
51
-
52
- get inputElement(): HTMLInputElement | undefined {
53
- return this.inputRef.value
54
- }
55
-
56
- get buttonElement(): HTMLButtonElement | undefined {
57
- return this.btnRef.value
58
- }
59
-
60
- get isInputReadonly(): boolean {
61
- return this.readonly || this.inputType === 'text'
62
- }
21
+ strings: IDatepickerStrings = defaultSingleStrings
63
22
 
64
23
  get isInputDisabled(): boolean {
65
24
  return (
@@ -70,57 +29,6 @@ export class PktDatepickerMultiple extends PktElement {
70
29
  )
71
30
  }
72
31
 
73
- private dispatchToggleCalendar(e: Event) {
74
- if (this.readonly) return
75
-
76
- this.dispatchEvent(
77
- new CustomEvent('toggle-calendar', {
78
- detail: e,
79
- bubbles: true,
80
- composed: true,
81
- }),
82
- )
83
- }
84
-
85
- private dispatchInput(e: Event) {
86
- this.dispatchEvent(
87
- new CustomEvent('input-change', {
88
- detail: e,
89
- bubbles: true,
90
- composed: true,
91
- }),
92
- )
93
- }
94
-
95
- private dispatchFocus() {
96
- this.dispatchEvent(
97
- new CustomEvent('input-focus', {
98
- bubbles: true,
99
- composed: true,
100
- }),
101
- )
102
- }
103
-
104
- private dispatchBlur(e: FocusEvent) {
105
- this.dispatchEvent(
106
- new CustomEvent('input-blur', {
107
- detail: e,
108
- bubbles: true,
109
- composed: true,
110
- }),
111
- )
112
- }
113
-
114
- private dispatchChange(e: Event) {
115
- this.dispatchEvent(
116
- new CustomEvent('input-changed', {
117
- detail: e,
118
- bubbles: true,
119
- composed: true,
120
- }),
121
- )
122
- }
123
-
124
32
  private dispatchAddToSelected(e: Event | KeyboardEvent) {
125
33
  this.dispatchEvent(
126
34
  new CustomEvent('add-to-selected', {
@@ -131,10 +39,6 @@ export class PktDatepickerMultiple extends PktElement {
131
39
  )
132
40
  }
133
41
 
134
- createRenderRoot() {
135
- return this
136
- }
137
-
138
42
  render() {
139
43
  return html`
140
44
  <div class="pkt-input__container">
@@ -185,18 +89,7 @@ export class PktDatepickerMultiple extends PktElement {
185
89
  }}
186
90
  ${ref(this.inputRef)}
187
91
  />
188
- <button
189
- class="${classMap(cssUtils.getButtonClasses())}"
190
- type="button"
191
- @click=${(e: Event) => this.dispatchToggleCalendar(e)}
192
- @keydown=${(e: KeyboardEvent) =>
193
- keyboardUtils.handleButtonKeydown(e, (event) => this.dispatchToggleCalendar(event))}
194
- ?disabled=${this.disabled}
195
- ${ref(this.btnRef)}
196
- >
197
- <pkt-icon name="calendar"></pkt-icon>
198
- <span class="pkt-btn__text">${this.strings.calendar.buttonAltText}</span>
199
- </button>
92
+ ${this.renderCalendarButton()}
200
93
  </div>
201
94
  `
202
95
  }
@@ -3,141 +3,36 @@ import { customElement, property } from 'lit/decorators.js'
3
3
  import { classMap } from 'lit/directives/class-map.js'
4
4
  import { ifDefined } from 'lit/directives/if-defined.js'
5
5
  import { Ref, createRef, ref } from 'lit/directives/ref.js'
6
- import { PktElement } from '@/base-elements/element'
6
+ import { PktDatepickerBase } from './datepicker-base'
7
7
  import { keyboardUtils, formUtils, cssUtils } from './datepicker-utils'
8
+ import { IDatepickerStrings, defaultRangeStrings } from './datepicker-types'
8
9
  import { isIOS } from 'shared-utils/device-utils'
9
10
  import '@/components/icon'
10
11
 
11
12
  @customElement('pkt-datepicker-range')
12
- export class PktDatepickerRange extends PktElement {
13
+ export class PktDatepickerRange extends PktDatepickerBase {
13
14
  @property({ type: Array })
14
15
  value: string[] = []
15
16
 
16
- @property({ type: String })
17
- inputType: string = 'date'
18
-
19
- @property({ type: String })
20
- id: string = ''
21
-
22
- @property({ type: String })
23
- min?: string
24
-
25
- @property({ type: String })
26
- max?: string
27
-
28
- @property({ type: String })
29
- placeholder?: string
30
-
31
- @property({ type: Boolean })
32
- readonly: boolean = false
33
-
34
- @property({ type: Boolean })
35
- disabled: boolean = false
36
-
37
17
  @property({ type: Boolean })
38
18
  showRangeLabels: boolean = false
39
19
 
40
20
  @property({ type: Object })
41
- inputClasses: Record<string, boolean> = {}
42
-
43
- @property({ type: Object })
44
- internals?: any
45
-
46
- @property({ type: Object })
47
- strings: any = { generic: { from: 'Fra', to: 'Til' } }
21
+ strings: IDatepickerStrings = defaultRangeStrings
48
22
 
49
- inputRef: Ref<HTMLInputElement> = createRef()
50
23
  inputRefTo: Ref<HTMLInputElement> = createRef()
51
- btnRef: Ref<HTMLButtonElement> = createRef()
52
-
53
- get inputElement(): HTMLInputElement | undefined {
54
- return this.inputRef.value
55
- }
56
24
 
57
25
  get inputElementTo(): HTMLInputElement | undefined {
58
26
  return this.inputRefTo.value
59
27
  }
60
28
 
61
- get buttonElement(): HTMLButtonElement | undefined {
62
- return this.btnRef.value
63
- }
64
-
65
- get isInputReadonly(): boolean {
66
- return this.readonly || this.inputType === 'text'
67
- }
68
-
69
- private dispatchToggleCalendar(e: Event) {
70
- if (this.readonly) return
71
-
72
- this.dispatchEvent(
73
- new CustomEvent('toggle-calendar', {
74
- detail: e,
75
- bubbles: true,
76
- composed: true,
77
- }),
78
- )
79
- }
80
-
81
- private dispatchInput(e: Event) {
82
- this.dispatchEvent(
83
- new CustomEvent('input-change', {
84
- detail: e,
85
- bubbles: true,
86
- composed: true,
87
- }),
88
- )
89
- }
90
-
91
- private dispatchFocus() {
92
- this.dispatchEvent(
93
- new CustomEvent('input-focus', {
94
- bubbles: true,
95
- composed: true,
96
- }),
97
- )
98
- }
99
-
100
- private dispatchBlur(e: FocusEvent) {
101
- this.dispatchEvent(
102
- new CustomEvent('input-blur', {
103
- detail: e,
104
- bubbles: true,
105
- composed: true,
106
- }),
107
- )
108
- }
109
-
110
- private dispatchChange(e: Event) {
111
- this.dispatchEvent(
112
- new CustomEvent('input-changed', {
113
- detail: e,
114
- bubbles: true,
115
- composed: true,
116
- }),
117
- )
118
- }
119
-
120
- private dispatchManageValidity(input: HTMLInputElement) {
121
- this.dispatchEvent(
122
- new CustomEvent('manage-validity', {
123
- detail: input,
124
- bubbles: true,
125
- composed: true,
126
- }),
127
- )
128
- }
129
-
130
- createRenderRoot() {
131
- return this
132
- }
133
-
134
29
  render() {
135
30
  const rangeLabelClasses = cssUtils.getRangeLabelClasses(this.showRangeLabels)
136
31
 
137
32
  return html`
138
33
  <div class="pkt-input__container">
139
34
  ${this.showRangeLabels
140
- ? html` <div class="pkt-input-prefix">${this.strings.generic.from}</div> `
35
+ ? html` <div class="pkt-input-prefix">${this.strings.generic?.from}</div> `
141
36
  : nothing}
142
37
  <input
143
38
  class=${classMap(this.inputClasses)}
@@ -178,7 +73,8 @@ export class PktDatepickerRange extends PktElement {
178
73
  this.dispatchToggleCalendar(new Event('focus'))
179
74
  }
180
75
  }}
181
- @blur=${(e: Event) => {
76
+ @blur=${(e: FocusEvent) => {
77
+ this.dispatchBlur(e)
182
78
  this.dispatchEvent(
183
79
  new CustomEvent('range-blur', {
184
80
  detail: {
@@ -192,12 +88,13 @@ export class PktDatepickerRange extends PktElement {
192
88
  )
193
89
  }}
194
90
  @change=${(e: Event) => {
91
+ this.dispatchChange(e)
195
92
  e.stopImmediatePropagation()
196
93
  }}
197
94
  ${ref(this.inputRef)}
198
95
  />
199
96
  <div class="${classMap(rangeLabelClasses)}" id="${this.id}-to-label">
200
- ${this.strings.generic.to}
97
+ ${this.strings.generic?.to}
201
98
  </div>
202
99
  ${!this.showRangeLabels ? html` <div class="pkt-input-separator">–</div> ` : nothing}
203
100
  <input
@@ -240,23 +137,17 @@ export class PktDatepickerRange extends PktElement {
240
137
  }}
241
138
  @blur=${(e: FocusEvent) => {
242
139
  this.dispatchBlur(e)
243
- if ((e.target as HTMLInputElement).value) {
244
- this.dispatchManageValidity(e.target as HTMLInputElement)
245
- this.dispatchEvent(
246
- new CustomEvent('validate-date-input', {
247
- detail: e.target as HTMLInputElement,
248
- bubbles: true,
249
- composed: true,
250
- }),
251
- )
252
- this.dispatchEvent(
253
- new CustomEvent('handle-date-select', {
254
- detail: (e.target as HTMLInputElement).value,
255
- bubbles: true,
256
- composed: true,
257
- }),
258
- )
259
- }
140
+ this.dispatchEvent(
141
+ new CustomEvent('range-blur', {
142
+ detail: {
143
+ event: e,
144
+ values: this.value,
145
+ inputType: 'to',
146
+ },
147
+ bubbles: true,
148
+ composed: true,
149
+ }),
150
+ )
260
151
  }}
261
152
  @change=${(e: Event) => {
262
153
  this.dispatchChange(e)
@@ -264,18 +155,7 @@ export class PktDatepickerRange extends PktElement {
264
155
  }}
265
156
  ${ref(this.inputRefTo)}
266
157
  />
267
- <button
268
- class="${classMap(cssUtils.getButtonClasses())}"
269
- type="button"
270
- @click=${(e: Event) => this.dispatchToggleCalendar(e)}
271
- @keydown=${(e: KeyboardEvent) =>
272
- keyboardUtils.handleButtonKeydown(e, (event) => this.dispatchToggleCalendar(event))}
273
- ?disabled=${this.disabled}
274
- ${ref(this.btnRef)}
275
- >
276
- <pkt-icon name="calendar"></pkt-icon>
277
- <span class="pkt-btn__text">${this.strings.calendar.buttonAltText}</span>
278
- </button>
158
+ ${this.renderCalendarButton()}
279
159
  </div>
280
160
  `
281
161
  }
@@ -2,112 +2,19 @@ import { html } from 'lit'
2
2
  import { customElement, property } from 'lit/decorators.js'
3
3
  import { classMap } from 'lit/directives/class-map.js'
4
4
  import { ifDefined } from 'lit/directives/if-defined.js'
5
- import { Ref, createRef, ref } from 'lit/directives/ref.js'
6
- import { PktElement } from '@/base-elements/element'
7
- import { keyboardUtils, formUtils, cssUtils } from './datepicker-utils'
5
+ import { ref } from 'lit/directives/ref.js'
6
+ import { PktDatepickerBase } from './datepicker-base'
7
+ import { keyboardUtils, formUtils } from './datepicker-utils'
8
+ import { IDatepickerStrings, defaultSingleStrings } from './datepicker-types'
8
9
  import { isIOS } from 'shared-utils/device-utils'
9
- import '@/components/icon'
10
10
 
11
11
  @customElement('pkt-datepicker-single')
12
- export class PktDatepickerSingle extends PktElement {
12
+ export class PktDatepickerSingle extends PktDatepickerBase {
13
13
  @property({ type: String })
14
14
  value: string = ''
15
15
 
16
- @property({ type: String })
17
- inputType: string = 'date'
18
-
19
- @property({ type: String })
20
- id: string = ''
21
-
22
- @property({ type: String })
23
- min?: string
24
-
25
- @property({ type: String })
26
- max?: string
27
-
28
- @property({ type: String })
29
- placeholder?: string
30
-
31
- @property({ type: Boolean })
32
- readonly: boolean = false
33
-
34
- @property({ type: Boolean })
35
- disabled: boolean = false
36
-
37
- @property({ type: Object })
38
- inputClasses: Record<string, boolean> = {}
39
-
40
- @property({ type: Object })
41
- internals?: any
42
-
43
16
  @property({ type: Object })
44
- strings: any = { calendar: { buttonAltText: 'Åpne kalender' } }
45
-
46
- inputRef: Ref<HTMLInputElement> = createRef()
47
- btnRef: Ref<HTMLButtonElement> = createRef()
48
-
49
- get inputElement(): HTMLInputElement | undefined {
50
- return this.inputRef.value
51
- }
52
-
53
- get buttonElement(): HTMLButtonElement | undefined {
54
- return this.btnRef.value
55
- }
56
-
57
- get isInputReadonly(): boolean {
58
- return this.readonly || this.inputType === 'text'
59
- }
60
-
61
- private dispatchToggleCalendar(e: Event) {
62
- if (this.readonly) return
63
-
64
- this.dispatchEvent(
65
- new CustomEvent('toggle-calendar', {
66
- detail: e,
67
- bubbles: true,
68
- composed: true,
69
- }),
70
- )
71
- }
72
-
73
- private dispatchInput(e: Event) {
74
- this.dispatchEvent(
75
- new CustomEvent('input-change', {
76
- detail: e,
77
- bubbles: true,
78
- composed: true,
79
- }),
80
- )
81
- }
82
-
83
- private dispatchFocus() {
84
- this.dispatchEvent(
85
- new CustomEvent('input-focus', {
86
- bubbles: true,
87
- composed: true,
88
- }),
89
- )
90
- }
91
-
92
- private dispatchBlur(e: FocusEvent) {
93
- this.dispatchEvent(
94
- new CustomEvent('input-blur', {
95
- detail: e,
96
- bubbles: true,
97
- composed: true,
98
- }),
99
- )
100
- }
101
-
102
- private dispatchChange(e: Event) {
103
- this.dispatchEvent(
104
- new CustomEvent('input-changed', {
105
- detail: e,
106
- bubbles: true,
107
- composed: true,
108
- }),
109
- )
110
- }
17
+ strings: IDatepickerStrings = defaultSingleStrings
111
18
 
112
19
  private dispatchManageValidity(input: HTMLInputElement) {
113
20
  this.dispatchEvent(
@@ -119,10 +26,6 @@ export class PktDatepickerSingle extends PktElement {
119
26
  )
120
27
  }
121
28
 
122
- createRenderRoot() {
123
- return this
124
- }
125
-
126
29
  render() {
127
30
  return html`
128
31
  <div class="pkt-input__container">
@@ -181,18 +84,7 @@ export class PktDatepickerSingle extends PktElement {
181
84
  }}
182
85
  ${ref(this.inputRef)}
183
86
  />
184
- <button
185
- class="${classMap(cssUtils.getButtonClasses())}"
186
- type="button"
187
- @click=${(e: Event) => this.dispatchToggleCalendar(e)}
188
- @keydown=${(e: KeyboardEvent) =>
189
- keyboardUtils.handleButtonKeydown(e, (event) => this.dispatchToggleCalendar(event))}
190
- ?disabled=${this.disabled}
191
- ${ref(this.btnRef)}
192
- >
193
- <pkt-icon name="calendar"></pkt-icon>
194
- <span class="pkt-btn__text">${this.strings.calendar.buttonAltText}</span>
195
- </button>
87
+ ${this.renderCalendarButton()}
196
88
  </div>
197
89
  `
198
90
  }
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Type definitions for the datepicker component family
3
+ */
4
+
5
+ /**
6
+ * Internationalization strings for the datepicker components
7
+ */
8
+ export interface IDatepickerStrings {
9
+ /** Calendar-related strings */
10
+ calendar?: {
11
+ /** Alt text for calendar button */
12
+ buttonAltText?: string
13
+ }
14
+ /** Generic UI strings (used in range datepicker) */
15
+ generic?: {
16
+ /** Label for range start input */
17
+ from?: string
18
+ /** Label for range end input */
19
+ to?: string
20
+ }
21
+ /** Form validation messages */
22
+ forms?: {
23
+ messages?: {
24
+ /** Error message when date is before minimum */
25
+ rangeUnderflow?: string
26
+ /** Error message when date is after maximum */
27
+ rangeOverflow?: string
28
+ /** Error message for invalid date format */
29
+ badInput?: string
30
+ /** Error message when required field is empty */
31
+ valueMissing?: string
32
+ }
33
+ }
34
+ /** Date-related strings */
35
+ dates?: {
36
+ /** Month label */
37
+ month?: string
38
+ /** Year label */
39
+ year?: string
40
+ }
41
+ }
42
+
43
+ /**
44
+ * Default strings for single/multiple datepicker
45
+ */
46
+ export const defaultSingleStrings: IDatepickerStrings = {
47
+ calendar: { buttonAltText: 'Åpne kalender' },
48
+ }
49
+
50
+ /**
51
+ * Default strings for range datepicker
52
+ */
53
+ export const defaultRangeStrings: IDatepickerStrings = {
54
+ calendar: { buttonAltText: 'Åpne kalender' },
55
+ generic: { from: 'Fra', to: 'Til' },
56
+ }