@oslokommune/punkt-elements 13.6.11 → 13.6.15

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 (40) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/dist/{combobox-cK_746ek.cjs → combobox-BFOjlFIj.cjs} +1 -1
  3. package/dist/{combobox-DxNotM0u.js → combobox-DaiEdUKx.js} +1 -1
  4. package/dist/datepicker-BR1imflE.cjs +289 -0
  5. package/dist/datepicker-CbVSKaOY.js +1390 -0
  6. package/dist/index.d.ts +125 -7
  7. package/dist/{input-wrapper-D_JdEqcO.js → input-wrapper-CQzXG44g.js} +22 -22
  8. package/dist/{input-wrapper-C9rZEgju.cjs → input-wrapper-DVjNwf8-.cjs} +11 -12
  9. package/dist/pkt-combobox.cjs +1 -1
  10. package/dist/pkt-combobox.js +1 -1
  11. package/dist/pkt-datepicker.cjs +1 -1
  12. package/dist/pkt-datepicker.js +8 -4
  13. package/dist/pkt-index.cjs +1 -1
  14. package/dist/pkt-index.js +6 -6
  15. package/dist/pkt-input-wrapper.cjs +1 -1
  16. package/dist/pkt-input-wrapper.js +1 -1
  17. package/dist/pkt-select.cjs +1 -1
  18. package/dist/pkt-select.js +1 -1
  19. package/dist/pkt-textarea.cjs +1 -1
  20. package/dist/pkt-textarea.js +1 -1
  21. package/dist/pkt-textinput.cjs +1 -1
  22. package/dist/pkt-textinput.js +1 -1
  23. package/dist/{select-D7OQaUrQ.js → select-DKkoxmgj.js} +1 -1
  24. package/dist/{select-Cf1RWSsI.cjs → select-DynzsPo0.cjs} +1 -1
  25. package/dist/{textarea-CXu8UUsY.cjs → textarea-BS1tgktz.cjs} +1 -1
  26. package/dist/{textarea-C0vTWTov.js → textarea-COG1CH_s.js} +1 -1
  27. package/dist/{textinput-C6wccDhZ.cjs → textinput-CCK8ti2y.cjs} +1 -1
  28. package/dist/{textinput-CmZrfH4A.js → textinput-CTOtfcTR.js} +1 -1
  29. package/package.json +3 -3
  30. package/src/components/datepicker/datepicker-multiple.ts +202 -0
  31. package/src/components/datepicker/datepicker-popup.test.ts +77 -0
  32. package/src/components/datepicker/datepicker-popup.ts +137 -0
  33. package/src/components/datepicker/datepicker-range.ts +281 -0
  34. package/src/components/datepicker/datepicker-single.ts +198 -0
  35. package/src/components/datepicker/datepicker-utils.ts +22 -9
  36. package/src/components/datepicker/datepicker.ts +179 -256
  37. package/src/components/datepicker/index.ts +5 -1
  38. package/src/components/input-wrapper/input-wrapper.ts +7 -7
  39. package/dist/datepicker-BEMo4X9s.js +0 -770
  40. package/dist/datepicker-n49TAIAt.cjs +0 -169
