@scripso-homepad/ui 0.4.9 → 0.4.11

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 (48) hide show
  1. package/dist/index.cjs +245 -127
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.d.cts +4 -2
  4. package/dist/index.d.ts +4 -2
  5. package/dist/index.js +247 -129
  6. package/dist/index.js.map +1 -1
  7. package/dist/web/index.cjs +451 -48
  8. package/dist/web/index.cjs.map +1 -1
  9. package/dist/web/index.css +161 -0
  10. package/dist/web/index.css.map +1 -1
  11. package/dist/web/index.d.cts +50 -2
  12. package/dist/web/index.d.ts +50 -2
  13. package/dist/web/index.js +444 -49
  14. package/dist/web/index.js.map +1 -1
  15. package/package.json +10 -3
  16. package/src/components/Calendar.tsx +10 -2
  17. package/src/components/DatePicker.tsx +40 -6
  18. package/src/components/Select.tsx +133 -38
  19. package/src/theme/select.ts +1 -1
  20. package/src/utils/calendarDays.ts +15 -6
  21. package/src/web/components/Badge.tsx +1 -1
  22. package/src/web/components/ConfirmModal.tsx +2 -2
  23. package/src/web/components/Drawer.stories.tsx +234 -0
  24. package/src/web/components/Drawer.tsx +169 -0
  25. package/src/web/components/HistoryTimeline.tsx +46 -0
  26. package/src/web/components/Modal.stories.tsx +1 -1
  27. package/src/web/components/Modal.tsx +30 -13
  28. package/src/web/components/Pagination.tsx +2 -2
  29. package/src/web/components/TableActionButton.tsx +4 -4
  30. package/src/web/components/TableActionsMenu.tsx +1 -1
  31. package/src/web/components/TableIconText.tsx +1 -1
  32. package/src/web/components/Toaster.stories.tsx +106 -0
  33. package/src/web/components/Toaster.tsx +92 -0
  34. package/src/web/components/ToasterProvider.tsx +6 -0
  35. package/src/web/components/drawer-scroll.css +41 -0
  36. package/src/web/components/sonner-toast.css +141 -0
  37. package/src/web/components/table/constants.ts +2 -2
  38. package/src/web/icons/TimelineCheckIcon.tsx +24 -0
  39. package/src/web/icons/ToastIcons.tsx +119 -0
  40. package/src/web/index.ts +20 -0
  41. package/src/web/layout/AppHeader.tsx +1 -1
  42. package/src/web/layout/BuildingSelect.tsx +2 -2
  43. package/src/web/layout/DashboardLayout.stories.tsx +1 -1
  44. package/src/web/layout/SidebarNavItem.tsx +1 -1
  45. package/src/web/layout/SidebarUserCard.tsx +3 -3
  46. package/src/web/mutation-toast.ts +33 -0
  47. package/src/web/styles/typography.css +99 -0
  48. package/src/web/toast.ts +1 -0
package/dist/index.cjs CHANGED
@@ -871,7 +871,7 @@ var selectLabelTypography = {
871
871
  fontFamily: fonts.sans,
872
872
  fontSize: fontSize.sm,
873
873
  fontWeight: fontWeight.medium,
874
- lineHeight: fontSize.sm,
874
+ lineHeight: 16,
875
875
  letterSpacing: 0,
876
876
  color: colors.slateBlue
877
877
  };
@@ -1191,6 +1191,86 @@ function renderSelectIcon(icon, color) {
1191
1191
  }
1192
1192
  return icon;
1193
1193
  }
