mirror of
https://github.com/docmost/docmost.git
synced 2026-08-22 03:51:05 +08:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2c978fdf4f | ||
|
|
66b424a3b8 |
@@ -18,6 +18,15 @@ RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends curl bash \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# drop npm and corepack
|
||||
RUN rm -rf /usr/local/lib/node_modules/npm \
|
||||
&& rm -rf /usr/local/lib/node_modules/corepack \
|
||||
&& rm -rf /usr/local/bin/npm \
|
||||
&& rm -rf /usr/local/bin/npx \
|
||||
&& rm -rf /usr/local/bin/corepack \
|
||||
&& rm -rf /root/.npm \
|
||||
&& rm -rf /root/.node-gyp
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copy apps
|
||||
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
Text,
|
||||
UnstyledButton,
|
||||
Button,
|
||||
MultiSelect,
|
||||
} from "@mantine/core";
|
||||
import { IconPlus, IconTrash } from "@tabler/icons-react";
|
||||
import {
|
||||
@@ -53,9 +52,6 @@ const NO_VALUE_OPERATORS: FilterOperator[] = ["isEmpty", "isNotEmpty"];
|
||||
// stored value so a stale shape isn't sent to the engine.
|
||||
function valueClass(op: FilterOperator, inputKind: string): string {
|
||||
if (NO_VALUE_OPERATORS.includes(op)) return "none";
|
||||
if (inputKind === "choices") {
|
||||
return op === "any" || op === "none" ? "choicesMulti" : "choicesSingle";
|
||||
}
|
||||
if (inputKind === "person") {
|
||||
return op === "any" || op === "none" ? "personMulti" : "personSingle";
|
||||
}
|
||||
@@ -74,10 +70,6 @@ function getOperatorsForType(type: string): FilterOperator[] {
|
||||
DEFAULT_FILTER_OPERATORS) as FilterOperator[];
|
||||
}
|
||||
|
||||
function isMultiChoice(op: FilterCondition["op"]): boolean {
|
||||
return op === "any" || op === "none";
|
||||
}
|
||||
|
||||
function FilterValueInput({
|
||||
condition,
|
||||
property,
|
||||
@@ -129,32 +121,6 @@ function FilterValueInput({
|
||||
const typeOptions = property.typeOptions as SelectTypeOptions | undefined;
|
||||
const choices = typeOptions?.choices ?? [];
|
||||
const choiceOptions = choices.map((c) => ({ value: c.id, label: c.name }));
|
||||
|
||||
if (isMultiChoice(condition.op)) {
|
||||
const { value } = condition;
|
||||
const selected = (
|
||||
Array.isArray(value) ? value : value ? [value] : []
|
||||
).filter((id) => choices.some((c) => c.id === id));
|
||||
|
||||
return (
|
||||
<MultiSelect
|
||||
size="xs"
|
||||
data={choiceOptions}
|
||||
comboboxProps={{ withinPortal: false }}
|
||||
value={selected}
|
||||
onChange={(values) => onChange(values)}
|
||||
w={160}
|
||||
styles={{
|
||||
pillsList: {
|
||||
maxHeight: 70,
|
||||
overflowY: "auto",
|
||||
},
|
||||
}}
|
||||
maxDropdownHeight={220}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Select
|
||||
size="xs"
|
||||
@@ -233,18 +199,11 @@ export function ViewFilterConfigPopover({
|
||||
label: p.name,
|
||||
}));
|
||||
|
||||
const [unSaved, setUnSaved] = useState(false)
|
||||
const [draft, setDraft] = useState<FilterCondition | null>(null);
|
||||
const [draftConditions, setDraftConditions] =
|
||||
useState<FilterCondition[]>(conditions);
|
||||
|
||||
useEffect(() => {
|
||||
if (opened) {
|
||||
setDraftConditions(conditions);
|
||||
setDraft(null);
|
||||
setUnSaved(false)
|
||||
}
|
||||
}, [opened, conditions]);
|
||||
if (!opened) setDraft(null);
|
||||
}, [opened]);
|
||||
|
||||
const handleStartDraft = useCallback(() => {
|
||||
const firstProperty = properties[0];
|
||||
@@ -257,21 +216,14 @@ export function ViewFilterConfigPopover({
|
||||
}, [properties]);
|
||||
|
||||
const handleSaveDraft = useCallback(() => {
|
||||
const nextConditions = draft
|
||||
? [...draftConditions, draft]
|
||||
: draftConditions;
|
||||
|
||||
onChange(nextConditions);
|
||||
if (!draft) return;
|
||||
onChange([...conditions, draft]);
|
||||
setDraft(null);
|
||||
setUnSaved(false)
|
||||
|
||||
}, [draft, draftConditions, onChange]);
|
||||
}, [draft, conditions, onChange]);
|
||||
|
||||
const handleCancelDraft = useCallback(() => {
|
||||
setDraftConditions(conditions)
|
||||
setDraft(null);
|
||||
setUnSaved(false)
|
||||
}, [conditions]);
|
||||
}, []);
|
||||
|
||||
const handleDraftPropertyChange = useCallback(
|
||||
(propertyId: string | null) => {
|
||||
@@ -320,19 +272,17 @@ export function ViewFilterConfigPopover({
|
||||
|
||||
const handleRemove = useCallback(
|
||||
(index: number) => {
|
||||
setUnSaved(true);
|
||||
setDraftConditions((current) => current.filter((_, i) => i !== index));
|
||||
onChange(conditions.filter((_, i) => i !== index));
|
||||
},
|
||||
[],
|
||||
[conditions, onChange],
|
||||
);
|
||||
|
||||
const handlePropertyChange = useCallback(
|
||||
(index: number, propertyId: string | null) => {
|
||||
if (!propertyId) return;
|
||||
const newProperty = properties.find((p) => p.id === propertyId);
|
||||
setUnSaved(true)
|
||||
setDraftConditions((current) =>
|
||||
current.map((f, i) => {
|
||||
onChange(
|
||||
conditions.map((f, i) => {
|
||||
if (i !== index) return f;
|
||||
if (newProperty) {
|
||||
const validOperators = getOperatorsForType(newProperty.type);
|
||||
@@ -352,16 +302,15 @@ export function ViewFilterConfigPopover({
|
||||
}),
|
||||
);
|
||||
},
|
||||
[properties],
|
||||
[conditions, properties, onChange],
|
||||
);
|
||||
|
||||
const handleOperatorChange = useCallback(
|
||||
(index: number, operator: string | null) => {
|
||||
if (!operator) return;
|
||||
const op = operator as FilterOperator;
|
||||
setUnSaved(true)
|
||||
setDraftConditions((current) =>
|
||||
current.map((f, i) => {
|
||||
onChange(
|
||||
conditions.map((f, i) => {
|
||||
if (i !== index) return f;
|
||||
const kind = inputKindForProperty(
|
||||
properties.find((p) => p.id === f.propertyId),
|
||||
@@ -371,17 +320,16 @@ export function ViewFilterConfigPopover({
|
||||
}),
|
||||
);
|
||||
},
|
||||
[properties],
|
||||
[conditions, properties, onChange],
|
||||
);
|
||||
|
||||
const handleValueChange = useCallback(
|
||||
(index: number, value: unknown) => {
|
||||
setUnSaved(true)
|
||||
setDraftConditions((current) =>
|
||||
current.map((f, i) => (i === index ? { ...f, value } : f)),
|
||||
onChange(
|
||||
conditions.map((f, i) => (i === index ? { ...f, value } : f)),
|
||||
);
|
||||
},
|
||||
[],
|
||||
[conditions, onChange],
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -414,13 +362,13 @@ export function ViewFilterConfigPopover({
|
||||
{t("Filter by")}
|
||||
</Text>
|
||||
|
||||
{draftConditions.length === 0 && !draft && (
|
||||
{conditions.length === 0 && !draft && (
|
||||
<Text size="xs" c="dimmed">
|
||||
{t("No filters applied")}
|
||||
</Text>
|
||||
)}
|
||||
|
||||
{draftConditions.map((condition, index) => {
|
||||
{conditions.map((condition, index) => {
|
||||
const needsValue = !NO_VALUE_OPERATORS.includes(condition.op);
|
||||
const property = properties.find(
|
||||
(p) => p.id === condition.propertyId,
|
||||
@@ -523,6 +471,14 @@ export function ViewFilterConfigPopover({
|
||||
/>
|
||||
)}
|
||||
</Group>
|
||||
<Group justify="flex-end" gap="xs">
|
||||
<Button variant="default" size="xs" onClick={handleCancelDraft}>
|
||||
{t("Cancel")}
|
||||
</Button>
|
||||
<Button size="xs" onClick={handleSaveDraft}>
|
||||
{t("Save")}
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
);
|
||||
})()}
|
||||
@@ -536,20 +492,6 @@ export function ViewFilterConfigPopover({
|
||||
{t("Add filter")}
|
||||
</UnstyledButton>
|
||||
)}
|
||||
<Group justify="flex-end" gap="xs">
|
||||
<Button
|
||||
variant="default"
|
||||
size="xs"
|
||||
onClick={handleCancelDraft}
|
||||
disabled={!draft && !unSaved}
|
||||
>
|
||||
{t("Cancel")}
|
||||
</Button>
|
||||
|
||||
<Button size="xs" onClick={handleSaveDraft} disabled={!draft && !unSaved}>
|
||||
{t("Save")}
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Popover.Dropdown>
|
||||
</Popover>
|
||||
|
||||
@@ -73,9 +73,13 @@ export const embedProviders: IEmbedProvider[] = [
|
||||
id: "vimeo",
|
||||
name: "Vimeo",
|
||||
regex:
|
||||
/^(https:)?\/\/(?:www\.|player\.)?vimeo.com\/(?:channels\/(?:\w+\/)?|groups\/([^/]*)\/videos\/|album\/(\d+)\/video\/|video\/|)(\d+)/,
|
||||
getEmbedUrl: (match) => {
|
||||
return `https://player.vimeo.com/video/${match[4]}`;
|
||||
/^(https:)?\/\/(?:www\.|player\.)?vimeo.com\/(?:channels\/(?:\w+\/)?|groups\/([^/]*)\/videos\/|album\/(\d+)\/video\/|video\/|)(\d+)(?:\/([\da-zA-Z]+))?/,
|
||||
getEmbedUrl: (match, url: string) => {
|
||||
// preserve ?h= hash for unlisted videos
|
||||
const hash =
|
||||
match[5] ?? new URL(url, "https://vimeo.com").searchParams.get("h");
|
||||
const base = `https://player.vimeo.com/video/${match[4]}`;
|
||||
return hash ? `${base}?h=${hash}` : base;
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user