@@ -0,0 +1,198 @@
1
+ import { html } from 'lit'
2
+ import { customElement, property } from 'lit/decorators.js'
3
+ import { classMap } from 'lit/directives/class-map.js'
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, deviceDetection, cssUtils } from './datepicker-utils'
8
+ import '@/components/icon'
9
+
10
+ @customElement('pkt-datepicker-single')
11
+ export class PktDatepickerSingle extends PktElement {
12
+ @property({ type: String })
13
+ value: string = ''
14
+
15
+ @property({ type: String })
16
+ inputType: string = 'date'
17
+
18
+ @property({ type: String })
19
+ id: string = ''
20
+
21
+ @property({ type: String })
22
+ min?: string
23
+
24
+ @property({ type: String })
25
+ max?: string
26
+
27
+ @property({ type: String })
28
+ placeholder?: string
29
+
30
+ @property({ type: Boolean })
31
+ readonly: boolean = false
32
+
33
+ @property({ type: Boolean })
34
+ disabled: boolean = false
35
+
36
+ @property({ type: Object })
37
+ inputClasses: Record<string, boolean> = {}
38
+
39
+ @property({ type: Object })
40
+ internals?: any
41
+
42
+ @property({ type: Object })
43
+ strings: any = { calendar: { buttonAltText: 'Åpne kalender' } }
44
+
45
+ inputRef: Ref<HTMLInputElement> = createRef()
46
+ btnRef: Ref<HTMLButtonElement> = createRef()
47
+
48
+ get inputElement(): HTMLInputElement | undefined {
49
+ return this.inputRef.value
50
+ }
51
+
52
+ get buttonElement(): HTMLButtonElement | undefined {
53
+ return this.btnRef.value
54
+ }
55
+
56
+ get isInputReadonly(): boolean {
57
+ return this.readonly || this.inputType === 'text'
58
+ }
59
+
60
+ private dispatchToggleCalendar(e: Event) {
61
+ if (this.readonly) return
62
+
63
+ this.dispatchEvent(
64
+ new CustomEvent('toggle-calendar', {
65
+ detail: e,
66
+ bubbles: true,
67
+ composed: true,
68
+ }),
69
+ )
70
+ }
71
+
72
+ private dispatchInput(e: Event) {
73
+ this.dispatchEvent(
74
+ new CustomEvent('input-change', {
75
+ detail: e,
76
+ bubbles: true,
77
+ composed: true,
78
+ }),
79
+ )
80
+ }
81
+
82
+ private dispatchFocus() {
83
+ this.dispatchEvent(
84
+ new CustomEvent('input-focus', {
85
+ bubbles: true,
86
+ composed: true,
87
+ }),
88
+ )
89
+ }
90
+
91
+ private dispatchBlur(e: FocusEvent) {
92
+ this.dispatchEvent(
93
+ new CustomEvent('input-blur', {
94
+ detail: e,
95
+ bubbles: true,
96
+ composed: true,
97
+ }),
98
+ )
99
+ }
100
+
101
+ private dispatchChange(e: Event) {
102
+ this.dispatchEvent(
103
+ new CustomEvent('input-changed', {
104
+ detail: e,
105
+ bubbles: true,
106
+ composed: true,
107
+ }),
108
+ )
109
+ }
110
+
111
+ private dispatchManageValidity(input: HTMLInputElement) {
112
+ this.dispatchEvent(
113
+ new CustomEvent('manage-validity', {
114
+ detail: input,
115
+ bubbles: true,
116
+ composed: true,
117
+ }),
118
+ )
119
+ }
120
+
121
+ createRenderRoot() {
122
+ return this
123
+ }
124
+
125
+ render() {
126
+ return html`
127
+ <div class="pkt-input__container">
128
+ <input
129
+ class="${classMap(this.inputClasses)}"
130
+ .type=${this.inputType}
131
+ id="${this.id}-input"
132
+ .value=${this.value}
133
+ min=${ifDefined(this.min)}
134
+ max=${ifDefined(this.max)}
135
+ placeholder=${ifDefined(this.placeholder)}
136
+ ?readonly=${this.isInputReadonly}
137
+ aria-describedby="${this.id}-helptext"
138
+ @click=${(e: MouseEvent) => {
139
+ e.preventDefault()
140
+ this.dispatchToggleCalendar(e)
141
+ }}
142
+ @touchend=${(e: TouchEvent) => {
143
+ e.preventDefault()
144
+ this.dispatchToggleCalendar(e)
145
+ }}
146
+ ?disabled=${this.disabled}
147
+ @keydown=${(e: KeyboardEvent) =>
148
+ keyboardUtils.handleInputKeydown(
149
+ e,
150
+ (event) => this.dispatchToggleCalendar(event),
151
+ () =>
152
+ formUtils.submitFormOrFallback(this.internals, () => this.inputRef.value?.blur()),
153
+ undefined,
154
+ () => this.inputRef.value?.blur(),
155
+ )}
156
+ @input=${(e: Event) => {
157
+ this.dispatchInput(e)
158
+ e.stopImmediatePropagation()
159
+ }}
160
+ @focus=${() => {
161
+ this.dispatchFocus()
162
+ if (deviceDetection.isIOS()) {
163
+ this.dispatchToggleCalendar(new Event('focus'))
164
+ }
165
+ }}
166
+ @blur=${(e: FocusEvent) => {
167
+ this.dispatchBlur(e)
168
+ this.dispatchManageValidity(e.target as HTMLInputElement)
169
+ this.dispatchEvent(
170
+ new CustomEvent('value-change', {
171
+ detail: (e.target as HTMLInputElement).value,
172
+ bubbles: true,
173
+ composed: true,
174
+ }),
175
+ )
176
+ }}
177
+ @change=${(e: Event) => {
178
+ this.dispatchChange(e)
179
+ e.stopImmediatePropagation()
180
+ }}
181
+ ${ref(this.inputRef)}
182
+ />
183
+ <button
184
+ class="${classMap(cssUtils.getButtonClasses())}"
185
+ type="button"
186
+ @click=${(e: Event) => this.dispatchToggleCalendar(e)}
187
+ @keydown=${(e: KeyboardEvent) =>
188
+ keyboardUtils.handleButtonKeydown(e, (event) => this.dispatchToggleCalendar(event))}
189
+ ?disabled=${this.disabled}
190
+ ${ref(this.btnRef)}
191
+ >
192
+ <pkt-icon name="calendar"></pkt-icon>
193
+ <span class="pkt-btn__text">${this.strings.calendar.buttonAltText}</span>
194
+ </button>
195
+ </div>
196
+ `
197
+ }
198
+ }
@@ -24,18 +24,23 @@ export const sleep = (ms: number): Promise<void> =>
24
24
  export const deviceDetection = {
25
25
  /**
26
26
  * Detects if the current device is iOS (iPhone, iPad, iPod)
27
+ * Handles modern iPad Safari which uses desktop user agent since iOS 13
27
28
  */
28
29
  isIOS(): boolean {
29
30
  const ua = navigator.userAgent
30
- return /iP(hone|od|ad)/.test(ua)
31
- },
32
31
 
33
- /**
34
- * Detects if the current device is Mobile Safari
35
- */
36
- isMobileSafari(): boolean {
37
- const ua = navigator.userAgent
38
- return /iP(hone|od|ad)/.test(ua) && /Safari/.test(ua) && !/CriOS|FxiOS/.test(ua)
32
+ // Legacy iOS detection (iPhone, iPod, older iPads)
33
+ if (/iP(hone|od|ad)/.test(ua)) {
34
+ return true
35
+ }
36
+
37
+ // Modern iPad detection (iOS 13+ iPads identify as Mac)
38
+ // Check for Mac + touch support
39
+ if (/Macintosh/.test(ua) && 'ontouchend' in document) {
40
+ return true
41
+ }
42
+
43
+ return false
39
44
  },
40
45
  }
41
46
 
@@ -317,7 +322,14 @@ export const cssUtils = {
317
322
  /**
318
323
  * Generates input classes for datepicker
319
324
  */
320
- getInputClasses(fullwidth: boolean, showRangeLabels: boolean, multiple: boolean, range: boolean) {
325
+ getInputClasses(
326
+ fullwidth: boolean,
327
+ showRangeLabels: boolean,
328
+ multiple: boolean,
329
+ range: boolean,
330
+ readonly?: boolean,
331
+ inputType?: string
332
+ ) {
321
333
  return {
322
334
  'pkt-input': true,
323
335
  'pkt-datepicker__input': true,
@@ -325,6 +337,7 @@ export const cssUtils = {
325
337
  'pkt-datepicker--hasrangelabels': showRangeLabels,
326
338
  'pkt-datepicker--multiple': multiple,
327
339
  'pkt-datepicker--range': range,
340
+ 'ios-readonly-hack': readonly === false && inputType === 'text',
328
341
  }
329
342
  },
330
343