1194
+ function resolveWebTextNode(ref) {
1195
+ if (reactNative.Platform.OS !== "web") {
1196
+ return null;
1197
+ }
1198
+ const node = ref.current;
1199
+ return node instanceof HTMLElement ? node : null;
1200
+ }
1201
+ function syncTruncatedNativeTitle(ref, label) {
1202
+ const element = resolveWebTextNode(ref);
1203
+ if (!element) {
1204
+ return;
1205
+ }
1206
+ if (element.scrollWidth > element.clientWidth) {
1207
+ element.setAttribute("title", label);
1208
+ return;
1209
+ }
1210
+ element.removeAttribute("title");
1211
+ }
1212
+ function clearNativeTitle(ref) {
1213
+ resolveWebTextNode(ref)?.removeAttribute("title");
1214
+ }
1215
+ function SelectOptionItem({
1216
+ option,
1217
+ compact,
1218
+ optionHeight,
1219
+ isSelected,
1220
+ isHighlighted,
1221
+ isLast,
1222
+ selectableIndex,
1223
+ onHighlight,
1224
+ onSelect
1225
+ }) {
1226
+ const labelRef = react.useRef(null);
1227
+ return /* @__PURE__ */ jsxRuntime.jsx(
1228
+ reactNative.Pressable,
1229
+ {
1230
+ accessibilityRole: "button",
1231
+ accessibilityState: { disabled: option.disabled, selected: isSelected },
1232
+ disabled: option.disabled,
1233
+ onPress: () => {
1234
+ if (!option.disabled) {
1235
+ onSelect(option.value);
1236
+ }
1237
+ },
1238
+ onHoverIn: () => {
1239
+ if (!option.disabled && selectableIndex >= 0) {
1240
+ onHighlight(selectableIndex);
1241
+ }
1242
+ syncTruncatedNativeTitle(labelRef, option.label);
1243
+ },
1244
+ onHoverOut: () => {
1245
+ clearNativeTitle(labelRef);
1246
+ },
1247
+ style: [
1248
+ selectDropdownMetrics.option,
1249
+ compact && styles4.optionCompact,
1250
+ { height: optionHeight, minHeight: optionHeight, maxHeight: optionHeight },
1251
+ isSelected && selectDropdownMetrics.optionSelected,
1252
+ compact && isSelected && styles4.optionSelectedCompact,
1253
+ isHighlighted && !isSelected && !option.disabled ? selectDropdownMetrics.optionHighlighted : null,
1254
+ !isLast && styles4.optionSpacing,
1255
+ option.disabled && styles4.optionDisabled
1256
+ ],
1257
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1258
+ reactNative.Text,
1259
+ {
1260
+ ref: labelRef,
1261
+ style: [
1262
+ selectDropdownMetrics.optionLabel,
1263
+ styles4.optionLabel,
1264
+ isSelected && selectDropdownMetrics.optionLabelSelected,
1265
+ option.disabled && styles4.optionLabelDisabled
1266
+ ],
1267
+ numberOfLines: 1,
1268
+ children: option.label
1269
+ }
1270
+ )
1271
+ }
1272
+ );
1273
+ }
1194
1274
  var useIsomorphicLayoutEffect = typeof window === "undefined" ? react.useEffect : react.useLayoutEffect;
