sveltacular 0.0.71 → 0.0.73

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.
@@ -1,23 +1,280 @@
1
- <script>import NumberBox from "../number-box/number-box.svelte";
1
+ <script>import { uniqueId } from "../../index.js";
2
+ import { createEventDispatcher } from "svelte";
3
+ import FormField from "../form-field.svelte";
4
+ import FormLabel from "../form-label.svelte";
5
+ const id = uniqueId();
6
+ const dipatch = createEventDispatcher();
2
7
  export let value;
3
- export let symbol = "$";
4
- export let allowCents = false;
8
+ export let prefix = "$";
9
+ export let suffix = "";
10
+ export let currency = "USD";
11
+ export let allowCents = true;
5
12
  export let placeholder = "";
6
13
  export let size = "full";
7
- export let step = 1;
14
+ export let units = "ones";
8
15
  export let min = 0;
9
- export let max = 1e6;
10
- export let isCents = false;
11
- let innerValue = isCents && value ? Math.round(value / 100) : value;
16
+ export let max = null;
17
+ const getDollarsFromValue = () => {
18
+ if (!value)
19
+ return "0";
20
+ if (isValueInCents)
21
+ return String(Math.abs(Math.floor(value / 100)));
22
+ return String(Math.abs(Math.floor(value)));
23
+ };
24
+ const getCentsFromValue = () => {
25
+ if (!value)
26
+ return "00";
27
+ if (isValueInCents)
28
+ return String(Math.abs(Math.round(value % 100))).padStart(2, "0");
29
+ return String(Math.abs(Math.round(value % 1 * 100))).padStart(2, "0");
30
+ };
31
+ let isValueInCents = units === "cents";
32
+ const fieldOrder = ["dollars", "cents"];
33
+ let dollars = getDollarsFromValue();
34
+ let cents = getCentsFromValue();
35
+ let lastState = [
36
+ { value: String(dollars), selectionStart: 0, selectionEnd: 0 },
37
+ { value: String(cents), selectionStart: 0, selectionEnd: 0 }
38
+ ];
12
39
  $:
13
- decimals = allowCents ? 2 : 0;
14
- const onChange = (e) => {
15
- const dollars = e.detail;
16
- if (dollars !== null)
17
- value = isCents ? Math.round(dollars * 100) : dollars;
40
+ if (value !== null && value >= 0) {
41
+ dollars = getDollarsFromValue();
42
+ cents = getCentsFromValue();
43
+ }
44
+ const getTargetProperties = (e) => {
45
+ const target = e.target;
46
+ const name = target.getAttribute("name");
47
+ const index = fieldOrder.indexOf(name ?? "") || 0;
48
+ const nextName = index < fieldOrder.length - 1 ? fieldOrder[index + 1] : null;
49
+ const prevName = index > 0 ? fieldOrder[index - 1] : null;
50
+ const selection = [target.selectionStart ?? 0, target.selectionEnd ?? 0];
51
+ const key = e instanceof KeyboardEvent ? e.key : "";
52
+ const isNumber = !isNaN(Number(key));
53
+ const isDecimal = key === ".";
54
+ const isBackspace = key === "Backspace";
55
+ const isAllowed = isNumber || isDecimal || ["Delete", "ArrowLeft", "ArrowRight", "Tab"].includes(key);
56
+ return {
57
+ element: target,
58
+ name,
59
+ index,
60
+ selectionStart: selection[0],
61
+ selectionEnd: selection[1],
62
+ isHighligted: selection[0] !== selection[1],
63
+ key,
64
+ isNumber,
65
+ isDecimal,
66
+ isBackspace,
67
+ isAllowed,
68
+ lastState: lastState[index],
69
+ value: target.value,
70
+ next: nextName ? document.getElementById(`${id}-${nextName}`) : null,
71
+ previous: prevName ? document.getElementById(`${id}-${prevName}`) : null
72
+ };
73
+ };
74
+ const focusAndHighlightText = (target) => {
75
+ target.focus();
76
+ target.setSelectionRange(0, target.value.length);
77
+ };
78
+ const updateLastState = (e) => {
79
+ const target = getTargetProperties(e);
80
+ lastState[target.index] = {
81
+ value: target.value,
82
+ selectionStart: target.selectionStart,
83
+ selectionEnd: target.selectionEnd
84
+ };
85
+ };
86
+ const isNumericString = (value2) => {
87
+ return !isNaN(Number(value2));
88
+ };
89
+ const moveExtraCentsToDollars = (centsValue, append = true) => {
90
+ if (centsValue.length > 2 && isNumericString(centsValue) && Number(centsValue) > 0) {
91
+ const whole = centsValue.substring(0, centsValue.length - 2);
92
+ const decimal = centsValue.substring(centsValue.length - 2);
93
+ dollars = append ? `${dollars}${whole}` : whole;
94
+ cents = decimal;
95
+ }
96
+ };
97
+ const onKeyPress = (e) => {
98
+ const target = getTargetProperties(e);
99
+ if (!target.isAllowed)
100
+ return e.preventDefault();
101
+ if (target.isDecimal) {
102
+ e.preventDefault();
103
+ if (target.next && allowCents)
104
+ focusAndHighlightText(target.next);
105
+ return;
106
+ }
107
+ ;
108
+ if (target.name === "cents" && target.value.length >= 2 && !target.isHighligted) {
109
+ if (target.isNumber)
110
+ moveExtraCentsToDollars(`${target.value}${e.key}`);
111
+ return e.preventDefault();
112
+ }
113
+ updateLastState(e);
114
+ };
115
+ const onKeyUp = (e) => {
116
+ const target = getTargetProperties(e);
117
+ if (target.key === "ArrowLeft" && !target.isHighligted && target.previous && target.lastState.selectionStart === 0) {
118
+ const preservedValue = String(target.previous.value);
119
+ focusAndHighlightText(target.previous);
120
+ target.previous.value = preservedValue;
121
+ return e.preventDefault();
122
+ }
123
+ if (target.key === "ArrowRight" && !target.isHighligted && target.next && target.lastState.selectionStart === target.value.length) {
124
+ focusAndHighlightText(target.next);
125
+ return e.preventDefault();
126
+ }
127
+ if (target.isBackspace) {
128
+ if (target.lastState.value.length === 0 && target.previous) {
129
+ target.previous.focus();
130
+ e.preventDefault();
131
+ }
132
+ }
133
+ updateLastState(e);
134
+ };
135
+ const onInput = (e) => {
136
+ const target = getTargetProperties(e);
137
+ if (!isNumericString(target.value)) {
138
+ target.element.value = target.lastState.value;
139
+ return e.preventDefault();
140
+ }
141
+ if (target.value.includes("-")) {
142
+ target.value = target.value.replace("-", "");
143
+ }
144
+ if (target.value.includes(".")) {
145
+ const parts = target.value.split(".");
146
+ dollars = parts[0];
147
+ cents = parts[1].padEnd(2, "0").substring(0, 2);
148
+ return e.preventDefault();
149
+ }
150
+ if (target.name === "cents" && target.value.length > 2) {
151
+ moveExtraCentsToDollars(target.value, false);
152
+ return e.preventDefault();
153
+ }
154
+ };
155
+ const onSaveStateEvent = (e) => {
156
+ if (dollars.length === 0)
157
+ dollars = "0";
158
+ if (cents.length === 0)
159
+ cents = "00";
160
+ updateLastState(e);
161
+ };
162
+ const onChange = () => {
163
+ let centValue = Math.abs(isNumericString(cents) ? Number(cents) : 0);
164
+ let dollarValue = Math.abs(isNumericString(dollars) ? Number(dollars) : 0);
165
+ if (isValueInCents)
166
+ value = dollarValue * 100 + centValue;
167
+ else
168
+ value = dollarValue + centValue / 100;
169
+ if (min && value < min)
170
+ value = min;
171
+ if (max && value > max)
172
+ value = max;
173
+ cents = String(centValue).padStart(2, "0");
18
174
  };
19
175
  </script>
20
176
 
21
- <NumberBox bind:value={innerValue} prefix={symbol} {decimals} {placeholder} {size} {min} {max} {step} on:change={onChange}>
22
- <slot />
23
- </NumberBox>
177
+ <FormField {size}>
178
+ {#if $$slots.default}
179
+ <FormLabel {id}><slot /></FormLabel>
180
+ {/if}
181
+ <div class="input {currency}" class:allowCents {id}>
182
+ {#if prefix}
183
+ <span class="prefix">{prefix}</span>
184
+ {/if}
185
+
186
+ <input
187
+ class="dollars"
188
+ {placeholder}
189
+ bind:value={dollars}
190
+ type="text"
191
+ on:keypress={onKeyPress}
192
+ on:keyup={onKeyUp}
193
+ on:input={onInput}
194
+ on:change={onChange}
195
+ on:mouseup={onSaveStateEvent}
196
+ on:focus={onSaveStateEvent}
197
+ on:blur={onSaveStateEvent}
198
+ name="dollars"
199
+ id="{id}-dollars"
200
+ inputmode="numeric"
201
+ />
202
+ {#if allowCents}
203
+ <span class="separator">.</span>
204
+ <input
205
+ {placeholder}
206
+ bind:value={cents}
207
+ type="text"
208
+ class="cents"
209
+ on:keypress={onKeyPress}
210
+ on:keyup={onKeyUp}
211
+ on:input={onInput}
212
+ on:change={onChange}
213
+ on:mouseup={onSaveStateEvent}
214
+ on:focus={onSaveStateEvent}
215
+ on:blur={onSaveStateEvent}
216
+ name="cents"
217
+ id="{id}-cents"
218
+ inputmode="numeric"
219
+ />
220
+ {/if}
221
+
222
+ {#if suffix}
223
+ <span class="suffix">{suffix}</span>
224
+ {/if}
225
+ </div>
226
+ </FormField>
227
+
228
+ <style>.input {
229
+ display: flex;
230
+ align-items: center;
231
+ justify-content: flex-start;
232
+ position: relative;
233
+ width: 100%;
234
+ height: 100%;
235
+ border-radius: 0.25rem;
236
+ border: 1px solid var(--form-input-border, black);
237
+ background-color: var(--form-input-bg, white);
238
+ color: var(--form-input-fg, black);
239
+ font-size: 1rem;
240
+ font-weight: 500;
241
+ line-height: 2rem;
242
+ transition: background-color 0.2s ease-in-out, border-color 0.2s ease-in-out, color 0.2s ease-in-out, fill 0.2s ease-in-out, stroke 0.2s ease-in-out;
243
+ user-select: none;
244
+ white-space: nowrap;
245
+ }
246
+ .input input {
247
+ background-color: transparent;
248
+ border: none;
249
+ line-height: 2rem;
250
+ font-size: 1rem;
251
+ padding-left: 1rem;
252
+ }
253
+ .input input:focus {
254
+ outline: none;
255
+ }
256
+ .input .dollars {
257
+ flex-grow: 1;
258
+ }
259
+ .input .separator {
260
+ width: 1rem;
261
+ text-align: center;
262
+ }
263
+ .input .cents {
264
+ width: 4rem;
265
+ }
266
+ .input .prefix,
267
+ .input .suffix {
268
+ font-size: 1rem;
269
+ line-height: 2rem;
270
+ padding-left: 1rem;
271
+ padding-right: 1rem;
272
+ background-color: var(--form-input-accent-bg, #ccc);
273
+ color: var(--form-input-accent-fg, black);
274
+ }
275
+ .input .prefix {
276
+ border-right: 1px solid var(--form-input-border, black);
277
+ }
278
+ .input .suffix {
279
+ border-left: 1px solid var(--form-input-border, black);
280
+ }</style>
@@ -1,18 +1,21 @@
1
1
  import { SvelteComponent } from "svelte";
2
- import type { FormFieldSizeOptions } from '../../index.js';
2
+ import { type FormFieldSizeOptions } from '../../index.js';
3
3
  declare const __propDef: {
4
4
  props: {
5
5
  value: number | null;
6
- symbol?: string | undefined;
6
+ prefix?: string | undefined;
7
+ suffix?: string | undefined;
8
+ currency?: string | undefined;
7
9
  allowCents?: boolean | undefined;
8
10
  placeholder?: string | undefined;
9
11
  size?: FormFieldSizeOptions | undefined;
10
- step?: number | undefined;
12
+ units?: "ones" | "cents" | undefined;
11
13
  min?: number | undefined;
12
- max?: number | undefined;
13
- isCents?: boolean | undefined;
14
+ max?: number | null | undefined;
14
15
  };
15
16
  events: {
17
+ change: CustomEvent<number | null>;
18
+ } & {
16
19
  [evt: string]: CustomEvent<any>;
17
20
  };
18
21
  slots: {
@@ -96,7 +96,6 @@ const keyUp = (event) => {
96
96
  return props.value;
97
97
  })();
98
98
  if (newValue.length >= props.maxLength) {
99
- console.log(event.key);
100
99
  props.target.value = newValue.slice(0, props.maxLength);
101
100
  if (props.nextInput)
102
101
  props.nextInput.focus();
@@ -2,7 +2,7 @@ import { SvelteComponent } from "svelte";
2
2
  declare const __propDef: {
3
3
  props: {
4
4
  value: string;
5
- type?: "sms" | "mobile" | "home" | "work" | "fax" | "other" | undefined;
5
+ type?: "home" | "mobile" | "work" | "sms" | "fax" | "other" | undefined;
6
6
  };
7
7
  events: {
8
8
  [evt: string]: CustomEvent<any>;
@@ -8,11 +8,16 @@ export declare const addMinutes: (minutes: number, fromDateTime?: string | Date)
8
8
  export declare const addHours: (hours: number, fromDateTime?: string | Date) => Date;
9
9
  export declare const addSeconds: (seconds: number, fromDateTime?: string | Date) => Date;
10
10
  export declare const addUnits: (value: number, units: DateUnit, fromDateTime?: string | Date) => Date;
11
- export declare const dateTimeFromSupabase: (dateTime: string | null) => string;
12
- export declare const dateTimeToSupabase: (dateTime: string) => string | null;
13
- export declare const dateTimeToHuman: (dateTime: string) => string;
14
- export declare const dateTimeToInput: (dateTime?: Date) => string;
11
+ export declare const dateTimeFromTz: (dateTime: string | null) => string;
12
+ export declare const dateTimeToTz: (dateTime: string) => string | null;
13
+ export declare const dateTimeToHuman: (dateTime: string, locale?: string) => string;
14
+ export declare const toDateTime: (date?: string | Date) => Date;
15
+ export declare const toDate: (date?: Date | string) => string;
16
+ export declare const dateTimeToInput: (dateTime?: Date | string) => string;
15
17
  export declare const isDateString: (date: string) => date is string;
16
18
  export declare const isDateTimeString: (dateTime: string) => dateTime is string;
17
19
  export declare const isDateOrDateTimeString: (dateOrDateTime: unknown) => dateOrDateTime is string;
18
20
  export declare const formatDateTime: (dateTime: string | Date) => string;
21
+ export declare const toHumanReadableDuration: (duration: number, singularUnits: string) => string;
22
+ export declare const diffDates: (start: string | Date, end: string | Date) => number;
23
+ export declare const isBetween: (date: string | Date, start: string | Date, end: string | Date) => boolean;
@@ -59,7 +59,7 @@ export const addUnits = (value, units, fromDateTime) => {
59
59
  return date;
60
60
  };
61
61
  // Convert datetime from YYYY-MM-DDTHH:MM+TZ format to YYYY-MM-DD HH:MM format
62
- export const dateTimeFromSupabase = (dateTime) => {
62
+ export const dateTimeFromTz = (dateTime) => {
63
63
  if (!dateTime)
64
64
  return '';
65
65
  const date = new Date(dateTime);
@@ -68,7 +68,7 @@ export const dateTimeFromSupabase = (dateTime) => {
68
68
  return date.toISOString().split('.')[0].slice(0, -3);
69
69
  };
70
70
  // Convert datetime from YYYY-MM-DD HH:MM format to YYYY-MM-DDTHH:MM+TZ format
71
- export const dateTimeToSupabase = (dateTime) => {
71
+ export const dateTimeToTz = (dateTime) => {
72
72
  if (!dateTime)
73
73
  return null;
74
74
  const date = new Date(dateTime);
@@ -79,23 +79,29 @@ export const dateTimeToSupabase = (dateTime) => {
79
79
  /*
80
80
  * Convert datetime to human-readable format with AM/PM, no seconds
81
81
  */
82
- export const dateTimeToHuman = (dateTime) => {
82
+ export const dateTimeToHuman = (dateTime, locale = 'en-US') => {
83
83
  const date = new Date(dateTime);
84
- const datePart = date.toLocaleString('en-US', {
84
+ const datePart = date.toLocaleString(locale, {
85
85
  month: 'long',
86
86
  day: 'numeric',
87
87
  year: 'numeric'
88
88
  });
89
- const timePart = date.toLocaleString('en-US', {
89
+ const timePart = date.toLocaleString(locale, {
90
90
  hour: 'numeric',
91
91
  minute: 'numeric',
92
92
  timeZoneName: 'short'
93
93
  });
94
94
  return `${datePart} @ ${timePart}`;
95
95
  };
96
+ export const toDateTime = (date) => {
97
+ return date ? new Date(date) : new Date();
98
+ };
99
+ export const toDate = (date) => {
100
+ return toDateTime(date).toISOString().split('T')[0];
101
+ };
96
102
  // From JavaScript date to YYYY-MM-DD HH:MM format
97
103
  export const dateTimeToInput = (dateTime) => {
98
- dateTime = dateTime || new Date();
104
+ dateTime = toDateTime(dateTime);
99
105
  const offset = dateTime.getTimezoneOffset();
100
106
  dateTime.setMinutes(dateTime.getMinutes() - offset);
101
107
  return dateTime.toISOString().split('.')[0].slice(0, -3);
@@ -119,3 +125,20 @@ export const formatDateTime = (dateTime) => {
119
125
  date.setMinutes(date.getMinutes() - offset);
120
126
  return date.toISOString().split('.')[0].slice(0, -3);
121
127
  };
128
+ export const toHumanReadableDuration = (duration, singularUnits) => {
129
+ return duration > 0 && duration <= 1
130
+ ? `${duration} ${singularUnits}`
131
+ : `${duration} ${singularUnits}s`;
132
+ };
133
+ export const diffDates = (start, end) => {
134
+ const startDate = new Date(start);
135
+ const endDate = new Date(end);
136
+ const duration = endDate.getTime() - startDate.getTime();
137
+ return duration;
138
+ };
139
+ export const isBetween = (date, start, end) => {
140
+ const dateToCheck = new Date(date);
141
+ const startDate = new Date(start);
142
+ const endDate = new Date(end);
143
+ return dateToCheck >= startDate && dateToCheck <= endDate;
144
+ };
@@ -0,0 +1 @@
1
+ export declare const removeProperties: (obj: unknown, props: string[]) => Record<string, unknown>;
@@ -0,0 +1,10 @@
1
+ // Remove properties from an object from an array of strings representing the property names
2
+ export const removeProperties = (obj, props) => {
3
+ if (!obj)
4
+ return {};
5
+ const newObj = { ...obj };
6
+ props.forEach((prop) => {
7
+ delete newObj[prop];
8
+ });
9
+ return newObj;
10
+ };
@@ -0,0 +1,4 @@
1
+ export type StripNulls<T> = {
2
+ [P in keyof T]: NonNullable<T[P]>;
3
+ };
4
+ export declare const stripNulls: <T extends object>(obj: T) => StripNulls<T>;
@@ -0,0 +1,5 @@
1
+ export const stripNulls = (obj) => {
2
+ if (!obj)
3
+ return {};
4
+ return obj;
5
+ };
package/dist/index.d.ts CHANGED
@@ -100,9 +100,11 @@ export * from './helpers/debounce.js';
100
100
  export * from './helpers/navigate-to.js';
101
101
  export * from './helpers/nobr.js';
102
102
  export * from './helpers/random.js';
103
+ export * from './helpers/remove-properties.js';
103
104
  export * from './helpers/round-to-decimals.js';
104
105
  export * from './helpers/split-new-lines.js';
105
106
  export * from './helpers/subscribable.js';
107
+ export * from './helpers/transform.js';
106
108
  export * from './helpers/ucfirst.js';
107
109
  export * from './helpers/unique-id.js';
108
110
  export * from './data/index.js';
package/dist/index.js CHANGED
@@ -111,9 +111,11 @@ export * from './helpers/debounce.js';
111
111
  export * from './helpers/navigate-to.js';
112
112
  export * from './helpers/nobr.js';
113
113
  export * from './helpers/random.js';
114
+ export * from './helpers/remove-properties.js';
114
115
  export * from './helpers/round-to-decimals.js';
115
116
  export * from './helpers/split-new-lines.js';
116
117
  export * from './helpers/subscribable.js';
118
+ export * from './helpers/transform.js';
117
119
  export * from './helpers/ucfirst.js';
118
120
  export * from './helpers/unique-id.js';
119
121
  // Data
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sveltacular",
3
- "version": "0.0.71",
3
+ "version": "0.0.73",
4
4
  "description": "A Svelte component library",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",