@budibase/bbui 2.23.12 → 2.24.1

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.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@budibase/bbui",
3
3
  "description": "A UI solution used in the different Budibase projects.",
4
- "version": "2.23.12",
4
+ "version": "2.24.1",
5
5
  "license": "MPL-2.0",
6
6
  "svelte": "src/index.js",
7
7
  "module": "dist/bbui.es.js",
@@ -35,14 +35,15 @@
35
35
  ],
36
36
  "dependencies": {
37
37
  "@adobe/spectrum-css-workflow-icons": "1.2.1",
38
- "@budibase/shared-core": "2.23.12",
39
- "@budibase/string-templates": "2.23.12",
38
+ "@budibase/shared-core": "2.24.1",
39
+ "@budibase/string-templates": "2.24.1",
40
40
  "@spectrum-css/accordion": "3.0.24",
41
41
  "@spectrum-css/actionbutton": "1.0.1",
42
42
  "@spectrum-css/actiongroup": "1.0.1",
43
43
  "@spectrum-css/avatar": "3.0.2",
44
44
  "@spectrum-css/button": "3.0.1",
45
45
  "@spectrum-css/buttongroup": "3.0.2",
46
+ "@spectrum-css/calendar": "3.2.7",
46
47
  "@spectrum-css/checkbox": "3.0.2",
47
48
  "@spectrum-css/dialog": "3.0.1",
48
49
  "@spectrum-css/divider": "1.0.3",
@@ -82,7 +83,6 @@
82
83
  "dayjs": "^1.10.8",
83
84
  "easymde": "^2.16.1",
84
85
  "svelte-dnd-action": "^0.9.8",
85
- "svelte-flatpickr": "3.2.3",
86
86
  "svelte-portal": "^1.0.0"
87
87
  },
88
88
  "resolutions": {
@@ -103,5 +103,5 @@
103
103
  }
104
104
  }
105
105
  },
106
- "gitHead": "35dcb2465a0a116c4a6169a01b6154990747627a"
106
+ "gitHead": "5cb6ac027c7f45e174e3545a7399d425822970d8"
107
107
  }
@@ -38,7 +38,15 @@
38
38
  <div use:getAnchor on:click={openMenu}>
39
39
  <slot name="control" />
40
40
  </div>
41
- <Popover bind:this={dropdown} {anchor} {align} {portalTarget} on:open on:close>
41
+ <Popover
42
+ bind:this={dropdown}
43
+ {anchor}
44
+ {align}
45
+ {portalTarget}
46
+ resizable={false}
47
+ on:open
48
+ on:close
49
+ >
42
50
  <Menu>
43
51
  <slot />
44
52
  </Menu>
@@ -1,7 +1,12 @@
1
1
  const ignoredClasses = [
2
- ".flatpickr-calendar",
3
- ".spectrum-Popover",
4
2
  ".download-js-link",
3
+ ".spectrum-Menu",
4
+ ".date-time-popover",
5
+ ]
6
+ const conditionallyIgnoredClasses = [
7
+ ".spectrum-Underlay",
8
+ ".drawer-wrapper",
9
+ ".spectrum-Popover",
5
10
  ]
6
11
  let clickHandlers = []
7
12
 
@@ -9,6 +14,9 @@ let clickHandlers = []
9
14
  * Handle a body click event
10
15
  */