1195
1275
  function normalizeSelectedValues(multiple, value) {
1196
1276
  if (multiple) {
@@ -1323,6 +1403,11 @@ function Select({
1323
1403
  onValueChange?.(nextValues);
1324
1404
  return;
1325
1405
  }
1406
+ if (selectedValues.includes(nextValue)) {
1407
+ onValueChange?.("");
1408
+ closeMenu();
1409
+ return;
1410
+ }
1326
1411
  onValueChange?.(nextValue);
1327
1412
  closeMenu();
1328
1413
  }
@@ -1332,43 +1417,17 @@ function Select({
1332
1417
  const isHighlighted = selectableIndex === highlightedIndex;
1333
1418
  const isLast = index === options.length - 1;
1334
1419
  return /* @__PURE__ */ jsxRuntime.jsx(
1335
- reactNative.Pressable,
1420
+ SelectOptionItem,
1336
1421
  {
1337
- accessibilityRole: "button",
1338
- accessibilityState: { disabled: option.disabled, selected: isSelected },
1339
- disabled: option.disabled,
1340
- onPress: () => {
1341
- if (!option.disabled) {
1342
- handleSelect(option.value);
1343
- }
1344
- },
1345
- onHoverIn: () => {
1346
- if (!option.disabled && selectableIndex >= 0) {
1347
- setHighlightedIndex(selectableIndex);
1348
- }
1349
- },
1350
- style: [
1351
- selectDropdownMetrics.option,
1352
- compact && styles4.optionCompact,
1353
- { height: optionHeight, minHeight: optionHeight, maxHeight: optionHeight },
1354
- isSelected && selectDropdownMetrics.optionSelected,
1355
- compact && isSelected && styles4.optionSelectedCompact,
1356
- isHighlighted && !isSelected && !option.disabled ? selectDropdownMetrics.optionHighlighted : null,
1357
- !isLast && styles4.optionSpacing,
1358
- option.disabled && styles4.optionDisabled
1359
- ],
1360
- children: /* @__PURE__ */ jsxRuntime.jsx(
1361
- reactNative.Text,
1362
- {
1363
- style: [
1364
- selectDropdownMetrics.optionLabel,
1365
- isSelected && selectDropdownMetrics.optionLabelSelected,
1366
- option.disabled && styles4.optionLabelDisabled
1367
- ],
1368
- numberOfLines: 1,
1369
- children: option.label
1370
- }
1371
- )
1422
+ option,
1423
+ compact,
1424
+ optionHeight,
1425
+ isSelected,
1426
+ isHighlighted,
1427
+ isLast,
1428
+ selectableIndex,
1429
+ onHighlight: setHighlightedIndex,
1430
+ onSelect: handleSelect
1372
1431
  },
1373
1432
  option.value
1374
1433
  );
@@ -1503,6 +1562,14 @@ var styles4 = reactNative.StyleSheet.create({
1503
1562
  optionDisabled: {
1504
1563
  opacity: 0.5
1505
1564
  },
1565
+ optionLabel: {
1566
+ minWidth: 0,
1567
+ ...reactNative.Platform.OS === "web" ? {
1568
+ overflow: "hidden",
1569
+ textOverflow: "ellipsis",
1570
+ whiteSpace: "nowrap"
1571
+ } : null
1572
+ },
1506
1573
  optionLabelDisabled: {
1507
1574
  color: colors.stormGray300
1508
1575
  },
@@ -2331,17 +2398,24 @@ function isInDateRange(date, start, end) {
2331
2398
  }
2332
2399
  function selectRangeDate(clicked, current) {
2333
2400
  const clickedDate = stripTime(clicked);
2334
- if (!current?.start || current.end) {
2401
+ if (!current?.start) {
2335
2402
  return { start: clickedDate };
2336
2403
  }
2337
2404
  const start = stripTime(current.start);
2338
- if (isSameDay(clickedDate, start)) {
2405
+ const end = current.end ? stripTime(current.end) : void 0;
2406
+ if (!end) {
2407
+ if (isSameDay(clickedDate, start)) {
2408
+ return void 0;
2409
+ }
2410
+ if (isBeforeDay(clickedDate, start)) {
2411
+ return { start: clickedDate };
2412
+ }
2339
2413
  return { start, end: clickedDate };
2340
2414
  }
2341
- if (isBeforeDay(clickedDate, start)) {
2342
- return { start: clickedDate };
2415
+ if (isSameDay(clickedDate, end)) {
2416
+ return void 0;
2343
2417
  }
2344
- return { start, end: clickedDate };
2418
+ return { start: clickedDate };
2345
2419
  }
2346
2420
  function isSameDay(left, right) {
2347
2421
  return left.getFullYear() === right.getFullYear() && left.getMonth() === right.getMonth() && left.getDate() === right.getDate();
@@ -2833,6 +2907,11 @@ function Calendar(props) {
2833
2907
  rangeProps.onChange?.(selectRangeDate(date, rangeProps.value));
2834
2908
  return;
2835
2909
  }
2910
+ const currentValue = singleProps?.value;
2911
+ if (currentValue && isSameDay(date, currentValue)) {
2912
+ singleProps?.onChange?.(void 0);
2913
+ return;
2914
+ }
2836
2915
  singleProps?.onChange?.(date);
2837
2916
  }
2838
2917
  const headerIsPressable = pickerView === "days" || pickerView === "months";
@@ -2990,6 +3069,8 @@ function DatePicker(props) {
2990
3069
  containerStyle,
2991
3070
  className,
2992
3071
  fieldClassName,
3072
+ fieldStyle,
3073
+ fieldOutlineStyle,
2993
3074
  panelClassName,
2994
3075
  labelClassName,
2995
3076
  errorClassName,
@@ -3079,100 +3160,124 @@ function DatePicker(props) {
3079
3160
  return;
3080
3161
  }
3081
3162
  props.onChange?.(range);
3082
- if (closeOnRangeComplete && range.end) {
3163
+ if (closeOnRangeComplete && range?.end) {
3083
3164
  closePicker();
3084
3165
  }
3085
3166
  }
3086
- return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { ref: wrapperRef, style: [styles7.wrapper, containerStyle], children: [
3087
- label ? /* @__PURE__ */ jsxRuntime.jsx(Label, { disabled, style: styles7.label, className: labelClassName, children: label }) : null,
3088
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [fieldStyles.outline, styles7.fieldOutline], children: /* @__PURE__ */ jsxRuntime.jsxs(
3089
- reactNative.Pressable,
3090
- {
3091
- ref: triggerRef,
3092
- accessibilityRole: "button",
3093
- accessibilityState: { disabled, expanded: open },
3094
- disabled,
3095
- onPress: openPicker,
3096
- style: [calendarFieldMetrics.container, fieldStyles.container],
3097
- children: [
3167
+ const isFluidField = Boolean(fieldStyle) || Boolean(fieldOutlineStyle);
3168
+ return /* @__PURE__ */ jsxRuntime.jsxs(
3169
+ reactNative.View,
3170
+ {
3171
+ ref: wrapperRef,
3172
+ style: [styles7.wrapper, isFluidField && styles7.wrapperFluid, containerStyle],
3173
+ children: [
3174
+ label ? /* @__PURE__ */ jsxRuntime.jsx(Label, { disabled, style: styles7.label, className: labelClassName, children: label }) : null,
3175
+ /* @__PURE__ */ jsxRuntime.jsx(
3176
+ reactNative.View,
3177
+ {
3178
+ style: [
3179
+ fieldStyles.outline,
3180
+ styles7.fieldOutline,
3181
+ isFluidField && styles7.fieldOutlineFluid,
3182
+ fieldOutlineStyle
3183
+ ],
3184
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
3185
+ reactNative.Pressable,
3186
+ {
3187
+ ref: triggerRef,
3188
+ accessibilityRole: "button",
3189
+ accessibilityState: { disabled, expanded: open },
3190
+ disabled,
3191
+ onPress: openPicker,
3192
+ style: [
3193
+ calendarFieldMetrics.container,
3194
+ isFluidField && styles7.fieldContainerFluid,
3195
+ fieldStyles.container,
3196
+ fieldStyle
3197
+ ],
3198
+ children: [
3199
+ /* @__PURE__ */ jsxRuntime.jsx(
3200
+ reactNative.Text,
3201
+ {
3202
+ style: [calendarFieldMetrics.value, valueTextStyle],
3203
+ numberOfLines: 1,
3204
+ children: displayValue
3205
+ }
3206
+ ),
3207
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles7.iconSlot, children: /* @__PURE__ */ jsxRuntime.jsx(
3208
+ CalendarIcon,
3209
+ {
3210
+ size: CALENDAR_FIELD_ICON_SIZE,
3211
+ color: disabled ? colors.stormGray200 : colors.stormGray150
3212
+ }
3213
+ ) })
3214
+ ]
3215
+ }
3216
+ )
3217
+ }
3218
+ ),
3219
+ helperMessage ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { ref: helperRef, style: error ? styles7.error : styles7.hint, children: helperMessage }) : null,
3220
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Modal, { visible: open, transparent: true, animationType: "fade", onRequestClose: closePicker, children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles7.modalRoot, children: [
3098
3221
  /* @__PURE__ */ jsxRuntime.jsx(
3099
- reactNative.Text,
3222
+ reactNative.Pressable,
3100
3223
  {
3101
- style: [calendarFieldMetrics.value, valueTextStyle],
3102
- numberOfLines: 1,
3103
- children: displayValue
3224
+ style: styles7.backdrop,
3225
+ onPress: closePicker,
3226
+ accessibilityRole: "button",
3227
+ accessibilityLabel: labels?.closeCalendar ?? translations.closeCalendar
3104
3228
  }
3105
3229
  ),
3106
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles7.iconSlot, children: /* @__PURE__ */ jsxRuntime.jsx(
3107
- CalendarIcon,
3108
- {
3109
- size: CALENDAR_FIELD_ICON_SIZE,
3110
- color: disabled ? colors.stormGray200 : colors.stormGray150
3111
- }
3112
- ) })
3113
- ]
3114
- }
3115
- ) }),
3116
- helperMessage ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { ref: helperRef, style: error ? styles7.error : styles7.hint, children: helperMessage }) : null,
3117
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Modal, { visible: open, transparent: true, animationType: "fade", onRequestClose: closePicker, children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles7.modalRoot, children: [
3118
- /* @__PURE__ */ jsxRuntime.jsx(
3119
- reactNative.Pressable,
3120
- {
3121
- style: styles7.backdrop,
3122
- onPress: closePicker,
3123
- accessibilityRole: "button",
3124
- accessibilityLabel: labels?.closeCalendar ?? translations.closeCalendar
3125
- }
3126
- ),
3127
- anchor ? /* @__PURE__ */ jsxRuntime.jsx(
3128
- reactNative.View,
3129
- {
3130
- style: [
3131
- styles7.panelPosition,
3132
- {
3133
- top: dropdownTop,
3134
- left: panelLeft,
3135
- width: panelWidth,
3136
- height: CALENDAR_PANEL_HEIGHT
3137
- }
3138
- ],
3139
- children: props.mode === "range" ? /* @__PURE__ */ jsxRuntime.jsx(
3140
- Calendar,
3141
- {
3142
- mode: "range",
3143
- value: props.value,
3144
- onChange: handleRangeSelect,
3145
- viewDate,
3146
- defaultViewDate: resolvedDefaultViewDate,
3147
- onViewDateChange,
3148
- locale: resolvedLocale,
3149
- labels,
3150
- minDate,
3151
- maxDate,
3152
- today,
3153
- className: panelClassName
3154
- }
3155
- ) : /* @__PURE__ */ jsxRuntime.jsx(
3156
- Calendar,
3230
+ anchor ? /* @__PURE__ */ jsxRuntime.jsx(
3231
+ reactNative.View,
3157
3232
  {
3158
- mode: "single",
3159
- value: props.value,
3160
- onChange: handleSingleSelect,
3161
- viewDate,
3162
- defaultViewDate: resolvedDefaultViewDate,
3163
- onViewDateChange,
3164
- locale: resolvedLocale,
3165
- labels,
3166
- minDate,
3167
- maxDate,
3168
- today,
3169
- className: panelClassName
3233
+ style: [
3234
+ styles7.panelPosition,
3235
+ {
3236
+ top: dropdownTop,
3237
+ left: panelLeft,
3238
+ width: panelWidth,
3239
+ height: CALENDAR_PANEL_HEIGHT
3240
+ }
3241
+ ],
3242
+ children: props.mode === "range" ? /* @__PURE__ */ jsxRuntime.jsx(
3243
+ Calendar,
3244
+ {
3245
+ mode: "range",
3246
+ value: props.value,
3247
+ onChange: handleRangeSelect,
3248
+ viewDate,
3249
+ defaultViewDate: resolvedDefaultViewDate,
3250
+ onViewDateChange,
3251
+ locale: resolvedLocale,
3252
+ labels,
3253
+ minDate,
3254
+ maxDate,
3255
+ today,
3256
+ className: panelClassName
3257
+ }
3258
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
3259
+ Calendar,
3260
+ {
3261
+ mode: "single",
3262
+ value: props.value,
3263
+ onChange: handleSingleSelect,
3264
+ viewDate,
3265
+ defaultViewDate: resolvedDefaultViewDate,
3266
+ onViewDateChange,
3267
+ locale: resolvedLocale,
3268
+ labels,
3269
+ minDate,
3270
+ maxDate,
3271
+ today,
3272
+ className: panelClassName
3273
+ }
3274
+ )
3170
3275
  }
3171
- )
3172
- }
3173
- ) : null
3174
- ] }) })
3175
- ] });
3276
+ ) : null
3277
+ ] }) })
3278
+ ]
3279
+ }
3280
+ );
3176
3281
  }
3177
3282
  var styles7 = reactNative.StyleSheet.create({
3178
3283
  wrapper: {
@@ -3180,11 +3285,24 @@ var styles7 = reactNative.StyleSheet.create({
3180
3285
  maxWidth: "100%",
3181
3286
  gap: SELECT_LABEL_GAP
3182
3287
  },
3288
+ wrapperFluid: {
3289
+ width: "100%"
3290
+ },
3183
3291
  fieldOutline: {
3184
3292
  width: CALENDAR_FIELD_WIDTH,
3185
3293
  maxWidth: "100%",
3186
3294
  alignSelf: "flex-start"
3187
3295
  },
3296
+ fieldOutlineFluid: {
3297
+ width: "100%",
3298
+ alignSelf: "stretch"
3299
+ },
3300
+ fieldContainerFluid: {
3301
+ width: "100%",
3302
+ minWidth: 0,
3303
+ maxWidth: "100%",
3304
+ alignSelf: "stretch"
3305
+ },
3188
3306
  label: {
3189
3307
  ...calendarLabelTypography
3190
3308
  },