@apptimate/ui 2.3.0 → 2.4.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/package.json +1 -1
- package/src/base-components/DateLabel.tsx +6 -2
- package/src/base-components/Dropdown.tsx +5 -4
- package/src/base-components/Input.tsx +2 -2
- package/src/base-components/Select.tsx +1 -1
- package/src/base-components/TableMobileOptions.tsx +21 -16
- package/src/common-components/PaymentSection.tsx +4 -4
package/package.json
CHANGED
|
@@ -3,17 +3,21 @@ import React from 'react';
|
|
|
3
3
|
export interface DateLabelProps {
|
|
4
4
|
date: string | Date;
|
|
5
5
|
className?: string;
|
|
6
|
+
isDateOnly?: boolean;
|
|
6
7
|
}
|
|
7
8
|
|
|
8
|
-
export function DateLabel({ date, className = '' }: DateLabelProps) {
|
|
9
|
+
export function DateLabel({ date, className = '', isDateOnly: forceDateOnly = false }: DateLabelProps) {
|
|
9
10
|
if (!date) return <span className="text-gray-300">-</span>;
|
|
10
11
|
|
|
11
|
-
let isDateOnly =
|
|
12
|
+
let isDateOnly = forceDateOnly;
|
|
12
13
|
let parsedDate: string | Date = date;
|
|
13
14
|
|
|
14
15
|
if (typeof date === 'string') {
|
|
15
16
|
if (/^\d{4}-\d{2}-\d{2}$/.test(date)) {
|
|
16
17
|
isDateOnly = true;
|
|
18
|
+
} else if (date.includes('T00:00:00.000000Z') || date.includes('T00:00:00.000Z')) {
|
|
19
|
+
isDateOnly = true;
|
|
20
|
+
date = date.split('T')[0];
|
|
17
21
|
} else if (/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/.test(date)) {
|
|
18
22
|
parsedDate = date.replace(' ', 'T') + 'Z';
|
|
19
23
|
}
|
|
@@ -6,14 +6,15 @@ import { cn } from '@apptimate/core-lib';
|
|
|
6
6
|
|
|
7
7
|
interface DropdownProps {
|
|
8
8
|
trigger: ReactNode;
|
|
9
|
-
children: ReactNode;
|
|
9
|
+
children: ReactNode | ((close: () => void) => ReactNode);
|
|
10
10
|
align?: 'left' | 'right';
|
|
11
11
|
valign?: 'top' | 'bottom';
|
|
12
12
|
className?: string;
|
|
13
13
|
contentClassName?: string;
|
|
14
|
+
closeOnContentClick?: boolean;
|
|
14
15
|
}
|
|
15
16
|
|
|
16
|
-
export function Dropdown({ trigger, children, align = 'left', valign = 'bottom', className, contentClassName }: DropdownProps) {
|
|
17
|
+
export function Dropdown({ trigger, children, align = 'left', valign = 'bottom', className, contentClassName, closeOnContentClick = true }: DropdownProps) {
|
|
17
18
|
const [isOpen, setIsOpen] = useState(false);
|
|
18
19
|
const [coords, setCoords] = useState({ top: 0, bottom: 0, left: 0, right: 0 });
|
|
19
20
|
const dropdownRef = useRef<HTMLDivElement>(null);
|
|
@@ -99,9 +100,9 @@ export function Dropdown({ trigger, children, align = 'left', valign = 'bottom',
|
|
|
99
100
|
valign === 'bottom' ? "mt-1.5" : "mb-1.5",
|
|
100
101
|
contentClassName
|
|
101
102
|
)}
|
|
102
|
-
onClick={closeDropdown}
|
|
103
|
+
onClick={closeOnContentClick ? closeDropdown : undefined}
|
|
103
104
|
>
|
|
104
|
-
{children}
|
|
105
|
+
{typeof children === 'function' ? children(closeDropdown) : children}
|
|
105
106
|
</div>,
|
|
106
107
|
document.body
|
|
107
108
|
)}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import React, { useState } from 'react';
|
|
4
3
|
import { cn } from '@apptimate/core-lib';
|
|
5
4
|
import { Eye, EyeOff } from 'lucide-react';
|
|
5
|
+
import React, { useState } from 'react';
|
|
6
6
|
import { Label } from './Label';
|
|
7
7
|
|
|
8
8
|
export interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
|
|
@@ -35,7 +35,7 @@ export const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|
|
35
35
|
ref={ref}
|
|
36
36
|
type={inputType}
|
|
37
37
|
className={cn(
|
|
38
|
-
|
|
38
|
+
"w-full bg-surface-0 border-[1.5px] border-border-subtle rounded-[10px] px-3.5 py-2.5 text-[13.5px] text-foreground-1 outline-none transition-all hover:border-gray-300 focus:border-gray-300 focus:bg-surface-1 placeholder:text-foreground-disabled",
|
|
39
39
|
icon && "pl-10",
|
|
40
40
|
isPassword && "pr-10",
|
|
41
41
|
error && "border-danger-alt focus:border-danger-alt hover:border-danger-alt"
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import React from 'react';
|
|
4
3
|
import { cn } from '@apptimate/core-lib';
|
|
5
4
|
import { ChevronDown } from 'lucide-react';
|
|
5
|
+
import React from 'react';
|
|
6
6
|
import { Label } from './Label';
|
|
7
7
|
|
|
8
8
|
export interface SelectProps extends React.SelectHTMLAttributes<HTMLSelectElement> {
|
|
@@ -9,7 +9,7 @@ export interface TableMobileOptionsProps {
|
|
|
9
9
|
searchPlaceholder?: string;
|
|
10
10
|
searchLabel?: string;
|
|
11
11
|
actionsLabel?: string;
|
|
12
|
-
children?: React.ReactNode;
|
|
12
|
+
children?: React.ReactNode | ((close: () => void) => React.ReactNode);
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
export const TableMobileOptions = ({
|
|
@@ -24,27 +24,32 @@ export const TableMobileOptions = ({
|
|
|
24
24
|
<Dropdown
|
|
25
25
|
align="right"
|
|
26
26
|
contentClassName="w-[280px] p-2"
|
|
27
|
+
closeOnContentClick={false}
|
|
27
28
|
trigger={
|
|
28
29
|
<button className="flex items-center justify-center gap-2 px-3 py-2 rounded-[10px] bg-white border border-gray-200 text-gray-700 hover:bg-gray-50 transition-colors shadow-sm font-bold text-[13px]">
|
|
29
30
|
<Settings2 size={16} /> Options
|
|
30
31
|
</button>
|
|
31
32
|
}
|
|
32
33
|
>
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
34
|
+
{(close) => (
|
|
35
|
+
<>
|
|
36
|
+
<div className="px-2 pb-2 pt-1 mb-1 border-b border-gray-100">
|
|
37
|
+
<p className="text-[11px] font-bold text-gray-400 uppercase tracking-wider mb-2">{searchLabel}</p>
|
|
38
|
+
<Input
|
|
39
|
+
placeholder={searchPlaceholder}
|
|
40
|
+
icon={<Search size={16} className="text-gray-400" />}
|
|
41
|
+
className="[&_input]:rounded-[8px] [&_input]:border [&_input]:border-gray-200 [&_input]:bg-white [&_input]:hover:border-gray-300 [&_input]:focus:border-gray-300 w-full"
|
|
42
|
+
value={searchValue}
|
|
43
|
+
onChange={(e) => onSearchChange(e.target.value)}
|
|
44
|
+
/>
|
|
45
|
+
</div>
|
|
46
|
+
{children && (
|
|
47
|
+
<div className="px-2 pt-1 pb-1">
|
|
48
|
+
<p className="text-[11px] font-bold text-gray-400 uppercase tracking-wider mb-1">{actionsLabel}</p>
|
|
49
|
+
{typeof children === 'function' ? children(close) : children}
|
|
50
|
+
</div>
|
|
51
|
+
)}
|
|
52
|
+
</>
|
|
48
53
|
)}
|
|
49
54
|
</Dropdown>
|
|
50
55
|
);
|
|
@@ -198,7 +198,7 @@ export function PaymentSection({ totalAmount, payments, onPaymentsChange, compac
|
|
|
198
198
|
.map((mode) => {
|
|
199
199
|
const isActive = selectionMode === "single" && payments.length === 1 && payments[0].mode_id === mode.id;
|
|
200
200
|
return (
|
|
201
|
-
<button key={mode.id} onClick={() => selectSingleMode(mode)}
|
|
201
|
+
<button key={mode.id} type="button" onClick={() => selectSingleMode(mode)}
|
|
202
202
|
className={cn(
|
|
203
203
|
"inline-flex items-center gap-2 px-4 py-2.5 text-[12px] font-semibold rounded-[10px] border transition-all",
|
|
204
204
|
isActive
|
|
@@ -211,7 +211,7 @@ export function PaymentSection({ totalAmount, payments, onPaymentsChange, compac
|
|
|
211
211
|
);
|
|
212
212
|
})}
|
|
213
213
|
{/* Split Button */}
|
|
214
|
-
<button onClick={activateSplitMode}
|
|
214
|
+
<button type="button" onClick={activateSplitMode}
|
|
215
215
|
className={cn(
|
|
216
216
|
"inline-flex items-center gap-2 px-4 py-2.5 text-[12px] font-semibold rounded-[10px] border transition-all",
|
|
217
217
|
selectionMode === "split"
|
|
@@ -248,7 +248,7 @@ export function PaymentSection({ totalAmount, payments, onPaymentsChange, compac
|
|
|
248
248
|
return canBeMultiple || !payments.some(p => p.mode_id === m.id);
|
|
249
249
|
})
|
|
250
250
|
.map((mode) => (
|
|
251
|
-
<button key={mode.id} onClick={() => addPayment(mode)}
|
|
251
|
+
<button key={mode.id} type="button" onClick={() => addPayment(mode)}
|
|
252
252
|
className="inline-flex items-center gap-1.5 px-3 py-1.5 text-[11px] font-semibold rounded-[8px] bg-surface-0 border border-border-subtle text-foreground-subtle hover:border-primary/40 hover:text-primary transition-all">
|
|
253
253
|
{getModeIcon(mode.code, 12)}
|
|
254
254
|
+ {mode.name}
|
|
@@ -334,7 +334,7 @@ function PaymentEntryRow({ entry, mode, bankAccounts, materialTypes = [], onUpda
|
|
|
334
334
|
{entry.mode_name}
|
|
335
335
|
</div>
|
|
336
336
|
{showRemove && (
|
|
337
|
-
<button onClick={onRemove}
|
|
337
|
+
<button type="button" onClick={onRemove}
|
|
338
338
|
className="w-7 h-7 rounded-[6px] flex items-center justify-center text-foreground-disabled hover:text-danger-alt hover:bg-danger-alt/10 transition-all">
|
|
339
339
|
<Trash2 size={13} />
|
|
340
340
|
</button>
|