@budibase/bbui 3.41.3 → 3.43.0
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/dist/bbui.mjs +1132 -41417
- package/package.json +3 -3
- package/src/Button/Button.svelte +10 -1
- package/src/Drawer/Drawer.svelte +2 -2
- package/src/FancyForm/FancyInput.svelte +1 -1
- package/src/Form/Core/DatePicker/DatePicker.svelte +14 -4
- package/src/Form/Core/DatePicker/DatePickerPopoverContents.svelte +26 -17
- package/src/Form/Core/DatePicker/NumberInput.svelte +11 -6
- package/src/Form/Core/DatePicker/TimePicker.svelte +8 -4
- package/src/Form/Core/DatePicker/utils.spec.ts +53 -0
- package/src/Form/Core/DatePicker/utils.ts +53 -55
- package/src/Form/Core/DateRangePicker.svelte +2 -2
- package/src/Form/Core/TextField.svelte +1 -1
- package/src/Form/DatePicker.svelte +2 -0
- package/src/Form/Dropzone.svelte +2 -0
- package/src/Form/TimeField.svelte +15 -5
- package/src/Modal/Modal.svelte +2 -2
- package/src/Switcher/Switcher.svelte +20 -5
- package/src/Tooltip/AbsTooltip.svelte +26 -1
- package/src/helpers.test.ts +57 -1
- package/src/helpers.ts +42 -5
- package/src/index.ts +1 -1
- package/src/styles.d.ts +2 -0
- package/src/types/events.ts +5 -0
- package/src/types/index.ts +1 -0
- package/src/utils/ids.test.ts +13 -0
- package/src/utils/ids.ts +5 -0
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": "
|
|
4
|
+
"version": "v3.43.0",
|
|
5
5
|
"license": "MPL-2.0",
|
|
6
6
|
"module": "dist/bbui.mjs",
|
|
7
7
|
"exports": {
|
|
@@ -75,9 +75,9 @@
|
|
|
75
75
|
"@spectrum-css/underlay": "2.0.9",
|
|
76
76
|
"@spectrum-css/vars": "3.0.1",
|
|
77
77
|
"atrament": "^4.3.0",
|
|
78
|
-
"date-fns": "^4.1.0",
|
|
79
78
|
"dayjs": "^1.10.8",
|
|
80
79
|
"easymde": "^2.16.1",
|
|
80
|
+
"nanoid": "6.0.1",
|
|
81
81
|
"sanitize-html": "^2.13.0",
|
|
82
82
|
"svelte-portal": "^2.2.1"
|
|
83
83
|
},
|
|
@@ -108,5 +108,5 @@
|
|
|
108
108
|
}
|
|
109
109
|
}
|
|
110
110
|
},
|
|
111
|
-
"gitHead": "
|
|
111
|
+
"gitHead": "221c199a898fffd9c8803d5327cc3089cc02b7f3"
|
|
112
112
|
}
|
package/src/Button/Button.svelte
CHANGED
|
@@ -23,6 +23,9 @@
|
|
|
23
23
|
export let active = false
|
|
24
24
|
export let tooltip: string | null = ""
|
|
25
25
|
export let tooltipPosition: TooltipPosition = TooltipPosition.Top
|
|
26
|
+
export let calculateTooltipWidth:
|
|
27
|
+
| ((target: Element) => number | undefined)
|
|
28
|
+
| undefined = undefined
|
|
26
29
|
export let newStyles = true
|
|
27
30
|
export let id: string | undefined = undefined
|
|
28
31
|
export let ref: HTMLButtonElement | undefined = undefined
|
|
@@ -32,10 +35,16 @@
|
|
|
32
35
|
$: tooltipText = tooltip ?? ""
|
|
33
36
|
</script>
|
|
34
37
|
|
|
35
|
-
<AbsTooltip
|
|
38
|
+
<AbsTooltip
|
|
39
|
+
text={tooltipText}
|
|
40
|
+
position={tooltipPosition}
|
|
41
|
+
disabledTarget={disabled && !!tooltipText}
|
|
42
|
+
calculateWidth={calculateTooltipWidth}
|
|
43
|
+
>
|
|
36
44
|
<button
|
|
37
45
|
{id}
|
|
38
46
|
{type}
|
|
47
|
+
{disabled}
|
|
39
48
|
bind:this={ref}
|
|
40
49
|
class:spectrum-Button--cta={cta}
|
|
41
50
|
class:spectrum-Button--primary={primary}
|
package/src/Drawer/Drawer.svelte
CHANGED
|
@@ -66,7 +66,6 @@
|
|
|
66
66
|
</script>
|
|
67
67
|
|
|
68
68
|
<script lang="ts">
|
|
69
|
-
import { generate } from "shortid"
|
|
70
69
|
import { createEventDispatcher, onDestroy, setContext } from "svelte"
|
|
71
70
|
import type { TransitionConfig } from "svelte/transition"
|
|
72
71
|
import Portal from "svelte-portal"
|
|
@@ -79,6 +78,7 @@
|
|
|
79
78
|
overlayStack,
|
|
80
79
|
BASE_Z_INDEX,
|
|
81
80
|
} from "../Modal/overlayStack"
|
|
81
|
+
import { generateId } from "../utils/ids"
|
|
82
82
|
|
|
83
83
|
interface DrawerEvents {
|
|
84
84
|
drawerShow: string
|
|
@@ -93,7 +93,7 @@
|
|
|
93
93
|
const spacing = 11
|
|
94
94
|
|
|
95
95
|
let visible: boolean = false
|
|
96
|
-
let drawerId: string =
|
|
96
|
+
let drawerId: string = generateId()
|
|
97
97
|
let depth: number = 0
|
|
98
98
|
let stackIndex: number = -1
|
|
99
99
|
let computedZIndex: number = BASE_Z_INDEX
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
export interface DatePickerApi {
|
|
3
|
+
open: () => void
|
|
4
|
+
close: () => void
|
|
5
|
+
}
|
|
6
|
+
</script>
|
|
7
|
+
|
|
1
8
|
<script lang="ts" generics="V">
|
|
2
9
|
import "@spectrum-css/calendar/dist/index-vars.css"
|
|
3
10
|
import "@spectrum-css/inputgroup/dist/index-vars.css"
|
|
@@ -12,18 +19,19 @@
|
|
|
12
19
|
import { getLocaleStartDayOfWeek, type Weekday } from "./utils"
|
|
13
20
|
import { resolveTranslationGroup } from "@budibase/shared-core"
|
|
14
21
|
|
|
15
|
-
export let id = null
|
|
22
|
+
export let id: string | null = null
|
|
16
23
|
export let disabled = false
|
|
17
24
|
export let readonly = false
|
|
18
|
-
export let error = null
|
|
25
|
+
export let error: string | false | null | undefined = null
|
|
19
26
|
export let enableTime = true
|
|
20
27
|
export let value: V | null = null
|
|
21
28
|
export let placeholder: string | null = null
|
|
22
29
|
export let timeOnly = false
|
|
30
|
+
export let setTimeTo: string | undefined = undefined
|
|
23
31
|
export let ignoreTimezones = false
|
|
24
32
|
export let useKeyboardShortcuts = true
|
|
25
|
-
export let appendTo = undefined
|
|
26
|
-
export let api = null
|
|
33
|
+
export let appendTo: string | undefined = undefined
|
|
34
|
+
export let api: DatePickerApi | null = null
|
|
27
35
|
export let align: PopoverAlignment = PopoverAlignment.Left
|
|
28
36
|
const browserStartDayOfWeek = getLocaleStartDayOfWeek()
|
|
29
37
|
export let startDayOfWeek: Weekday | undefined = undefined
|
|
@@ -37,6 +45,7 @@
|
|
|
37
45
|
|
|
38
46
|
$: parsedValue = parseDate(value as string | dayjs.Dayjs | null, {
|
|
39
47
|
enableTime,
|
|
48
|
+
setTimeTo,
|
|
40
49
|
})
|
|
41
50
|
|
|
42
51
|
const onOpen = () => {
|
|
@@ -88,6 +97,7 @@
|
|
|
88
97
|
{ignoreTimezones}
|
|
89
98
|
{enableTime}
|
|
90
99
|
{timeOnly}
|
|
100
|
+
{setTimeTo}
|
|
91
101
|
startDayOfWeek={resolvedStartDayOfWeek}
|
|
92
102
|
{calendarLabels}
|
|
93
103
|
value={parsedValue}
|
|
@@ -1,25 +1,29 @@
|
|
|
1
|
-
<script>
|
|
2
|
-
import dayjs from "dayjs"
|
|
3
|
-
import TimePicker from "./TimePicker.svelte"
|
|
4
|
-
import Calendar from "./Calendar.svelte"
|
|
5
|
-
import ActionButton from "../../../ActionButton/ActionButton.svelte"
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import dayjs, { type Dayjs } from "dayjs"
|
|
6
3
|
import { createEventDispatcher, onMount } from "svelte"
|
|
7
|
-
import { stringifyDate } from "../../../helpers"
|
|
8
4
|
import { resolveTranslationGroup } from "@budibase/shared-core"
|
|
5
|
+
import ActionButton from "../../../ActionButton/ActionButton.svelte"
|
|
6
|
+
import { stringifyDate } from "../../../helpers"
|
|
7
|
+
import Calendar from "./Calendar.svelte"
|
|
8
|
+
import TimePicker from "./TimePicker.svelte"
|
|
9
|
+
import { getLocaleStartDayOfWeek, type Weekday } from "./utils"
|
|
9
10
|
|
|
10
11
|
export let useKeyboardShortcuts = true
|
|
11
|
-
export let ignoreTimezones
|
|
12
|
-
export let enableTime
|
|
13
|
-
export let timeOnly
|
|
14
|
-
export let
|
|
15
|
-
export let
|
|
12
|
+
export let ignoreTimezones = false
|
|
13
|
+
export let enableTime = true
|
|
14
|
+
export let timeOnly = false
|
|
15
|
+
export let setTimeTo: string | undefined = undefined
|
|
16
|
+
export let value: Dayjs | null | undefined = null
|
|
17
|
+
const browserStartDayOfWeek = getLocaleStartDayOfWeek()
|
|
18
|
+
export let startDayOfWeek: Weekday | undefined = undefined
|
|
16
19
|
export let calendarLabels = resolveTranslationGroup("calendar")
|
|
17
20
|
|
|
18
|
-
const dispatch = createEventDispatcher()
|
|
19
|
-
let calendar
|
|
21
|
+
const dispatch = createEventDispatcher<{ change: string | null }>()
|
|
22
|
+
let calendar: { setDate: (date: Dayjs) => void } | undefined
|
|
20
23
|
|
|
21
24
|
$: showCalendar = !timeOnly
|
|
22
25
|
$: showTime = enableTime || timeOnly
|
|
26
|
+
$: resolvedStartDayOfWeek = startDayOfWeek ?? browserStartDayOfWeek
|
|
23
27
|
|
|
24
28
|
const setToNow = () => {
|
|
25
29
|
const now = dayjs().second(0).millisecond(0)
|
|
@@ -27,14 +31,19 @@
|
|
|
27
31
|
handleChange(now)
|
|
28
32
|
}
|
|
29
33
|
|
|
30
|
-
const handleChange = date => {
|
|
34
|
+
const handleChange = (date: Dayjs | null | undefined) => {
|
|
31
35
|
dispatch(
|
|
32
36
|
"change",
|
|
33
|
-
stringifyDate(date
|
|
37
|
+
stringifyDate(date ?? null, {
|
|
38
|
+
enableTime,
|
|
39
|
+
timeOnly,
|
|
40
|
+
ignoreTimezones,
|
|
41
|
+
setTimeTo,
|
|
42
|
+
})
|
|
34
43
|
)
|
|
35
44
|
}
|
|
36
45
|
|
|
37
|
-
const clearDateOnBackspace = event => {
|
|
46
|
+
const clearDateOnBackspace = (event: KeyboardEvent) => {
|
|
38
47
|
// Ignore if we're typing a value
|
|
39
48
|
if (document.activeElement?.tagName.toLowerCase() === "input") {
|
|
40
49
|
return
|
|
@@ -58,7 +67,7 @@
|
|
|
58
67
|
{#if showCalendar}
|
|
59
68
|
<Calendar
|
|
60
69
|
{value}
|
|
61
|
-
{
|
|
70
|
+
startDayOfWeek={resolvedStartDayOfWeek}
|
|
62
71
|
{calendarLabels}
|
|
63
72
|
on:change={e => handleChange(e.detail)}
|
|
64
73
|
bind:this={calendar}
|
|
@@ -1,16 +1,20 @@
|
|
|
1
|
-
<script>
|
|
2
|
-
export let value
|
|
3
|
-
export let min = undefined
|
|
4
|
-
export let max = undefined
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
export let value: string | number | undefined = undefined
|
|
3
|
+
export let min: string | number | undefined = undefined
|
|
4
|
+
export let max: string | number | undefined = undefined
|
|
5
5
|
export let hideArrows = false
|
|
6
|
-
export let width = undefined
|
|
6
|
+
export let width: number | undefined = undefined
|
|
7
7
|
export let type = "number"
|
|
8
8
|
export let disabled = false
|
|
9
9
|
export let readonly = false
|
|
10
|
+
export let step: string | number | undefined = undefined
|
|
10
11
|
|
|
11
12
|
$: style = width ? `width:${width}px;` : ""
|
|
12
13
|
|
|
13
|
-
const selectAll = event =>
|
|
14
|
+
const selectAll = (event: MouseEvent) => {
|
|
15
|
+
const input = event.currentTarget as HTMLInputElement
|
|
16
|
+
input.select()
|
|
17
|
+
}
|
|
14
18
|
</script>
|
|
15
19
|
|
|
16
20
|
<input
|
|
@@ -22,6 +26,7 @@
|
|
|
22
26
|
{max}
|
|
23
27
|
{disabled}
|
|
24
28
|
{readonly}
|
|
29
|
+
{step}
|
|
25
30
|
on:click={selectAll}
|
|
26
31
|
on:change
|
|
27
32
|
on:input
|
|
@@ -4,14 +4,15 @@
|
|
|
4
4
|
import NumberInput from "./NumberInput.svelte"
|
|
5
5
|
import { createEventDispatcher } from "svelte"
|
|
6
6
|
|
|
7
|
-
export let value: Dayjs | undefined
|
|
7
|
+
export let value: Dayjs | null | undefined
|
|
8
8
|
export let disableClearing = false
|
|
9
9
|
export let disabled = false
|
|
10
10
|
export let readonly = false
|
|
11
|
+
export let showSeconds = false
|
|
11
12
|
|
|
12
13
|
const dispatch = createEventDispatcher<{ change: Dayjs | undefined }>()
|
|
13
14
|
|
|
14
|
-
$: displayValue = value?.format("HH:mm")
|
|
15
|
+
$: displayValue = value?.format(showSeconds ? "HH:mm:ss" : "HH:mm")
|
|
15
16
|
|
|
16
17
|
const handleChange = async (e: Event) => {
|
|
17
18
|
if (disabled || readonly) {
|
|
@@ -28,10 +29,12 @@
|
|
|
28
29
|
return
|
|
29
30
|
}
|
|
30
31
|
|
|
31
|
-
const [hour, minute] = target.value
|
|
32
|
+
const [hour, minute, second = 0] = target.value
|
|
33
|
+
.split(":")
|
|
34
|
+
.map(x => parseInt(x))
|
|
32
35
|
dispatch(
|
|
33
36
|
"change",
|
|
34
|
-
(value || dayjs()).hour(hour).minute(minute).second(
|
|
37
|
+
(value || dayjs()).hour(hour).minute(minute).second(second).millisecond(0)
|
|
35
38
|
)
|
|
36
39
|
}
|
|
37
40
|
</script>
|
|
@@ -43,6 +46,7 @@
|
|
|
43
46
|
value={displayValue}
|
|
44
47
|
{disabled}
|
|
45
48
|
{readonly}
|
|
49
|
+
step={showSeconds ? 1 : undefined}
|
|
46
50
|
on:input={handleChange}
|
|
47
51
|
on:change={handleChange}
|
|
48
52
|
/>
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it, vi } from "vitest"
|
|
2
|
+
import { getLocaleStartDayOfWeek } from "./utils"
|
|
3
|
+
|
|
4
|
+
interface LocaleWithWeekInfo {
|
|
5
|
+
readonly weekInfo?: { firstDay?: number }
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
describe("getLocaleStartDayOfWeek", () => {
|
|
9
|
+
afterEach(() => {
|
|
10
|
+
vi.restoreAllMocks()
|
|
11
|
+
vi.unstubAllGlobals()
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
it("uses the locale's first day of the week", () => {
|
|
15
|
+
expect(getLocaleStartDayOfWeek(["en-US"])).toBe("Sunday")
|
|
16
|
+
expect(getLocaleStartDayOfWeek(["en-GB"])).toBe("Monday")
|
|
17
|
+
expect(getLocaleStartDayOfWeek(["ar-AF"])).toBe("Saturday")
|
|
18
|
+
expect(getLocaleStartDayOfWeek(["dv-MV"])).toBe("Friday")
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
it("normalizes locale separators", () => {
|
|
22
|
+
expect(getLocaleStartDayOfWeek(["en_US"])).toBe("Sunday")
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
it("uses the browser's preferred locales by default", () => {
|
|
26
|
+
vi.stubGlobal("navigator", {
|
|
27
|
+
language: "en-US",
|
|
28
|
+
languages: ["en-US"],
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
expect(getLocaleStartDayOfWeek()).toBe("Sunday")
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it("uses the next valid locale", () => {
|
|
35
|
+
expect(getLocaleStartDayOfWeek(["invalid--locale", "en-US"])).toBe("Sunday")
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
it("uses region data when locale week information is unavailable", () => {
|
|
39
|
+
const localePrototype: Intl.Locale & LocaleWithWeekInfo =
|
|
40
|
+
Intl.Locale.prototype
|
|
41
|
+
vi.spyOn(localePrototype, "weekInfo", "get").mockReturnValue(undefined)
|
|
42
|
+
|
|
43
|
+
expect(getLocaleStartDayOfWeek(["en-US"])).toBe("Sunday")
|
|
44
|
+
expect(getLocaleStartDayOfWeek(["ar-AF"])).toBe("Saturday")
|
|
45
|
+
expect(getLocaleStartDayOfWeek(["dv-MV"])).toBe("Friday")
|
|
46
|
+
expect(getLocaleStartDayOfWeek(["en-GB"])).toBe("Monday")
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
it("falls back to Monday without supported locale metadata", () => {
|
|
50
|
+
expect(getLocaleStartDayOfWeek([])).toBe("Monday")
|
|
51
|
+
expect(getLocaleStartDayOfWeek(["invalid--locale"])).toBe("Monday")
|
|
52
|
+
})
|
|
53
|
+
})
|
|
@@ -1,12 +1,18 @@
|
|
|
1
|
-
import type { Locale } from "date-fns"
|
|
2
|
-
import * as dateFnsLocales from "date-fns/locale"
|
|
3
|
-
|
|
4
1
|
interface Input {
|
|
5
2
|
max: number
|
|
6
3
|
pad: number
|
|
7
4
|
fallback: string
|
|
8
5
|
}
|
|
9
6
|
|
|
7
|
+
interface LocaleWeekInfo {
|
|
8
|
+
firstDay?: number
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
interface LocaleWithWeekInfo {
|
|
12
|
+
readonly weekInfo?: LocaleWeekInfo
|
|
13
|
+
getWeekInfo?: () => LocaleWeekInfo
|
|
14
|
+
}
|
|
15
|
+
|
|
10
16
|
export type Weekday =
|
|
11
17
|
| "Sunday"
|
|
12
18
|
| "Monday"
|
|
@@ -27,6 +33,19 @@ const WEEKDAY_BY_INDEX: Weekday[] = [
|
|
|
27
33
|
"Saturday",
|
|
28
34
|
]
|
|
29
35
|
|
|
36
|
+
// Fallback data for browsers without Intl.Locale week information support.
|
|
37
|
+
// Regions not listed use CLDR's global Monday default.
|
|
38
|
+
// Source: unicode-org/cldr-json/cldr-json/cldr-core/supplemental/weekData.json
|
|
39
|
+
const SUNDAY_START_REGIONS = new Set(
|
|
40
|
+
"AG AS BD BR BS BT BW BZ CA CO DM DO ET GT GU HK HN ID IL IN IS JM JP KE KH KR LA MH MM MO MT MX MZ NI NP PA PE PH PK PR PT SA SG SV TH TT TW UM US VE VI WS YE ZA ZW".split(
|
|
41
|
+
" "
|
|
42
|
+
)
|
|
43
|
+
)
|
|
44
|
+
const SATURDAY_START_REGIONS = new Set(
|
|
45
|
+
"AF BH DJ DZ EG IQ IR JO KW LY OM QA SD SY".split(" ")
|
|
46
|
+
)
|
|
47
|
+
const FRIDAY_START_REGIONS = new Set(["MV"])
|
|
48
|
+
|
|
30
49
|
const normalizeLocaleCode = (code?: string | null) => {
|
|
31
50
|
if (!code) {
|
|
32
51
|
return null
|
|
@@ -34,52 +53,6 @@ const normalizeLocaleCode = (code?: string | null) => {
|
|
|
34
53
|
return code.toLowerCase().replace(/_/g, "-")
|
|
35
54
|
}
|
|
36
55
|
|
|
37
|
-
const normalizeLocaleKey = (key?: string) => {
|
|
38
|
-
if (!key) {
|
|
39
|
-
return null
|
|
40
|
-
}
|
|
41
|
-
return key
|
|
42
|
-
.replace(/_/g, "-")
|
|
43
|
-
.replace(/([a-z])([A-Z])/g, "$1-$2")
|
|
44
|
-
.toLowerCase()
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const localeLookup = (() => {
|
|
48
|
-
const lookup = new Map<string, Locale>()
|
|
49
|
-
|
|
50
|
-
const register = (key: string, locale: Locale | undefined) => {
|
|
51
|
-
if (!locale) {
|
|
52
|
-
return
|
|
53
|
-
}
|
|
54
|
-
const codeCandidates = new Set<string>()
|
|
55
|
-
const normalizedKey = normalizeLocaleKey(key)
|
|
56
|
-
if (normalizedKey) {
|
|
57
|
-
codeCandidates.add(normalizedKey)
|
|
58
|
-
}
|
|
59
|
-
const normalizedLocaleCode = normalizeLocaleCode(locale.code)
|
|
60
|
-
if (normalizedLocaleCode) {
|
|
61
|
-
codeCandidates.add(normalizedLocaleCode)
|
|
62
|
-
}
|
|
63
|
-
for (const candidate of codeCandidates) {
|
|
64
|
-
if (!lookup.has(candidate)) {
|
|
65
|
-
lookup.set(candidate, locale)
|
|
66
|
-
}
|
|
67
|
-
const base = candidate.split("-")[0]
|
|
68
|
-
if (base && !lookup.has(base)) {
|
|
69
|
-
lookup.set(base, locale)
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
for (const [key, locale] of Object.entries(dateFnsLocales)) {
|
|
75
|
-
if (typeof locale === "object" && locale) {
|
|
76
|
-
register(key, locale as Locale)
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
return lookup
|
|
81
|
-
})()
|
|
82
|
-
|
|
83
56
|
const getNavigatorLocales = (): readonly string[] => {
|
|
84
57
|
if (typeof navigator === "undefined") {
|
|
85
58
|
return []
|
|
@@ -90,6 +63,34 @@ const getNavigatorLocales = (): readonly string[] => {
|
|
|
90
63
|
return navigator.language ? [navigator.language] : []
|
|
91
64
|
}
|
|
92
65
|
|
|
66
|
+
const getFallbackFirstDay = (locale: Intl.Locale): number => {
|
|
67
|
+
const region = locale.region ?? locale.maximize().region
|
|
68
|
+
if (region && SUNDAY_START_REGIONS.has(region)) {
|
|
69
|
+
return 7
|
|
70
|
+
}
|
|
71
|
+
if (region && SATURDAY_START_REGIONS.has(region)) {
|
|
72
|
+
return 6
|
|
73
|
+
}
|
|
74
|
+
if (region && FRIDAY_START_REGIONS.has(region)) {
|
|
75
|
+
return 5
|
|
76
|
+
}
|
|
77
|
+
return 1
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const getLocaleFirstDay = (code: string): number | undefined => {
|
|
81
|
+
if (typeof Intl.Locale !== "function") {
|
|
82
|
+
return undefined
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
try {
|
|
86
|
+
const locale = new Intl.Locale(code) as Intl.Locale & LocaleWithWeekInfo
|
|
87
|
+
const weekInfo = locale.weekInfo ?? locale.getWeekInfo?.()
|
|
88
|
+
return weekInfo?.firstDay ?? getFallbackFirstDay(locale)
|
|
89
|
+
} catch {
|
|
90
|
+
return undefined
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
93
94
|
export const getLocaleStartDayOfWeek = (
|
|
94
95
|
locales: readonly string[] = getNavigatorLocales()
|
|
95
96
|
): Weekday => {
|
|
@@ -98,12 +99,9 @@ export const getLocaleStartDayOfWeek = (
|
|
|
98
99
|
if (!normalized) {
|
|
99
100
|
continue
|
|
100
101
|
}
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
if (typeof weekStartsOn === "number") {
|
|
105
|
-
const index = ((weekStartsOn % 7) + 7) % 7
|
|
106
|
-
return WEEKDAY_BY_INDEX[index]
|
|
102
|
+
const firstDay = getLocaleFirstDay(normalized)
|
|
103
|
+
if (typeof firstDay === "number" && firstDay >= 1 && firstDay <= 7) {
|
|
104
|
+
return WEEKDAY_BY_INDEX[firstDay % 7]
|
|
107
105
|
}
|
|
108
106
|
}
|
|
109
107
|
return DEFAULT_WEEKDAY
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
const onChangeFrom = (utc: string) => {
|
|
42
|
+
const onChangeFrom = (utc: string | null) => {
|
|
43
43
|
// Preserve the time if its editable
|
|
44
44
|
const fromDate = utc
|
|
45
45
|
? enableTime
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
dispatch("change", [fromDate, toDate])
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
const onChangeTo = (utc: string) => {
|
|
58
|
+
const onChangeTo = (utc: string | null) => {
|
|
59
59
|
// Preserve the time if its editable
|
|
60
60
|
const toDate = utc
|
|
61
61
|
? enableTime
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import "@spectrum-css/textfield/dist/index-vars.css"
|
|
3
3
|
import { createEventDispatcher, onMount, tick } from "svelte"
|
|
4
4
|
import type { FullAutoFill } from "svelte/elements"
|
|
5
|
-
import type { UIEvent } from "
|
|
5
|
+
import type { UIEvent } from "../../types"
|
|
6
6
|
|
|
7
7
|
export let value: V | null = null
|
|
8
8
|
export let placeholder: string | undefined = undefined
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
export let error = undefined
|
|
14
14
|
export let enableTime = true
|
|
15
15
|
export let timeOnly = false
|
|
16
|
+
export let setTimeTo: string | undefined = undefined
|
|
16
17
|
export let placeholder: string | null = null
|
|
17
18
|
export let appendTo = undefined
|
|
18
19
|
export let ignoreTimezones = false
|
|
@@ -36,6 +37,7 @@
|
|
|
36
37
|
{placeholder}
|
|
37
38
|
{enableTime}
|
|
38
39
|
{timeOnly}
|
|
40
|
+
{setTimeTo}
|
|
39
41
|
{appendTo}
|
|
40
42
|
{ignoreTimezones}
|
|
41
43
|
{calendarLabels}
|
package/src/Form/Dropzone.svelte
CHANGED
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
undefined
|
|
23
23
|
export let gallery: boolean = true
|
|
24
24
|
export let fileTags: string[] = []
|
|
25
|
+
export let extensions: string = "*"
|
|
25
26
|
export let maximum: number | undefined = undefined
|
|
26
27
|
export let compact: boolean = false
|
|
27
28
|
export let helpText: string | undefined = undefined
|
|
@@ -44,6 +45,7 @@
|
|
|
44
45
|
{handleTooManyFiles}
|
|
45
46
|
{gallery}
|
|
46
47
|
{fileTags}
|
|
48
|
+
{extensions}
|
|
47
49
|
{maximum}
|
|
48
50
|
{compact}
|
|
49
51
|
on:change={onChange}
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
id?: string
|
|
17
17
|
name?: string
|
|
18
18
|
disableClearing?: boolean
|
|
19
|
+
showSeconds?: boolean
|
|
19
20
|
onchange?: (value: string | undefined) => void
|
|
20
21
|
}
|
|
21
22
|
</script>
|
|
@@ -39,33 +40,41 @@
|
|
|
39
40
|
id = undefined,
|
|
40
41
|
name = undefined,
|
|
41
42
|
disableClearing = false,
|
|
43
|
+
showSeconds = false,
|
|
42
44
|
onchange,
|
|
43
45
|
}: Props = $props()
|
|
44
46
|
|
|
45
|
-
const FALLBACK_TIME = "00:00"
|
|
47
|
+
const FALLBACK_TIME = $derived(showSeconds ? "00:00:00" : "00:00")
|
|
46
48
|
|
|
47
49
|
const parseValue = (time: TimeFieldValue) => {
|
|
48
50
|
if (!time) {
|
|
49
51
|
return undefined
|
|
50
52
|
}
|
|
51
53
|
|
|
52
|
-
const
|
|
54
|
+
const parts = time.split(":")
|
|
55
|
+
if (parts.length !== (showSeconds ? 3 : 2)) {
|
|
56
|
+
return undefined
|
|
57
|
+
}
|
|
58
|
+
const [hour, minute, second = 0] = parts.map(part => Number(part))
|
|
53
59
|
if (
|
|
54
60
|
!Number.isInteger(hour) ||
|
|
55
61
|
!Number.isInteger(minute) ||
|
|
62
|
+
!Number.isInteger(second) ||
|
|
56
63
|
hour < 0 ||
|
|
57
64
|
hour > 23 ||
|
|
58
65
|
minute < 0 ||
|
|
59
|
-
minute > 59
|
|
66
|
+
minute > 59 ||
|
|
67
|
+
second < 0 ||
|
|
68
|
+
second > 59
|
|
60
69
|
) {
|
|
61
70
|
return undefined
|
|
62
71
|
}
|
|
63
72
|
|
|
64
|
-
return dayjs().hour(hour).minute(minute).second(
|
|
73
|
+
return dayjs().hour(hour).minute(minute).second(second).millisecond(0)
|
|
65
74
|
}
|
|
66
75
|
|
|
67
76
|
const handleChange = (event: CustomEvent<Dayjs | undefined>) => {
|
|
68
|
-
const nextValue = event.detail?.format("HH:mm")
|
|
77
|
+
const nextValue = event.detail?.format(showSeconds ? "HH:mm:ss" : "HH:mm")
|
|
69
78
|
if (!nextValue && disableClearing) {
|
|
70
79
|
return
|
|
71
80
|
}
|
|
@@ -104,6 +113,7 @@
|
|
|
104
113
|
{disableClearing}
|
|
105
114
|
{disabled}
|
|
106
115
|
{readonly}
|
|
116
|
+
{showSeconds}
|
|
107
117
|
on:change={handleChange}
|
|
108
118
|
/>
|
|
109
119
|
{#if name}
|
package/src/Modal/Modal.svelte
CHANGED
|
@@ -21,7 +21,6 @@
|
|
|
21
21
|
import Portal from "svelte-portal"
|
|
22
22
|
import Context from "../context"
|
|
23
23
|
import { ModalCancelFrom } from "../constants"
|
|
24
|
-
import { generate } from "shortid"
|
|
25
24
|
import {
|
|
26
25
|
BASE_Z_INDEX,
|
|
27
26
|
overlayStack,
|
|
@@ -29,6 +28,7 @@
|
|
|
29
28
|
removeOverlay,
|
|
30
29
|
isActiveOverlay,
|
|
31
30
|
} from "./overlayStack"
|
|
31
|
+
import { generateId } from "../utils/ids"
|
|
32
32
|
|
|
33
33
|
export let fixed: boolean = false
|
|
34
34
|
export let inline: boolean = false
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
|
|
45
45
|
// Ensure any popovers inside this modal are rendered inside this modal
|
|
46
46
|
// Unique ids are required to ensure nested modals are parented correctly
|
|
47
|
-
const uniqueId =
|
|
47
|
+
const uniqueId = generateId()
|
|
48
48
|
const modalId = uniqueId
|
|
49
49
|
setContext(Context.PopoverRoot, `.spectrum-Modal-${uniqueId}`)
|
|
50
50
|
|