11
16
  const handleClick = event => {
17
+ // Treat right clicks (context menu events) as normal clicks
18
+ const eventType = event.type === "contextmenu" ? "click" : event.type
19
+
12
20
  // Ignore click if this is an ignored class
13
21
  if (event.target.closest('[data-ignore-click-outside="true"]')) {
14
22
  return
@@ -21,26 +29,23 @@ const handleClick = event => {
21
29
 
22
30
  // Process handlers
23
31
  clickHandlers.forEach(handler => {
24
- if (handler.element.contains(event.target)) {
32
+ // Check that we're the right kind of click event
33
+ if (handler.allowedType && eventType !== handler.allowedType) {
25
34
  return
26
35
  }
27
36
 
28
- // Ignore clicks for modals, unless the handler is registered from a modal
29
- const sourceInModal = handler.anchor.closest(".spectrum-Underlay") != null
30
- const clickInModal = event.target.closest(".spectrum-Underlay") != null
31
- if (clickInModal && !sourceInModal) {
32
- return
33
- }
34
-
35
- // Ignore clicks for drawers, unless the handler is registered from a drawer
36
- const sourceInDrawer = handler.anchor.closest(".drawer-wrapper") != null
37
- const clickInDrawer = event.target.closest(".drawer-wrapper") != null
38
- if (clickInDrawer && !sourceInDrawer) {
37
+ // Check that the click isn't inside the target
38
+ if (handler.element.contains(event.target)) {
39
39
  return
40
40
  }
41
41
 
42
- if (handler.allowedType && event.type !== handler.allowedType) {
43
- return
42
+ // Ignore clicks for certain classes unless we're nested inside them
43
+ for (let className of conditionallyIgnoredClasses) {
44
+ const sourceInside = handler.anchor.closest(className) != null
45
+ const clickInside = event.target.closest(className) != null
46
+ if (clickInside && !sourceInside) {
47
+ return
48
+ }
44
49
  }
45
50
 
46
51
  handler.callback?.(event)
@@ -48,6 +53,7 @@ const handleClick = event => {
48
53
  }
49
54
  document.documentElement.addEventListener("click", handleClick, true)
50
55
  document.documentElement.addEventListener("mousedown", handleClick, true)
56
+ document.documentElement.addEventListener("contextmenu", handleClick, true)
51
57
 
52
58
  /**
53
59
  * Adds or updates a click handler
@@ -1,3 +1,22 @@
1
+ /**
2
+ * Valid alignment options are
3
+ * - left
4
+ * - right
5
+ * - left-outside
6
+ * - right-outside
7
+ **/
8
+
9
+ // Strategies are defined as [Popover]To[Anchor].
10
+ // They can apply for both horizontal and vertical alignment.
11
+ const Strategies = {
12
+ StartToStart: "StartToStart", // e.g. left alignment
13
+ EndToEnd: "EndToEnd", // e.g. right alignment
14
+ StartToEnd: "StartToEnd", // e.g. right-outside alignment
15
+ EndToStart: "EndToStart", // e.g. left-outside alignment
16
+ MidPoint: "MidPoint", // centers relative to midpoints
17
+ ScreenEdge: "ScreenEdge", // locks to screen edge
18
+ }
19
+
1
20
  export default function positionDropdown(element, opts) {
2
21
  let resizeObserver
3
22
  let latestOpts = opts
@@ -19,6 +38,8 @@ export default function positionDropdown(element, opts) {
19
38
  useAnchorWidth,
20
39
  offset = 5,
21
40
  customUpdate,
41
+ resizable,
42
+ wrap,
22
43
  } = opts
23
44
  if (!anchor) {
24
45
  return
@@ -27,56 +48,159 @@ export default function positionDropdown(element, opts) {
27
48
  // Compute bounds
28
49
  const anchorBounds = anchor.getBoundingClientRect()
29
50
  const elementBounds = element.getBoundingClientRect()
51
+ const winWidth = window.innerWidth
52
+ const winHeight = window.innerHeight
53
+ const screenOffset = 8
30
54
  let styles = {
31
- maxHeight: null,
32
- minWidth,
33
- maxWidth,
55
+ maxHeight,
56
+ minWidth: useAnchorWidth ? anchorBounds.width : minWidth,
57
+ maxWidth: useAnchorWidth ? anchorBounds.width : maxWidth,
34
58
  left: null,
35
59
  top: null,
36
60
  }
37
61
 
62
+ // Ignore all our logic for custom logic
38
63
  if (typeof customUpdate === "function") {
39
64
  styles = customUpdate(anchorBounds, elementBounds, {
40
65
  ...styles,
41
66
  offset: opts.offset,
42
67
  })
43
- } else {
44
- // Determine vertical styles
45
- if (align === "right-outside" || align === "left-outside") {
46
- styles.top =
47
- anchorBounds.top + anchorBounds.height / 2 - elementBounds.height / 2
48
- styles.maxHeight = maxHeight
49
- if (styles.top + elementBounds.height > window.innerHeight) {
50
- styles.top = window.innerHeight - elementBounds.height
68
+ }
69
+
70
+ // Otherwise position ourselves as normal
71
+ else {
72
+ // Checks if we overflow off the screen. We only report that we overflow
73
+ // when the alternative dimension is larger than the one we are checking.
74
+ const doesXOverflow = () => {
75
+ const overflows = styles.left + elementBounds.width > winWidth
76
+ return overflows && anchorBounds.left > winWidth - anchorBounds.right
77
+ }
78
+ const doesYOverflow = () => {
79
+ const overflows = styles.top + elementBounds.height > winHeight
80
+ return overflows && anchorBounds.top > winHeight - anchorBounds.bottom
81
+ }
82
+
83
+ // Applies a dynamic max height constraint if appropriate
84
+ const applyMaxHeight = height => {
85
+ if (!styles.maxHeight && resizable) {
86
+ styles.maxHeight = height
51
87
  }
52
- } else if (
53
- window.innerHeight - anchorBounds.bottom <
54
- (maxHeight || 100)
55
- ) {
56
- styles.top = anchorBounds.top - elementBounds.height - offset
57
- styles.maxHeight = maxHeight || 240
58
- } else {
59
- styles.top = anchorBounds.bottom + offset
60
- styles.maxHeight =
61
- maxHeight || window.innerHeight - anchorBounds.bottom - 20
62
88
  }
63
89
 
64
- // Determine horizontal styles
65
- if (!maxWidth && useAnchorWidth) {
66
- styles.maxWidth = anchorBounds.width
90
+ // Applies the X strategy to our styles
91
+ const applyXStrategy = strategy => {
92
+ switch (strategy) {
93
+ case Strategies.StartToStart:
94
+ default:
95
+ styles.left = anchorBounds.left
96
+ break
97
+ case Strategies.EndToEnd:
98
+ styles.left = anchorBounds.right - elementBounds.width
99
+ break
100
+ case Strategies.StartToEnd:
101
+ styles.left = anchorBounds.right + offset
102
+ break
103
+ case Strategies.EndToStart:
104
+ styles.left = anchorBounds.left - elementBounds.width - offset
105
+ break
106
+ case Strategies.MidPoint:
107
+ styles.left =
108
+ anchorBounds.left +
109
+ anchorBounds.width / 2 -
110
+ elementBounds.width / 2
111
+ break
112
+ case Strategies.ScreenEdge:
113
+ styles.left = winWidth - elementBounds.width - screenOffset
114
+ break
115
+ }
67
116
  }
68
- if (useAnchorWidth) {
69
- styles.minWidth = anchorBounds.width
117
+
118
+ // Applies the Y strategy to our styles
119
+ const applyYStrategy = strategy => {
120
+ switch (strategy) {
121
+ case Strategies.StartToStart:
122
+ styles.top = anchorBounds.top
123
+ applyMaxHeight(winHeight - anchorBounds.top - screenOffset)
124
+ break
125
+ case Strategies.EndToEnd:
126
+ styles.top = anchorBounds.bottom - elementBounds.height
127
+ applyMaxHeight(anchorBounds.bottom - screenOffset)
128
+ break
129
+ case Strategies.StartToEnd:
130
+ default:
131
+ styles.top = anchorBounds.bottom + offset
132
+ applyMaxHeight(winHeight - anchorBounds.bottom - screenOffset)
133
+ break
134
+ case Strategies.EndToStart:
135
+ styles.top = anchorBounds.top - elementBounds.height - offset
136
+ applyMaxHeight(anchorBounds.top - screenOffset)
137
+ break
138
+ case Strategies.MidPoint:
139
+ styles.top =
140
+ anchorBounds.top +
141
+ anchorBounds.height / 2 -
142
+ elementBounds.height / 2
143
+ break
144
+ case Strategies.ScreenEdge:
145
+ styles.top = winHeight - elementBounds.height - screenOffset
146
+ applyMaxHeight(winHeight - 2 * screenOffset)
147
+ break
148
+ }
70
149
  }
150
+
151
+ // Determine X strategy
71
152
  if (align === "right") {
72
- styles.left =
73
- anchorBounds.left + anchorBounds.width - elementBounds.width
153
+ applyXStrategy(Strategies.EndToEnd)
74
154
  } else if (align === "right-outside") {
75
- styles.left = anchorBounds.right + offset
155
+ applyXStrategy(Strategies.StartToEnd)
76
156
  } else if (align === "left-outside") {
77
- styles.left = anchorBounds.left - elementBounds.width - offset
157
+ applyXStrategy(Strategies.EndToStart)
158
+ } else {
159
+ applyXStrategy(Strategies.StartToStart)
160
+ }
161
+
162
+ // Determine Y strategy
163
+ if (align === "right-outside" || align === "left-outside") {
164
+ applyYStrategy(Strategies.MidPoint)
78
165
  } else {
79
- styles.left = anchorBounds.left
166
+ applyYStrategy(Strategies.StartToEnd)
167
+ }
168
+
169
+ // Handle screen overflow
170
+ if (doesXOverflow()) {
171
+ // Swap left to right
172
+ if (align === "left") {
173
+ applyXStrategy(Strategies.EndToEnd)
174
+ }
175
+ // Swap right-outside to left-outside
176
+ else if (align === "right-outside") {
177
+ applyXStrategy(Strategies.EndToStart)
178
+ }
179
+ }
180
+ if (doesYOverflow()) {
181
+ // If wrapping, lock to the bottom of the screen and also reposition to
182
+ // the side to not block the anchor
183
+ if (wrap) {
184
+ applyYStrategy(Strategies.MidPoint)
185
+ if (doesYOverflow()) {
186
+ applyYStrategy(Strategies.ScreenEdge)
187
+ }
188
+ applyXStrategy(Strategies.StartToEnd)
189
+ if (doesXOverflow()) {
190
+ applyXStrategy(Strategies.EndToStart)
191
+ }
192
+ }
193
+ // Othewise invert as normal
194
+ else {
195
+ // If using an outside strategy then lock to the bottom of the screen
196
+ if (align === "left-outside" || align === "right-outside") {
197
+ applyYStrategy(Strategies.ScreenEdge)
198
+ }
199
+ // Otherwise flip above
200
+ else {
201
+ applyYStrategy(Strategies.EndToStart)
202
+ }
203
+ }
80
204
  }
81
205
  }
82
206
 
@@ -0,0 +1,252 @@
1
+ <script>
2
+ import { cleanInput } from "./utils"
3
+ import Select from "../../Select.svelte"
4
+ import dayjs from "dayjs"
5
+ import NumberInput from "./NumberInput.svelte"
6
+ import { createEventDispatcher } from "svelte"
7
+ import isoWeek from "dayjs/plugin/isoWeek"
8
+
9
+ dayjs.extend(isoWeek)
10
+
11
+ export let value
12
+
13
+ const dispatch = createEventDispatcher()
14
+ const DaysOfWeek = [
15
+ "Monday",
16
+ "Tuesday",
17
+ "Wednesday",
18
+ "Thursday",
19
+ "Friday",
20
+ "Saturday",
21
+ "Sunday",
22
+ ]
23
+ const MonthsOfYear = [
24
+ "January",
25
+ "February",
26
+ "March",
27
+ "April",
28
+ "May",
29
+ "June",
30
+ "July",
31
+ "August",
32
+ "September",
33
+ "October",
34
+ "November",
35
+ "December",
36
+ ]
37
+
38
+ const now = dayjs()
39
+ let calendarDate
40
+
41
+ $: calendarDate = dayjs(value || dayjs()).startOf("month")
42
+ $: mondays = getMondays(calendarDate)
43
+
44
+ const getMondays = monthStart => {
45
+ if (!monthStart?.isValid()) {
46
+ return []
47
+ }
48
+ let monthEnd = monthStart.endOf("month")
49
+ let calendarStart = monthStart.startOf("isoWeek")
50
+ const numWeeks = Math.ceil((monthEnd.diff(calendarStart, "day") + 1) / 7)
51
+
52
+ let mondays = []
53
+ for (let i = 0; i < numWeeks; i++) {
54
+ mondays.push(calendarStart.add(i, "weeks"))
55
+ }
56
+ return mondays
57
+ }
58
+
59
+ const handleCalendarYearChange = e => {
60
+ calendarDate = calendarDate.year(parseInt(e.target.value))
61
+ }
62
+
63
+ const handleDateChange = date => {
64
+ const base = value || now
65
+ dispatch(
66
+ "change",
67
+ base.year(date.year()).month(date.month()).date(date.date())
68
+ )
69
+ }
70
+
71
+ export const setDate = date => {
72
+ calendarDate = date
73
+ }
74
+
75
+ const cleanYear = cleanInput({ max: 9999, pad: 0, fallback: now.year() })
76
+ </script>
77
+
78
+ <div class="spectrum-Calendar">
79
+ <div class="spectrum-Calendar-header">
80
+ <div
81
+ class="spectrum-Calendar-title"
82
+ aria-live="assertive"
83
+ aria-atomic="true"
84
+ >
85
+ <div class="month-selector">
86
+ <Select
87
+ autoWidth
88
+ placeholder={null}
89
+ options={MonthsOfYear.map((m, idx) => ({ label: m, value: idx }))}
90
+ value={calendarDate.month()}
91
+ on:change={e => (calendarDate = calendarDate.month(e.detail))}
92
+ />
93
+ </div>
94
+ <NumberInput
95
+ value={calendarDate.year()}
96
+ min={0}
97
+ max={9999}
98
+ width={64}
99
+ on:change={handleCalendarYearChange}
100
+ on:input={cleanYear}
101
+ />
102
+ </div>
103
+ <button
104
+ aria-label="Previous"
105
+ title="Previous"
106
+ class="spectrum-ActionButton spectrum-ActionButton--quiet spectrum-Calendar-prevMonth"
107
+ on:click={() => (calendarDate = calendarDate.subtract(1, "month"))}
108
+ >
109
+ <svg
110
+ class="spectrum-Icon spectrum-UIIcon-ChevronLeft100"
111
+ focusable="false"
112
+ aria-hidden="true"
113
+ >
114
+ <use xlink:href="#spectrum-css-icon-Chevron100" />
115
+ </svg>
116
+ </button>
117
+ <button
118
+ aria-label="Next"
119
+ title="Next"
120
+ class="spectrum-ActionButton spectrum-ActionButton--quiet spectrum-Calendar-nextMonth"
121
+ on:click={() => (calendarDate = calendarDate.add(1, "month"))}
122
+ >
123
+ <svg
124
+ class="spectrum-Icon spectrum-UIIcon-ChevronRight100"
125
+ focusable="false"
126
+ aria-hidden="true"
127
+ >
128
+ <use xlink:href="#spectrum-css-icon-Chevron100" />
129
+ </svg>
130
+ </button>
131
+ </div>
132
+ <div
133
+ class="spectrum-Calendar-body"
134
+ aria-readonly="true"
135
+ aria-disabled="false"
136
+ >
137
+ <table role="presentation" class="spectrum-Calendar-table">
138
+ <thead role="presentation">
139
+ <tr>
140
+ {#each DaysOfWeek as day}
141
+ <th scope="col" class="spectrum-Calendar-tableCell">
142
+ <abbr class="spectrum-Calendar-dayOfWeek" title={day}>
143
+ {day[0]}
144
+ </abbr>
145
+ </th>
146
+ {/each}
147
+ </tr>
148
+ </thead>
149
+ <tbody role="presentation">
150
+ {#each mondays as monday}
151
+ <tr>
152
+ {#each [0, 1, 2, 3, 4, 5, 6] as dayOffset}
153
+ {@const date = monday.add(dayOffset, "days")}
154
+ {@const outsideMonth = date.month() !== calendarDate.month()}
155
+ <td
156
+ class="spectrum-Calendar-tableCell"
157
+ aria-disabled="true"
158
+ aria-selected="false"
159
+ aria-invalid="false"
160
+ title={date.format("dddd, MMMM D, YYYY")}
161
+ on:click={() => handleDateChange(date)}
162
+ >
163
+ <span
164
+ role="presentation"
165
+ class="spectrum-Calendar-date"
166
+ class:is-outsideMonth={outsideMonth}
167
+ class:is-today={!outsideMonth && date.isSame(now, "day")}
168
+ class:is-selected={date.isSame(value, "day")}
169
+ >
170
+ {date.date()}
171
+ </span>
172
+ </td>
173
+ {/each}
174
+ </tr>
175
+ {/each}
176
+ </tbody>
177
+ </table>
178
+ </div>
179
+ </div>
180
+
181
+ <style>
182
+ /* Calendar overrides */
183
+ .spectrum-Calendar {
184
+ width: auto;
185
+ }
186
+ .spectrum-Calendar-header {
187
+ width: auto;
188
+ }
189
+ .spectrum-Calendar-title {
190
+ display: flex;
191
+ justify-content: flex-start;
192
+ align-items: stretch;
193
+ flex: 1 1 auto;
194
+ }
195
+ .spectrum-Calendar-header button {
196
+ border-radius: 4px;
197
+ }
198
+ .spectrum-Calendar-date.is-outsideMonth {
199
+ visibility: visible;
200
+ color: var(--spectrum-global-color-gray-400);
201
+ }
202
+ .spectrum-Calendar-date.is-today,
203
+ .spectrum-Calendar-date.is-today::before {
204
+ border-color: var(--spectrum-global-color-gray-400);
205
+ }
206
+ .spectrum-Calendar-date.is-today.is-selected,
207
+ .spectrum-Calendar-date.is-today.is-selected::before {
208
+ border-color: var(
209
+ --primaryColorHover,
210
+ var(--spectrum-global-color-blue-700)
211
+ );
212
+ }
213
+ .spectrum-Calendar-date.is-selected:not(.is-range-selection) {
214
+ background: var(--primaryColor, var(--spectrum-global-color-blue-400));
215
+ }
216
+ .spectrum-Calendar tr {
217
+ box-sizing: content-box;
218
+ height: 40px;
219
+ }
220
+ .spectrum-Calendar-tableCell {
221
+ box-sizing: content-box;
222
+ }
223
+ .spectrum-Calendar-nextMonth,
224
+ .spectrum-Calendar-prevMonth {
225
+ order: 1;
226
+ padding: 4px;
227
+ }
228
+ .spectrum-Calendar-date {
229
+ color: var(--spectrum-alias-text-color);
230
+ }
231
+ .spectrum-Calendar-date.is-selected {
232
+ color: white;
233
+ }
234
+ .spectrum-Calendar-dayOfWeek {
235
+ color: var(--spectrum-global-color-gray-600);
236
+ }
237
+
238
+ /* Style select */
239
+ .month-selector :global(.spectrum-Picker) {
240
+ background: none;
241
+ border: none;
242
+ padding: 4px 6px;
243
+ }
244
+ .month-selector :global(.spectrum-Picker:hover),
245
+ .month-selector :global(.spectrum-Picker.is-open) {
246
+ background: var(--spectrum-global-color-gray-200);
247
+ }
248
+ .month-selector :global(.spectrum-Picker-label) {
249
+ font-size: 18px;
250
+ font-weight: bold;
251
+ }
252
+ </style>
@@ -0,0 +1,94 @@
1
+ <script>
2
+ import Icon from "../../../Icon/Icon.svelte"
3
+ import { getDateDisplayValue } from "../../../helpers"
4
+
5
+ export let anchor
6
+ export let disabled
7
+ export let readonly
8
+ export let error
9
+ export let focused
10
+ export let placeholder
11
+ export let id
12
+ export let value
13
+ export let icon
14
+ export let enableTime
15
+ export let timeOnly
16
+
17
+ $: displayValue = getDateDisplayValue(value, { enableTime, timeOnly })
18
+ </script>
19
+
20
+ <!-- svelte-ignore a11y-no-static-element-interactions -->
21
+ <!-- svelte-ignore a11y-click-events-have-key-events -->
22
+ <div
23
+ bind:this={anchor}
24
+ class:is-disabled={disabled || readonly}
25
+ class:is-invalid={!!error}
26
+ class:is-focused={focused}
27
+ class="spectrum-InputGroup spectrum-Datepicker"
28
+ aria-readonly={readonly}
29
+ aria-required="false"
30
+ aria-haspopup="true"
31
+ on:click
32
+ >
33
+ <div
34
+ class="spectrum-Textfield spectrum-InputGroup-textfield"
35
+ class:is-disabled={disabled}
36
+ class:is-invalid={!!error}
37
+ >
38
+ {#if !!error}
39
+ <svg
40
+ class="spectrum-Icon spectrum-Icon--sizeM spectrum-Textfield-validationIcon"
41
+ focusable="false"
42
+ aria-hidden="true"
43
+ >
44
+ <use xlink:href="#spectrum-icon-18-Alert" />
45
+ </svg>
46
+ {/if}
47
+ <input
48
+ {disabled}
49
+ {readonly}
50
+ data-input
51
+ type="text"
52
+ class="spectrum-Textfield-input spectrum-InputGroup-input"
53
+ class:is-disabled={disabled}
54
+ {placeholder}
55
+ {id}
56
+ value={displayValue}
57
+ />
58
+ </div>
59
+ {#if !disabled && !readonly}
60
+ <button
61
+ type="button"
62
+ class="spectrum-Picker spectrum-Picker--sizeM spectrum-InputGroup-button"
63
+ tabindex="-1"
64
+ class:is-invalid={!!error}
65
+ >
66
+ <Icon name={icon} />
67
+ </button>
68
+ {/if}
69
+ </div>
70
+
71
+ <style>
72
+ /* Date label overrides */
73
+ .spectrum-Textfield-input {
74
+ pointer-events: none;
75
+ }
76
+ .spectrum-Textfield:not(.is-disabled):hover {
77
+ cursor: pointer;
78
+ }
79
+ .spectrum-Datepicker {
80
+ width: 100%;
81
+ overflow: hidden;
82
+ }
83
+ .spectrum-Datepicker .spectrum-Textfield {
84
+ width: 100%;
85
+ }
86
+ .is-disabled {
87
+ pointer-events: none !important;
88
+ }
89
+ input:read-only {
90
+ border-right-width: 1px;
91
+ border-top-right-radius: var(--spectrum-textfield-border-radius);
92
+ border-bottom-right-radius: var(--spectrum-textfield-border-radius);
93
+ }
94
+ </style>