매매 시그널알림 화면 수정
This commit is contained in:
@@ -11549,6 +11549,9 @@ html.theme-light .tam-disclaimer { color: #90a4ae; }
|
|||||||
color: #f7768e;
|
color: #f7768e;
|
||||||
border-color: rgba(247, 118, 142, 0.55);
|
border-color: rgba(247, 118, 142, 0.55);
|
||||||
}
|
}
|
||||||
|
.tnl-icon-btn--refreshing svg {
|
||||||
|
animation: spin 0.8s linear infinite;
|
||||||
|
}
|
||||||
.tnl-row-icon-btn {
|
.tnl-row-icon-btn {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
align-self: center;
|
align-self: center;
|
||||||
@@ -11576,6 +11579,7 @@ html.theme-light .tam-disclaimer { color: #90a4ae; }
|
|||||||
background: var(--bg3);
|
background: var(--bg3);
|
||||||
}
|
}
|
||||||
.tnl-chip--warn { background: rgba(239,83,80,0.15); color: #f7768e; }
|
.tnl-chip--warn { background: rgba(239,83,80,0.15); color: #f7768e; }
|
||||||
|
.tnl-chip--selected { background: rgba(122,162,247,0.18); color: var(--accent, #7aa2f7); font-weight: 600; }
|
||||||
.tnl-filter { display: flex; gap: 4px; }
|
.tnl-filter { display: flex; gap: 4px; }
|
||||||
.tnl-filter button,
|
.tnl-filter button,
|
||||||
.tnl-btn {
|
.tnl-btn {
|
||||||
|
|||||||
@@ -41,6 +41,14 @@ const IcTrash = () => (
|
|||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const IcRefresh = () => (
|
||||||
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden>
|
||||||
|
<polyline points="23 4 23 10 17 10" />
|
||||||
|
<polyline points="1 20 1 14 7 14" />
|
||||||
|
<path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
theme: Theme;
|
theme: Theme;
|
||||||
onGoToChart: (market: string) => void;
|
onGoToChart: (market: string) => void;
|
||||||
@@ -77,7 +85,7 @@ export const TradeNotificationListPage: React.FC<Props> = ({
|
|||||||
markAsRead,
|
markAsRead,
|
||||||
markAllAsRead,
|
markAllAsRead,
|
||||||
deleteNotification,
|
deleteNotification,
|
||||||
deleteUnreadNotifications,
|
deleteNotifications,
|
||||||
deleteAllNotifications,
|
deleteAllNotifications,
|
||||||
openDetail,
|
openDetail,
|
||||||
refreshHistory,
|
refreshHistory,
|
||||||
@@ -91,12 +99,13 @@ export const TradeNotificationListPage: React.FC<Props> = ({
|
|||||||
() => loadTradeNotificationGridPreset(),
|
() => loadTradeNotificationGridPreset(),
|
||||||
);
|
);
|
||||||
const [selectedMarket, setSelectedMarket] = useState(defaultMarket);
|
const [selectedMarket, setSelectedMarket] = useState(defaultMarket);
|
||||||
const [selectedNotifyId, setSelectedNotifyId] = useState<string | null>(null);
|
const [selectedIds, setSelectedIds] = useState<Set<string>>(() => new Set());
|
||||||
const [rightTab, setRightTab] = useState<RightTab>('trade');
|
const [rightTab, setRightTab] = useState<RightTab>('trade');
|
||||||
const [fillBuy, setFillBuy] = useState<TradeOrderFillRequest | null>(null);
|
const [fillBuy, setFillBuy] = useState<TradeOrderFillRequest | null>(null);
|
||||||
const [fillSell, setFillSell] = useState<TradeOrderFillRequest | null>(null);
|
const [fillSell, setFillSell] = useState<TradeOrderFillRequest | null>(null);
|
||||||
const [summary, setSummary] = useState<PaperSummaryDto | null>(null);
|
const [summary, setSummary] = useState<PaperSummaryDto | null>(null);
|
||||||
const orderAnchorRef = useRef<HTMLDivElement>(null);
|
const orderAnchorRef = useRef<HTMLDivElement>(null);
|
||||||
|
const [refreshing, setRefreshing] = useState(false);
|
||||||
const { settings: appSettings } = useAppSettings();
|
const { settings: appSettings } = useAppSettings();
|
||||||
const chartLiveReceiveHighlight = appSettings.chartLiveReceiveHighlight ?? true;
|
const chartLiveReceiveHighlight = appSettings.chartLiveReceiveHighlight ?? true;
|
||||||
|
|
||||||
@@ -150,37 +159,54 @@ export const TradeNotificationListPage: React.FC<Props> = ({
|
|||||||
setRightTab('trade');
|
setRightTab('trade');
|
||||||
}, [tickers]);
|
}, [tickers]);
|
||||||
|
|
||||||
|
const selectedCount = selectedIds.size;
|
||||||
|
|
||||||
const handleNotificationSelect = useCallback((item: TradeNotificationItem) => {
|
const handleNotificationSelect = useCallback((item: TradeNotificationItem) => {
|
||||||
if (!item.isRead) markAsRead(item.id);
|
if (!item.isRead) markAsRead(item.id);
|
||||||
setSelectedNotifyId(item.id);
|
setSelectedIds(prev => {
|
||||||
const price = coerceFiniteNumber(item.price);
|
const adding = !prev.has(item.id);
|
||||||
if (price != null && price > 0) {
|
const next = new Set(prev);
|
||||||
fillBothSides(item.market, price, setFillBuy, setFillSell);
|
if (adding) {
|
||||||
} else {
|
next.add(item.id);
|
||||||
handleSelectMarket(item.market);
|
const price = coerceFiniteNumber(item.price);
|
||||||
}
|
if (price != null && price > 0) {
|
||||||
setRightTab('trade');
|
fillBothSides(item.market, price, setFillBuy, setFillSell);
|
||||||
|
} else {
|
||||||
|
handleSelectMarket(item.market);
|
||||||
|
}
|
||||||
|
setRightTab('trade');
|
||||||
|
} else {
|
||||||
|
next.delete(item.id);
|
||||||
|
}
|
||||||
|
return next;
|
||||||
|
});
|
||||||
}, [markAsRead, handleSelectMarket]);
|
}, [markAsRead, handleSelectMarket]);
|
||||||
|
|
||||||
const handleDeleteOne = useCallback(async (item: TradeNotificationItem, e: React.MouseEvent) => {
|
const handleDeleteOne = useCallback(async (item: TradeNotificationItem, e: React.MouseEvent) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
if (!window.confirm('이 알림을 삭제하시겠습니까?')) return;
|
if (!window.confirm('이 알림을 삭제하시겠습니까?')) return;
|
||||||
await deleteNotification(item.id);
|
await deleteNotification(item.id);
|
||||||
if (selectedNotifyId === item.id) setSelectedNotifyId(null);
|
setSelectedIds(prev => {
|
||||||
}, [deleteNotification, selectedNotifyId]);
|
if (!prev.has(item.id)) return prev;
|
||||||
|
const next = new Set(prev);
|
||||||
|
next.delete(item.id);
|
||||||
|
return next;
|
||||||
|
});
|
||||||
|
}, [deleteNotification]);
|
||||||
|
|
||||||
const handleDeleteUnread = useCallback(async () => {
|
const handleDeleteSelected = useCallback(async () => {
|
||||||
if (unreadCount === 0) return;
|
if (selectedCount === 0) return;
|
||||||
if (!window.confirm(`읽지 않은 알림 ${unreadCount}건을 삭제하시겠습니까?`)) return;
|
const ids = [...selectedIds];
|
||||||
await deleteUnreadNotifications();
|
if (!window.confirm(`선택한 알림 ${ids.length}건을 삭제하시겠습니까?`)) return;
|
||||||
setSelectedNotifyId(null);
|
await deleteNotifications(ids);
|
||||||
}, [unreadCount, deleteUnreadNotifications]);
|
setSelectedIds(new Set());
|
||||||
|
}, [selectedCount, selectedIds, deleteNotifications]);
|
||||||
|
|
||||||
const handleDeleteAll = useCallback(async () => {
|
const handleDeleteAll = useCallback(async () => {
|
||||||
if (allNotifications.length === 0) return;
|
if (allNotifications.length === 0) return;
|
||||||
if (!window.confirm(`알림 ${allNotifications.length}건을 모두 삭제하시겠습니까?`)) return;
|
if (!window.confirm(`알림 ${allNotifications.length}건을 모두 삭제하시겠습니까?`)) return;
|
||||||
await deleteAllNotifications();
|
await deleteAllNotifications();
|
||||||
setSelectedNotifyId(null);
|
setSelectedIds(new Set());
|
||||||
}, [allNotifications.length, deleteAllNotifications]);
|
}, [allNotifications.length, deleteAllNotifications]);
|
||||||
|
|
||||||
const handleOrderbookRowClick = useCallback((price: number, rowType: 'ask' | 'bid') => {
|
const handleOrderbookRowClick = useCallback((price: number, rowType: 'ask' | 'bid') => {
|
||||||
@@ -200,6 +226,19 @@ export const TradeNotificationListPage: React.FC<Props> = ({
|
|||||||
saveTradeNotificationListLayout(layout);
|
saveTradeNotificationListLayout(layout);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const handlePageRefresh = useCallback(async () => {
|
||||||
|
if (refreshing) return;
|
||||||
|
setRefreshing(true);
|
||||||
|
try {
|
||||||
|
await Promise.all([
|
||||||
|
refreshHistory({ serverOnly: true }),
|
||||||
|
refreshPaperData(),
|
||||||
|
]);
|
||||||
|
} finally {
|
||||||
|
setRefreshing(false);
|
||||||
|
}
|
||||||
|
}, [refreshing, refreshHistory, refreshPaperData]);
|
||||||
|
|
||||||
const handleGridPresetChange = useCallback((preset: TradeNotificationGridPresetId) => {
|
const handleGridPresetChange = useCallback((preset: TradeNotificationGridPresetId) => {
|
||||||
setGridPreset(preset);
|
setGridPreset(preset);
|
||||||
saveTradeNotificationGridPreset(preset);
|
saveTradeNotificationGridPreset(preset);
|
||||||
@@ -214,13 +253,11 @@ export const TradeNotificationListPage: React.FC<Props> = ({
|
|||||||
<div className="tnl-main" style={{ containerType: 'inline-size', containerName: 'tnl-main' }}>
|
<div className="tnl-main" style={{ containerType: 'inline-size', containerName: 'tnl-main' }}>
|
||||||
<div className="tnl-header">
|
<div className="tnl-header">
|
||||||
<h1 className="tnl-title">매매 시그널 알림</h1>
|
<h1 className="tnl-title">매매 시그널 알림</h1>
|
||||||
<p className="tnl-sub">
|
|
||||||
{isListView
|
|
||||||
? '각 알림 왼쪽은 가상매매와 동일한 신호 상세 카드, 오른쪽은 캔들·지표 차트입니다. 영역 우측 버튼으로 가로 스크롤할 수 있습니다.'
|
|
||||||
: `목록형과 동일한 알림 상세 카드를 ${getTradeNotificationGridPreset(gridPreset).label} 그리드로 표시합니다. 툴바 우측에서 배치를 변경할 수 있습니다.`}
|
|
||||||
</p>
|
|
||||||
<div className="tnl-toolbar">
|
<div className="tnl-toolbar">
|
||||||
<span className="tnl-chip tnl-chip--warn">미확인 {unreadCount}건</span>
|
<span className="tnl-chip tnl-chip--warn">미확인 {unreadCount}건</span>
|
||||||
|
{selectedCount > 0 && (
|
||||||
|
<span className="tnl-chip tnl-chip--selected">선택 {selectedCount}건</span>
|
||||||
|
)}
|
||||||
<div className="tnl-filter">
|
<div className="tnl-filter">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@@ -237,9 +274,6 @@ export const TradeNotificationListPage: React.FC<Props> = ({
|
|||||||
미확인
|
미확인
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<button type="button" className="tnl-btn" onClick={() => void refreshHistory()}>
|
|
||||||
새로고침
|
|
||||||
</button>
|
|
||||||
{unreadCount > 0 && (
|
{unreadCount > 0 && (
|
||||||
<button type="button" className="tnl-btn tnl-btn--primary" onClick={markAllAsRead}>
|
<button type="button" className="tnl-btn tnl-btn--primary" onClick={markAllAsRead}>
|
||||||
모두 읽음
|
모두 읽음
|
||||||
@@ -262,6 +296,16 @@ export const TradeNotificationListPage: React.FC<Props> = ({
|
|||||||
그리드형
|
그리드형
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={['tnl-icon-btn', refreshing ? 'tnl-icon-btn--refreshing' : ''].filter(Boolean).join(' ')}
|
||||||
|
title="화면 새로고침"
|
||||||
|
aria-label="화면 새로고침"
|
||||||
|
disabled={refreshing}
|
||||||
|
onClick={() => void handlePageRefresh()}
|
||||||
|
>
|
||||||
|
<IcRefresh />
|
||||||
|
</button>
|
||||||
<TradeNotificationGridLayoutPicker
|
<TradeNotificationGridLayoutPicker
|
||||||
value={gridPreset}
|
value={gridPreset}
|
||||||
onChange={handleGridPresetChange}
|
onChange={handleGridPresetChange}
|
||||||
@@ -271,10 +315,10 @@ export const TradeNotificationListPage: React.FC<Props> = ({
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="tnl-icon-btn"
|
className="tnl-icon-btn"
|
||||||
title="미확인 알림 삭제"
|
title={selectedCount > 0 ? `선택 알림 삭제 (${selectedCount}건)` : '선택 알림 삭제'}
|
||||||
aria-label="미확인 알림 삭제"
|
aria-label={selectedCount > 0 ? `선택 알림 ${selectedCount}건 삭제` : '선택 알림 삭제'}
|
||||||
disabled={unreadCount === 0}
|
disabled={selectedCount === 0}
|
||||||
onClick={() => void handleDeleteUnread()}
|
onClick={() => void handleDeleteSelected()}
|
||||||
>
|
>
|
||||||
<IcTrash />
|
<IcTrash />
|
||||||
</button>
|
</button>
|
||||||
@@ -310,7 +354,7 @@ export const TradeNotificationListPage: React.FC<Props> = ({
|
|||||||
item={item}
|
item={item}
|
||||||
theme={theme}
|
theme={theme}
|
||||||
layoutMode={listLayout}
|
layoutMode={listLayout}
|
||||||
isSelected={selectedNotifyId === item.id}
|
isSelected={selectedIds.has(item.id)}
|
||||||
chartsEnabled={isListView}
|
chartsEnabled={isListView}
|
||||||
chartLiveReceiveHighlight={chartLiveReceiveHighlight}
|
chartLiveReceiveHighlight={chartLiveReceiveHighlight}
|
||||||
onSelect={() => handleNotificationSelect(item)}
|
onSelect={() => handleNotificationSelect(item)}
|
||||||
|
|||||||
@@ -197,6 +197,16 @@ const TradeNotificationListRow: React.FC<Props> = ({
|
|||||||
[chartCardCount],
|
[chartCardCount],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const handleRowClick = useCallback((e: React.MouseEvent) => {
|
||||||
|
const t = e.target as HTMLElement;
|
||||||
|
if (t.closest(
|
||||||
|
'button, a, input, select, textarea, .tnl-chart-view-btn, .tnl-hscroll-pane__rail-btn',
|
||||||
|
)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
onSelect();
|
||||||
|
}, [onSelect]);
|
||||||
|
|
||||||
const summaryCard = (
|
const summaryCard = (
|
||||||
<article
|
<article
|
||||||
className={[
|
className={[
|
||||||
@@ -207,11 +217,7 @@ const TradeNotificationListRow: React.FC<Props> = ({
|
|||||||
].filter(Boolean).join(' ')}
|
].filter(Boolean).join(' ')}
|
||||||
aria-label="알림 요약"
|
aria-label="알림 요약"
|
||||||
>
|
>
|
||||||
<button
|
<div className="tnl-summary-panel" role="presentation">
|
||||||
type="button"
|
|
||||||
className="tnl-summary-panel"
|
|
||||||
onClick={onSelect}
|
|
||||||
>
|
|
||||||
<div className="tnl-summary-head">
|
<div className="tnl-summary-head">
|
||||||
<div className="tnl-summary-title">
|
<div className="tnl-summary-title">
|
||||||
<span className="tnl-summary-ko">{primary}</span>
|
<span className="tnl-summary-ko">{primary}</span>
|
||||||
@@ -264,7 +270,7 @@ const TradeNotificationListRow: React.FC<Props> = ({
|
|||||||
{strategyLabel}
|
{strategyLabel}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</div>
|
||||||
|
|
||||||
<div className="tnl-summary-card-actions">
|
<div className="tnl-summary-card-actions">
|
||||||
<button
|
<button
|
||||||
@@ -297,6 +303,7 @@ const TradeNotificationListRow: React.FC<Props> = ({
|
|||||||
isSelected ? 'tnl-row--selected' : '',
|
isSelected ? 'tnl-row--selected' : '',
|
||||||
receiving ? 'tnl-row--receiving' : '',
|
receiving ? 'tnl-row--receiving' : '',
|
||||||
].filter(Boolean).join(' ')}
|
].filter(Boolean).join(' ')}
|
||||||
|
onClick={handleRowClick}
|
||||||
>
|
>
|
||||||
{summaryCard}
|
{summaryCard}
|
||||||
</li>
|
</li>
|
||||||
@@ -313,6 +320,7 @@ const TradeNotificationListRow: React.FC<Props> = ({
|
|||||||
isSelected ? 'tnl-row--selected' : '',
|
isSelected ? 'tnl-row--selected' : '',
|
||||||
receiving ? 'tnl-row--receiving' : '',
|
receiving ? 'tnl-row--receiving' : '',
|
||||||
].filter(Boolean).join(' ')}
|
].filter(Boolean).join(' ')}
|
||||||
|
onClick={handleRowClick}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={['tnl-row-gallery', chartExpanded ? 'tnl-row-gallery--chart-expanded' : ''].filter(Boolean).join(' ')}
|
className={['tnl-row-gallery', chartExpanded ? 'tnl-row-gallery--chart-expanded' : ''].filter(Boolean).join(' ')}
|
||||||
|
|||||||
@@ -57,6 +57,8 @@ interface TradeNotificationContextValue {
|
|||||||
markAllAsRead: () => void;
|
markAllAsRead: () => void;
|
||||||
/** 알림 목록에서 단건 삭제 */
|
/** 알림 목록에서 단건 삭제 */
|
||||||
deleteNotification: (id: string) => Promise<void>;
|
deleteNotification: (id: string) => Promise<void>;
|
||||||
|
/** 지정 id 알림 일괄 삭제 (목록 선택 삭제) */
|
||||||
|
deleteNotifications: (ids: string[]) => Promise<void>;
|
||||||
/** 읽지 않은(미확인) 알림만 삭제 */
|
/** 읽지 않은(미확인) 알림만 삭제 */
|
||||||
deleteUnreadNotifications: () => Promise<void>;
|
deleteUnreadNotifications: () => Promise<void>;
|
||||||
/** 알림 목록 전체 삭제 */
|
/** 알림 목록 전체 삭제 */
|
||||||
@@ -377,33 +379,33 @@ export const TradeNotificationProvider: React.FC<ProviderProps> = ({
|
|||||||
?? toastNotificationsRef.current.find(n => n.id === id);
|
?? toastNotificationsRef.current.find(n => n.id === id);
|
||||||
}, [allNotifications]);
|
}, [allNotifications]);
|
||||||
|
|
||||||
const deleteNotification = useCallback(async (id: string) => {
|
const deleteNotifications = useCallback(async (ids: string[]) => {
|
||||||
const item = findNotificationById(id);
|
const unique = [...new Set(ids)].filter(Boolean);
|
||||||
purgeNotifications([id]);
|
if (unique.length === 0) return;
|
||||||
if (item?.dbId != null) {
|
const dbIds: number[] = [];
|
||||||
try {
|
for (const id of unique) {
|
||||||
await deleteTradeSignal(item.dbId);
|
const item = findNotificationById(id);
|
||||||
} catch (e) {
|
if (item?.dbId != null) dbIds.push(item.dbId);
|
||||||
console.warn('[TradeNotification] 서버 삭제 실패:', e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}, [findNotificationById, purgeNotifications]);
|
purgeNotifications(unique);
|
||||||
|
|
||||||
const deleteUnreadNotifications = useCallback(async () => {
|
|
||||||
const snapshot = [...allNotifications];
|
|
||||||
const unread = snapshot.filter(n => !n.isRead);
|
|
||||||
if (unread.length === 0) return;
|
|
||||||
const ids = unread.map(n => n.id);
|
|
||||||
const dbIds = unread.map(n => n.dbId).filter((x): x is number => x != null);
|
|
||||||
purgeNotifications(ids);
|
|
||||||
if (dbIds.length > 0) {
|
if (dbIds.length > 0) {
|
||||||
try {
|
try {
|
||||||
await deleteTradeSignalsBatch(dbIds);
|
await deleteTradeSignalsBatch(dbIds);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn('[TradeNotification] 미확인 일괄 삭제 실패:', e);
|
console.warn('[TradeNotification] 일괄 삭제 실패:', e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [allNotifications, purgeNotifications]);
|
}, [findNotificationById, purgeNotifications]);
|
||||||
|
|
||||||
|
const deleteNotification = useCallback(async (id: string) => {
|
||||||
|
await deleteNotifications([id]);
|
||||||
|
}, [deleteNotifications]);
|
||||||
|
|
||||||
|
const deleteUnreadNotifications = useCallback(async () => {
|
||||||
|
const unread = allNotifications.filter(n => !n.isRead);
|
||||||
|
if (unread.length === 0) return;
|
||||||
|
await deleteNotifications(unread.map(n => n.id));
|
||||||
|
}, [allNotifications, deleteNotifications]);
|
||||||
|
|
||||||
const deleteAllNotifications = useCallback(async () => {
|
const deleteAllNotifications = useCallback(async () => {
|
||||||
const ids = [
|
const ids = [
|
||||||
@@ -454,6 +456,7 @@ export const TradeNotificationProvider: React.FC<ProviderProps> = ({
|
|||||||
markAsRead,
|
markAsRead,
|
||||||
markAllAsRead,
|
markAllAsRead,
|
||||||
deleteNotification,
|
deleteNotification,
|
||||||
|
deleteNotifications,
|
||||||
deleteUnreadNotifications,
|
deleteUnreadNotifications,
|
||||||
deleteAllNotifications,
|
deleteAllNotifications,
|
||||||
refreshHistory,
|
refreshHistory,
|
||||||
|
|||||||
@@ -1033,12 +1033,25 @@ ul.tnl-list.tnl-list--grid {
|
|||||||
box-shadow: 0 0 0 1px color-mix(in srgb, var(--accent, #7aa2f7) 35%, transparent);
|
box-shadow: 0 0 0 1px color-mix(in srgb, var(--accent, #7aa2f7) 35%, transparent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tnl-row--gallery.tnl-row--selected,
|
||||||
|
.tnl-row--grid.tnl-row--selected {
|
||||||
|
border-color: var(--accent, #7aa2f7) !important;
|
||||||
|
box-shadow:
|
||||||
|
0 0 0 2px color-mix(in srgb, var(--accent, #7aa2f7) 45%, transparent),
|
||||||
|
0 0 12px 2px color-mix(in srgb, var(--accent, #7aa2f7) 18%, transparent);
|
||||||
|
}
|
||||||
|
|
||||||
.tnl-row--gallery.tnl-row--selected .tnl-hscroll-pane,
|
.tnl-row--gallery.tnl-row--selected .tnl-hscroll-pane,
|
||||||
.tnl-row--gallery.tnl-row--selected .tnl-summary-card--selected {
|
.tnl-row--gallery.tnl-row--selected .tnl-summary-card--selected {
|
||||||
border-color: var(--accent, #7aa2f7);
|
border-color: var(--accent, #7aa2f7);
|
||||||
box-shadow: 0 0 0 1px color-mix(in srgb, var(--accent, #7aa2f7) 35%, transparent);
|
box-shadow: 0 0 0 1px color-mix(in srgb, var(--accent, #7aa2f7) 35%, transparent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tnl-row--gallery,
|
||||||
|
.tnl-row--grid {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
.tnl-list--gallery .tnl-row--gallery {
|
.tnl-list--gallery .tnl-row--gallery {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user