0.13.1-1 Episode Detection
This commit is contained in:
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "ripster-frontend",
|
||||
"version": "0.13.1",
|
||||
"version": "0.13.1-1",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "ripster-frontend",
|
||||
"version": "0.13.1",
|
||||
"version": "0.13.1-1",
|
||||
"dependencies": {
|
||||
"primeicons": "^7.0.0",
|
||||
"primereact": "^10.9.2",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ripster-frontend",
|
||||
"version": "0.13.1",
|
||||
"version": "0.13.1-1",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
@@ -32,6 +32,137 @@ function normalizeTrackId(value) {
|
||||
return Math.trunc(parsed);
|
||||
}
|
||||
|
||||
function normalizePositiveInt(value) {
|
||||
const parsed = Number(value);
|
||||
if (!Number.isFinite(parsed) || parsed <= 0) {
|
||||
return null;
|
||||
}
|
||||
return Math.trunc(parsed);
|
||||
}
|
||||
|
||||
function formatTwoDigit(value) {
|
||||
const normalized = normalizePositiveInt(value);
|
||||
if (normalized === null) {
|
||||
return '??';
|
||||
}
|
||||
return String(normalized).padStart(2, '0');
|
||||
}
|
||||
|
||||
function buildEpisodeRangeToken(start, end) {
|
||||
const startNormalized = normalizePositiveInt(start);
|
||||
const endNormalized = normalizePositiveInt(end);
|
||||
if (!startNormalized) {
|
||||
return '??';
|
||||
}
|
||||
if (!endNormalized || endNormalized <= startNormalized) {
|
||||
return formatTwoDigit(startNormalized);
|
||||
}
|
||||
return `${formatTwoDigit(startNormalized)}-${formatTwoDigit(endNormalized)}`;
|
||||
}
|
||||
|
||||
function buildEpisodePartsToken(start, end) {
|
||||
const startNormalized = normalizePositiveInt(start) || 1;
|
||||
const endNormalized = normalizePositiveInt(end) || startNormalized;
|
||||
const span = Math.max(1, endNormalized - startNormalized + 1);
|
||||
if (span <= 1) {
|
||||
return '1';
|
||||
}
|
||||
if (span === 2) {
|
||||
return '1+2';
|
||||
}
|
||||
return `1-${span}`;
|
||||
}
|
||||
|
||||
function stripEpisodePartSuffix(value) {
|
||||
const source = String(value || '').replace(/\s+/g, ' ').trim();
|
||||
if (!source) {
|
||||
return '';
|
||||
}
|
||||
const suffixPattern = /(?:\s*[-–—:.,]?\s*)(?:\(?\s*(?:teil|part|pt)\s*(?:\d{1,2}|[ivxlcdm]{1,6})(?:\s*(?:\/|von|of)\s*(?:\d{1,2}|[ivxlcdm]{1,6}))?\s*\)?)\s*$/i;
|
||||
let normalized = source;
|
||||
let changed = false;
|
||||
for (let i = 0; i < 2; i += 1) {
|
||||
const next = normalized.replace(suffixPattern, '').replace(/[-–—:.,\s]+$/g, '').trim();
|
||||
if (!next || next === normalized) {
|
||||
break;
|
||||
}
|
||||
normalized = next;
|
||||
changed = true;
|
||||
}
|
||||
return changed ? (normalized || source) : source;
|
||||
}
|
||||
|
||||
function normalizeEpisodeFillTitle(value, isMulti = false) {
|
||||
const source = String(value || '').replace(/\s+/g, ' ').trim();
|
||||
if (!source) {
|
||||
return '';
|
||||
}
|
||||
if (!isMulti) {
|
||||
return source;
|
||||
}
|
||||
return stripEpisodePartSuffix(source) || source;
|
||||
}
|
||||
|
||||
function resolveTitleDurationSecondsForFill(title = null) {
|
||||
const durationSeconds = Number(title?.durationSeconds || 0);
|
||||
if (Number.isFinite(durationSeconds) && durationSeconds > 0) {
|
||||
return durationSeconds;
|
||||
}
|
||||
const durationMinutes = Number(title?.durationMinutes || 0);
|
||||
if (Number.isFinite(durationMinutes) && durationMinutes > 0) {
|
||||
return durationMinutes * 60;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
function medianPositiveNumber(values = []) {
|
||||
const numbers = (Array.isArray(values) ? values : [])
|
||||
.map((value) => Number(value))
|
||||
.filter((value) => Number.isFinite(value) && value > 0)
|
||||
.sort((a, b) => a - b);
|
||||
if (numbers.length === 0) {
|
||||
return 0;
|
||||
}
|
||||
const middle = Math.floor(numbers.length / 2);
|
||||
if (numbers.length % 2 === 1) {
|
||||
return numbers[middle];
|
||||
}
|
||||
return (numbers[middle - 1] + numbers[middle]) / 2;
|
||||
}
|
||||
|
||||
function estimateEpisodeSpanForFill(title = null, allTitles = []) {
|
||||
const titleDuration = resolveTitleDurationSecondsForFill(title);
|
||||
if (!Number.isFinite(titleDuration) || titleDuration <= 0) {
|
||||
return 1;
|
||||
}
|
||||
const durations = (Array.isArray(allTitles) ? allTitles : [])
|
||||
.map((item) => resolveTitleDurationSecondsForFill(item))
|
||||
.filter((value) => Number.isFinite(value) && value > 0);
|
||||
if (durations.length === 0) {
|
||||
return 1;
|
||||
}
|
||||
const medianDuration = medianPositiveNumber(durations);
|
||||
if (!medianDuration) {
|
||||
return 1;
|
||||
}
|
||||
const baselinePool = durations.filter((value) => value <= medianDuration * 1.35);
|
||||
const baselineDuration = medianPositiveNumber(baselinePool.length > 0 ? baselinePool : durations) || medianDuration;
|
||||
if (!baselineDuration) {
|
||||
return 1;
|
||||
}
|
||||
const ratio = titleDuration / baselineDuration;
|
||||
const roundedSpan = Math.round(ratio);
|
||||
if (!Number.isFinite(roundedSpan) || roundedSpan < 2 || roundedSpan > 4) {
|
||||
return 1;
|
||||
}
|
||||
const minRatio = roundedSpan === 2 ? 1.55 : roundedSpan * 0.72;
|
||||
const maxRatio = roundedSpan * 1.32;
|
||||
if (ratio < minRatio || ratio > maxRatio) {
|
||||
return 1;
|
||||
}
|
||||
return roundedSpan;
|
||||
}
|
||||
|
||||
function normalizeTrackIdList(values) {
|
||||
const list = Array.isArray(values) ? values : [];
|
||||
const seen = new Set();
|
||||
@@ -429,7 +560,7 @@ function transliterateForFilename(input) {
|
||||
|
||||
function sanitizePathSegment(value) {
|
||||
return transliterateForFilename(String(value || ''))
|
||||
.replace(/[^a-zA-Z0-9 ().[\]_-]/g, '')
|
||||
.replace(/[^a-zA-Z0-9 ().[\]_+-]/g, '')
|
||||
.replace(/\s+/g, ' ')
|
||||
.trim();
|
||||
}
|
||||
@@ -1319,27 +1450,175 @@ export default function MediaInfoReviewPanel({
|
||||
const normalizedEpisodeRows = (Array.isArray(selectedMetadataEpisodes) ? selectedMetadataEpisodes : [])
|
||||
.map((episode) => {
|
||||
const episodeId = Number(episode?.episodeId ?? episode?.id ?? 0);
|
||||
const episodeNumber = Number(episode?.episodeNumber ?? episode?.number ?? 0);
|
||||
const episodeIdStart = Number(episode?.episodeIdStart ?? episode?.idStart ?? episode?.episodeId ?? episode?.id ?? 0);
|
||||
const episodeIdEnd = Number(episode?.episodeIdEnd ?? episode?.idEnd ?? 0);
|
||||
const episodeNumberStart = Number(
|
||||
episode?.episodeNumberStart
|
||||
?? episode?.numberStart
|
||||
?? episode?.episodeNoStart
|
||||
?? episode?.episodeNumber
|
||||
?? episode?.number
|
||||
?? 0
|
||||
);
|
||||
const episodeNumberEnd = Number(
|
||||
episode?.episodeNumberEnd
|
||||
?? episode?.numberEnd
|
||||
?? episode?.episodeNoEnd
|
||||
?? 0
|
||||
);
|
||||
const episodeSpan = Number(episode?.episodeSpan ?? episode?.episodeCount ?? 0);
|
||||
const episodeNumber = Number(episode?.episodeNumber ?? episode?.number ?? episodeNumberStart ?? 0);
|
||||
const seasonNumber = Number(episode?.seasonNumber ?? episode?.season ?? 0);
|
||||
const episodeTitle = String(episode?.episodeTitle || episode?.name || episode?.title || '').trim();
|
||||
return {
|
||||
episodeId: Number.isFinite(episodeId) && episodeId > 0 ? Math.trunc(episodeId) : null,
|
||||
episodeIdStart: Number.isFinite(episodeIdStart) && episodeIdStart > 0 ? Math.trunc(episodeIdStart) : null,
|
||||
episodeIdEnd: Number.isFinite(episodeIdEnd) && episodeIdEnd > 0 ? Math.trunc(episodeIdEnd) : null,
|
||||
episodeNumber: Number.isFinite(episodeNumber) && episodeNumber > 0 ? Math.trunc(episodeNumber) : null,
|
||||
episodeNumberStart: Number.isFinite(episodeNumberStart) && episodeNumberStart > 0 ? Math.trunc(episodeNumberStart) : null,
|
||||
episodeNumberEnd: Number.isFinite(episodeNumberEnd) && episodeNumberEnd > 0 ? Math.trunc(episodeNumberEnd) : null,
|
||||
episodeSpan: Number.isFinite(episodeSpan) && episodeSpan > 0 ? Math.trunc(episodeSpan) : null,
|
||||
seasonNumber: Number.isFinite(seasonNumber) && seasonNumber > 0 ? Math.trunc(seasonNumber) : null,
|
||||
episodeTitle: episodeTitle || null
|
||||
};
|
||||
})
|
||||
.filter((episode) => episode.episodeId || episode.episodeNumber !== null);
|
||||
const episodeFillOptions = normalizedEpisodeRows.map((episode) => {
|
||||
const seasonLabel = episode.seasonNumber !== null ? `S${String(episode.seasonNumber).padStart(2, '0')}` : 'S??';
|
||||
const episodeLabel = episode.episodeNumber !== null ? `E${String(episode.episodeNumber).padStart(2, '0')}` : 'E??';
|
||||
const titleLabel = episode.episodeTitle || '(ohne Titel)';
|
||||
const value = String(episode.episodeId || episode.episodeNumber || '');
|
||||
return {
|
||||
label: `${seasonLabel}${episodeLabel} - ${titleLabel}`,
|
||||
value
|
||||
};
|
||||
}).filter((option) => option.value);
|
||||
const selectedTitlesForFill = selectedTitleIds.length > 0
|
||||
? displayTitles.filter((title) => selectedTitleIdSet.has(String(normalizeTitleId(title?.id))))
|
||||
: displayTitles.filter((title) => Boolean(title?.selectedForEncode));
|
||||
const titleAnchorsByEpisodeIndex = (() => {
|
||||
const anchors = new Map();
|
||||
const rows = Array.isArray(normalizedEpisodeRows) ? normalizedEpisodeRows : [];
|
||||
if (rows.length === 0) {
|
||||
return anchors;
|
||||
}
|
||||
let cursor = 0;
|
||||
for (const title of selectedTitlesForFill) {
|
||||
if (cursor >= rows.length) {
|
||||
break;
|
||||
}
|
||||
const explicitSpan = normalizePositiveInt(
|
||||
title?.episodeSpan
|
||||
?? title?.episodeCount
|
||||
?? null
|
||||
);
|
||||
const estimatedSpan = estimateEpisodeSpanForFill(title, selectedTitlesForFill);
|
||||
const span = Math.max(1, explicitSpan || 1, estimatedSpan || 1);
|
||||
const safeSpan = Math.max(1, Math.min(span, rows.length - cursor));
|
||||
anchors.set(cursor, { span: safeSpan });
|
||||
cursor += safeSpan;
|
||||
}
|
||||
return anchors;
|
||||
})();
|
||||
|
||||
const fallbackEpisodeFillOptions = (() => {
|
||||
const rows = Array.isArray(normalizedEpisodeRows) ? normalizedEpisodeRows : [];
|
||||
const options = [];
|
||||
let index = 0;
|
||||
while (index < rows.length) {
|
||||
const startRow = rows[index];
|
||||
const seasonNumber = normalizePositiveInt(startRow?.seasonNumber ?? null);
|
||||
const seasonLabel = seasonNumber !== null ? `S${String(seasonNumber).padStart(2, '0')}` : 'S??';
|
||||
const rangeStart = normalizePositiveInt(
|
||||
startRow?.episodeNumberStart
|
||||
?? startRow?.episodeNoStart
|
||||
?? startRow?.episodeNumber
|
||||
?? null
|
||||
);
|
||||
const explicitRangeEnd = normalizePositiveInt(
|
||||
startRow?.episodeNumberEnd
|
||||
?? startRow?.episodeNoEnd
|
||||
?? null
|
||||
);
|
||||
const explicitRangeSpan = normalizePositiveInt(
|
||||
startRow?.episodeSpan
|
||||
?? startRow?.episodeCount
|
||||
?? null
|
||||
);
|
||||
let consumedRows = 1;
|
||||
let rangeEnd = explicitRangeEnd || rangeStart;
|
||||
|
||||
if (
|
||||
rangeStart
|
||||
&& (
|
||||
(explicitRangeEnd && explicitRangeEnd > rangeStart)
|
||||
|| (explicitRangeSpan && explicitRangeSpan > 1)
|
||||
)
|
||||
) {
|
||||
const effectiveSpan = explicitRangeEnd && explicitRangeEnd > rangeStart
|
||||
? Math.max(1, explicitRangeEnd - rangeStart + 1)
|
||||
: Math.max(1, explicitRangeSpan || 1);
|
||||
rangeEnd = rangeStart + effectiveSpan - 1;
|
||||
consumedRows = 1;
|
||||
} else {
|
||||
const anchor = titleAnchorsByEpisodeIndex.get(index) || null;
|
||||
const anchorSpan = normalizePositiveInt(anchor?.span) || 1;
|
||||
if (anchorSpan > 1) {
|
||||
const endIndex = Math.min(rows.length - 1, index + anchorSpan - 1);
|
||||
const endRow = rows[endIndex];
|
||||
const endRowStart = normalizePositiveInt(
|
||||
endRow?.episodeNumberStart
|
||||
?? endRow?.episodeNoStart
|
||||
?? endRow?.episodeNumber
|
||||
?? null
|
||||
);
|
||||
rangeEnd = endRowStart || (rangeStart ? (rangeStart + anchorSpan - 1) : null);
|
||||
consumedRows = anchorSpan;
|
||||
}
|
||||
}
|
||||
|
||||
if (rangeStart && rangeEnd && rangeEnd < rangeStart) {
|
||||
rangeEnd = rangeStart;
|
||||
}
|
||||
const isMulti = Boolean(rangeStart && rangeEnd && rangeEnd > rangeStart);
|
||||
const episodeRangeToken = buildEpisodeRangeToken(rangeStart, rangeEnd);
|
||||
const partsToken = buildEpisodePartsToken(rangeStart, rangeEnd);
|
||||
const baseTitle = String(startRow?.episodeTitle || '(ohne Titel)').trim() || '(ohne Titel)';
|
||||
const normalizedTitle = normalizeEpisodeFillTitle(baseTitle, isMulti);
|
||||
const titleLabel = isMulti
|
||||
? `${normalizedTitle} (Teil ${partsToken})`
|
||||
: normalizedTitle;
|
||||
const episodeLabel = rangeStart ? `E${episodeRangeToken}` : 'E??';
|
||||
const value = String(
|
||||
startRow?.episodeIdStart
|
||||
|| startRow?.episodeId
|
||||
|| rangeStart
|
||||
|| ''
|
||||
);
|
||||
if (value) {
|
||||
options.push({
|
||||
label: `${seasonLabel}${episodeLabel} - ${titleLabel}`,
|
||||
value,
|
||||
startRef: value
|
||||
});
|
||||
}
|
||||
index += Math.max(1, consumedRows);
|
||||
}
|
||||
return options;
|
||||
})();
|
||||
const episodeFillOptions = fallbackEpisodeFillOptions;
|
||||
const selectedEpisodeFillValue = (() => {
|
||||
const raw = String(selectedEpisodeFillStart || '').trim();
|
||||
if (!raw) {
|
||||
return null;
|
||||
}
|
||||
const direct = episodeFillOptions.find((option) => String(option?.value || '').trim() === raw);
|
||||
if (direct) {
|
||||
return direct.value;
|
||||
}
|
||||
const byStartRef = episodeFillOptions.find((option) => String(option?.startRef || '').trim() === raw);
|
||||
if (byStartRef) {
|
||||
return byStartRef.value;
|
||||
}
|
||||
const tokenMatch = raw.match(/^fill:([^:]+):title:\d+$/i);
|
||||
if (tokenMatch && tokenMatch[1]) {
|
||||
const byTokenRef = episodeFillOptions.find((option) => String(option?.startRef || '').trim() === String(tokenMatch[1]).trim());
|
||||
if (byTokenRef) {
|
||||
return byTokenRef.value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
})();
|
||||
const processedFiles = Number(review.processedFiles || displayTitles.length || 0);
|
||||
const totalFiles = Number(review.totalFiles || displayTitles.length || 0);
|
||||
const playlistRecommendation = review.playlistRecommendation || null;
|
||||
@@ -1893,7 +2172,7 @@ export default function MediaInfoReviewPanel({
|
||||
<div className="episode-fill-field">
|
||||
<label>Episodentitel befüllen ab Episode</label>
|
||||
<Dropdown
|
||||
value={String(selectedEpisodeFillStart || '').trim() || null}
|
||||
value={selectedEpisodeFillValue}
|
||||
options={episodeFillOptions}
|
||||
optionLabel="label"
|
||||
optionValue="value"
|
||||
@@ -1906,6 +2185,7 @@ export default function MediaInfoReviewPanel({
|
||||
/>
|
||||
<small>
|
||||
Startet bei der gewählten Episode und befüllt alle ausgewählten DVD-Titel der Reihe nach.
|
||||
Multi-Episoden werden als Bereich (z.B. E16-17 | Teil 1+2) angezeigt.
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -345,6 +345,11 @@ function getQueueActionResult(response) {
|
||||
return response?.result && typeof response.result === 'object' ? response.result : {};
|
||||
}
|
||||
|
||||
function isPipelineMetadataFlowStatus(value) {
|
||||
const normalized = String(value || '').trim().toUpperCase();
|
||||
return ['METADATA_SELECTION', 'READY_TO_START', 'WAITING_FOR_USER_DECISION'].includes(normalized);
|
||||
}
|
||||
|
||||
function normalizeSortText(value) {
|
||||
return String(value || '').trim().toLocaleLowerCase('de-DE');
|
||||
}
|
||||
@@ -1113,6 +1118,14 @@ export default function HistoryPage({ refreshToken = 0 }) {
|
||||
const discNumber = selectedMetadata?.discNumber ?? analyzeContext?.seriesLookupHint?.discNumber ?? null;
|
||||
const context = {
|
||||
jobId,
|
||||
status: row?.status || null,
|
||||
lastState: row?.last_state || null,
|
||||
submitMode: (
|
||||
isPipelineMetadataFlowStatus(row?.status)
|
||||
|| isPipelineMetadataFlowStatus(row?.last_state)
|
||||
)
|
||||
? 'pipeline'
|
||||
: 'assign',
|
||||
detectedTitle: row?.detected_title || row?.title || '',
|
||||
selectedMetadata: {
|
||||
...(selectedMetadata && typeof selectedMetadata === 'object' ? selectedMetadata : {}),
|
||||
@@ -1166,8 +1179,53 @@ export default function HistoryPage({ refreshToken = 0 }) {
|
||||
|
||||
const handleOmdbSubmit = async (payload) => {
|
||||
const provider = String(payload?.metadataProvider || metadataDialogContext?.metadataProvider || 'omdb').trim().toLowerCase() || 'omdb';
|
||||
const submitMode = String(metadataDialogContext?.submitMode || 'assign').trim().toLowerCase() || 'assign';
|
||||
const isPipelineFlow = submitMode === 'pipeline';
|
||||
setOmdbAssignBusy(true);
|
||||
try {
|
||||
if (isPipelineFlow) {
|
||||
await api.selectMetadata(payload);
|
||||
setMetadataDialogVisible(false);
|
||||
setMetadataDialogContext(null);
|
||||
|
||||
try {
|
||||
const startResponse = await api.startJob(payload.jobId);
|
||||
const startResult = getQueueActionResult(startResponse);
|
||||
if (startResult.queued) {
|
||||
toastRef.current?.show({
|
||||
severity: 'info',
|
||||
summary: 'Job in Queue',
|
||||
detail: startResult.queuePosition > 0
|
||||
? `Metadaten übernommen. Start wurde in Queue-Position ${startResult.queuePosition} eingeplant.`
|
||||
: 'Metadaten übernommen. Start wurde in der Queue eingeplant.',
|
||||
life: 3500
|
||||
});
|
||||
} else {
|
||||
toastRef.current?.show({
|
||||
severity: 'success',
|
||||
summary: 'Job gestartet',
|
||||
detail: 'Metadaten übernommen. Die Verarbeitung wurde gestartet.',
|
||||
life: 3200
|
||||
});
|
||||
}
|
||||
} catch (startError) {
|
||||
const startMessage = String(startError?.message || '').trim();
|
||||
const waitingForPlaylist = startMessage.includes('waiting_for_manual_playlist_selection');
|
||||
toastRef.current?.show({
|
||||
severity: waitingForPlaylist ? 'warn' : 'error',
|
||||
summary: waitingForPlaylist ? 'Metadaten übernommen' : 'Start fehlgeschlagen',
|
||||
detail: waitingForPlaylist
|
||||
? 'Metadaten wurden übernommen, aber es fehlt noch eine Titel-/Playlist-Auswahl. Bitte im Ripper fortsetzen.'
|
||||
: (startMessage || 'Job konnte nach der Metadaten-Übernahme nicht gestartet werden.'),
|
||||
life: waitingForPlaylist ? 4200 : 4500
|
||||
});
|
||||
}
|
||||
|
||||
await load();
|
||||
await refreshDetailIfOpen(payload.jobId);
|
||||
return;
|
||||
}
|
||||
|
||||
await api.assignJobOmdb(payload.jobId, payload);
|
||||
toastRef.current?.show({
|
||||
severity: 'success',
|
||||
@@ -1180,6 +1238,9 @@ export default function HistoryPage({ refreshToken = 0 }) {
|
||||
await load();
|
||||
await refreshDetailIfOpen(payload.jobId);
|
||||
} catch (error) {
|
||||
if (isPipelineFlow) {
|
||||
throw error;
|
||||
}
|
||||
toastRef.current?.show({
|
||||
severity: 'error',
|
||||
summary: provider === 'tmdb' ? 'TMDb-Zuweisung fehlgeschlagen' : 'OMDb-Zuweisung fehlgeschlagen',
|
||||
|
||||
@@ -11,6 +11,12 @@ export function confirmModal(options = {}) {
|
||||
const rejectLabel = String(options?.rejectLabel || 'Abbrechen').trim() || 'Abbrechen';
|
||||
const icon = String(options?.icon || 'pi pi-exclamation-triangle').trim() || 'pi pi-exclamation-triangle';
|
||||
const danger = Boolean(options?.danger);
|
||||
const style = options?.style && typeof options.style === 'object'
|
||||
? options.style
|
||||
: null;
|
||||
const breakpoints = options?.breakpoints && typeof options.breakpoints === 'object'
|
||||
? options.breakpoints
|
||||
: null;
|
||||
|
||||
return new Promise((resolve) => {
|
||||
let finished = false;
|
||||
@@ -32,6 +38,8 @@ export function confirmModal(options = {}) {
|
||||
dismissableMask: true,
|
||||
acceptLabel,
|
||||
rejectLabel,
|
||||
...(style ? { style } : {}),
|
||||
...(breakpoints ? { breakpoints } : {}),
|
||||
...(danger ? { acceptClassName: 'p-button-danger' } : {}),
|
||||
accept: () => settle(true),
|
||||
reject: () => settle(false),
|
||||
|
||||
Reference in New Issue
Block a user