This commit is contained in:
Rama Priyanto 2025-02-11 13:54:10 +07:00
commit fe5e56d3e5
249 changed files with 8396 additions and 1646 deletions

View File

@ -34,6 +34,9 @@ const FormSchema = z.object({
email: z.string({
required_error: "Required",
}),
position: z.string({
required_error: "Required",
}),
region: z.string({
required_error: "Required",
}),
@ -147,7 +150,28 @@ export default function AddExpertForm() {
</FormItem>
)}
/>
<FormField
control={form.control}
name="position"
render={({ field }) => (
<FormItem>
<FormLabel>Posisi</FormLabel>
<Select onValueChange={field.onChange} value={field.value}>
<FormControl>
<SelectTrigger>
<SelectValue placeholder="Pilih Region" />
</SelectTrigger>
</FormControl>
<SelectContent>
<SelectItem value="koor-kurator">Koor Kurator</SelectItem>
<SelectItem value="kurator">Kurator</SelectItem>
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="region"

View File

@ -0,0 +1,54 @@
import * as React from "react";
import { ColumnDef } from "@tanstack/react-table";
import { Eye, MoreVertical, SquarePen, Trash2 } from "lucide-react";
import { cn } from "@/lib/utils";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuTrigger,
DropdownMenuItem,
} from "@/components/ui/dropdown-menu";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import {
formatDateToIndonesian,
getOnlyDate,
htmlToString,
} from "@/utils/globals";
import { Link, useRouter } from "@/i18n/routing";
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from "@/components/ui/accordion";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import { Collapsible, CollapsibleContent } from "@/components/ui/collapsible";
const columns: ColumnDef<any>[] = [
{
accessorKey: "no",
header: "No",
cell: ({ row }) => <span>{row.getValue("no")}</span>,
},
{
accessorKey: "title",
header: "Nama Media Online",
cell: ({ row }) => <span>{row.getValue("title")}</span>,
},
{
accessorKey: "link",
header: "Link",
cell: ({ row }) => <span>{row.getValue("categoryName")}</span>,
},
];
export default columns;

View File

@ -0,0 +1,302 @@
"use client";
import * as React from "react";
import {
ColumnDef,
ColumnFiltersState,
PaginationState,
SortingState,
VisibilityState,
flexRender,
getCoreRowModel,
getFilteredRowModel,
getPaginationRowModel,
getSortedRowModel,
useReactTable,
} from "@tanstack/react-table";
import { Button } from "@/components/ui/button";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import {
ChevronLeft,
ChevronRight,
Eye,
MoreVertical,
Search,
SquarePen,
Trash2,
TrendingDown,
TrendingUp,
UserIcon,
} from "lucide-react";
import { cn } from "@/lib/utils";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuRadioGroup,
DropdownMenuRadioItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Input } from "@/components/ui/input";
import { InputGroup, InputGroupText } from "@/components/ui/input-group";
import { paginationBlog } from "@/service/blog/blog";
import { ticketingPagination } from "@/service/ticketing/ticketing";
import { Badge } from "@/components/ui/badge";
import { useRouter, useSearchParams } from "next/navigation";
import TablePagination from "@/components/table/table-pagination";
import columns from "./column";
import { getPlanningPagination } from "@/service/agenda-setting/agenda-setting";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import { listDataMedia } from "@/service/broadcast/broadcast";
import { listEnableCategory } from "@/service/content/content";
import { Checkbox } from "@/components/ui/checkbox";
import { close, loading } from "@/config/swal";
import { Link } from "@/i18n/routing";
const MediaOnlineTable = () => {
const router = useRouter();
const searchParams = useSearchParams();
const [search, setSearch] = React.useState("");
const [showData, setShowData] = React.useState("10");
const [categories, setCategories] = React.useState<any>();
const [dataTable, setDataTable] = React.useState<any[]>([]);
const [totalData, setTotalData] = React.useState<number>(1);
const [sorting, setSorting] = React.useState<SortingState>([]);
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
[]
);
const [columnVisibility, setColumnVisibility] =
React.useState<VisibilityState>({});
const [rowSelection, setRowSelection] = React.useState({});
const [pagination, setPagination] = React.useState<PaginationState>({
pageIndex: 0,
pageSize: Number(showData),
});
const [categoryFilter, setCategoryFilter] = React.useState<number[]>([]);
const [statusFilter, setStatusFilter] = React.useState<number[]>([]);
const [page, setPage] = React.useState(1);
const [totalPage, setTotalPage] = React.useState(1);
const table = useReactTable({
data: dataTable,
columns,
onSortingChange: setSorting,
onColumnFiltersChange: setColumnFilters,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
getSortedRowModel: getSortedRowModel(),
getFilteredRowModel: getFilteredRowModel(),
onColumnVisibilityChange: setColumnVisibility,
onRowSelectionChange: setRowSelection,
onPaginationChange: setPagination,
state: {
sorting,
columnFilters,
columnVisibility,
rowSelection,
pagination,
},
});
let typingTimer: any;
const doneTypingInterval = 1500;
// const handleKeyUp = () => {
// clearTimeout(typingTimer);
// typingTimer = setTimeout(doneTyping, doneTypingInterval);
// };
const handleKeyDown = () => {
clearTimeout(typingTimer);
};
// async function doneTyping() {
// fetchData();
// }
React.useEffect(() => {
const pageFromUrl = searchParams?.get("page");
if (pageFromUrl) {
setPage(Number(pageFromUrl));
}
}, [searchParams]);
// React.useEffect(() => {
// fetchData();
// setPagination({
// pageIndex: 0,
// pageSize: Number(showData),
// });
// }, [page, showData]);
// async function fetchData() {
// try {
// loading();
// const res = await listDataMedia(
// page - 1,
// showData,
// search,
// categoryFilter?.sort().join(","),
// statusFilter?.sort().join(",")
// );
// const data = res?.data?.data;
// const contentData = data?.content;
// contentData.forEach((item: any, index: number) => {
// item.no = (page - 1) * Number(showData) + index + 1;
// });
// console.log("contentData : ", data);
// setDataTable(contentData);
// setTotalData(data?.totalElements);
// setTotalPage(data?.totalPages);
// close();
// } catch (error) {
// console.error("Error fetching tasks:", error);
// }
// }
React.useEffect(() => {
getCategories();
}, []);
async function getCategories() {
const category = await listEnableCategory("");
const resCategory = category?.data?.data?.content;
setCategories(resCategory);
}
const handleChange = (type: string, id: number, checked: boolean) => {
if (type === "category") {
if (checked) {
const temp: number[] = [...categoryFilter];
temp.push(id);
setCategoryFilter(temp);
} else {
const temp = categoryFilter.filter((a) => a !== id);
setCategoryFilter(temp);
}
} else {
if (checked) {
const temp: number[] = [...statusFilter];
temp.push(id);
setStatusFilter(temp);
} else {
const temp = statusFilter.filter((a) => a !== id);
setStatusFilter(temp);
}
}
};
return (
<div className="w-full overflow-x-auto bg-white p-4 rounded-sm space-y-3">
<div className="flex justify-between mb-10 items-center">
<p className="text-xl font-medium text-default-900">Media Online</p>
</div>
<div className="flex justify-between ">
<Link href="/admin/broadcast/campaign-list">
<Button color="primary" size="md" className="text-sm mr-3">
Tambah Media Online
</Button>
</Link>
<Input
type="text"
placeholder="Search"
// onKeyUp={handleKeyUp}
onKeyDown={handleKeyDown}
onChange={(e) => setSearch(e.target.value)}
className="max-w-[300px]"
/>
<div className="flex flex-row gap-2">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button size="md" variant="outline">
1 - {showData} Data
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="w-56">
<DropdownMenuRadioGroup
value={showData}
onValueChange={setShowData}
>
<DropdownMenuRadioItem value="10">
1 - 10 Data
</DropdownMenuRadioItem>
<DropdownMenuRadioItem value="20">
1 - 20 Data
</DropdownMenuRadioItem>
<DropdownMenuRadioItem value="25">
1 - 25 Data
</DropdownMenuRadioItem>
<DropdownMenuRadioItem value="50">
1 - 50 Data
</DropdownMenuRadioItem>
</DropdownMenuRadioGroup>
</DropdownMenuContent>
</DropdownMenu>
</div>
</div>
<Table className="overflow-hidden">
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id} className="bg-default-200">
{headerGroup.headers.map((header) => (
<TableHead key={header.id}>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext()
)}
</TableHead>
))}
</TableRow>
))}
</TableHeader>
<TableBody>
{table.getRowModel().rows?.length ? (
table.getRowModel().rows.map((row) => (
<TableRow
key={row.id}
data-state={row.getIsSelected() && "selected"}
className="h-[75px]"
>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</TableCell>
))}
</TableRow>
))
) : (
<TableRow>
<TableCell colSpan={columns.length} className="h-24 text-center">
No results.
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
<TablePagination
table={table}
totalData={totalData}
totalPage={totalPage}
/>
</div>
);
};
export default MediaOnlineTable;

View File

@ -0,0 +1,11 @@
import SiteBreadcrumb from "@/components/site-breadcrumb";
import MediaOnlineTable from "./component/table";
export default function AdminMediaOnline() {
return (
<div>
<SiteBreadcrumb />
<MediaOnlineTable />
</div>
);
}

View File

@ -0,0 +1,58 @@
import * as React from "react";
import { ColumnDef } from "@tanstack/react-table";
import { Eye, MoreVertical, SquarePen, Trash2 } from "lucide-react";
import { cn } from "@/lib/utils";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuTrigger,
DropdownMenuItem,
} from "@/components/ui/dropdown-menu";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import {
formatDateToIndonesian,
getOnlyDate,
htmlToString,
} from "@/utils/globals";
import { Link, useRouter } from "@/i18n/routing";
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from "@/components/ui/accordion";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import { Collapsible, CollapsibleContent } from "@/components/ui/collapsible";
const columns: ColumnDef<any>[] = [
{
accessorKey: "no",
header: "No",
cell: ({ row }) => <span>{row.getValue("no")}</span>,
},
{
accessorKey: "date",
header: "Tanggal",
cell: ({ row }) => <span>{row.getValue("categoryName")}</span>,
},
{
accessorKey: "title",
header: "Media Online",
cell: ({ row }) => <span>{row.getValue("title")}</span>,
},
{
accessorKey: "link",
header: "Link Berita",
cell: ({ row }) => <span>{row.getValue("categoryName")}</span>,
},
];
export default columns;

View File

@ -0,0 +1,305 @@
"use client";
import * as React from "react";
import {
ColumnDef,
ColumnFiltersState,
PaginationState,
SortingState,
VisibilityState,
flexRender,
getCoreRowModel,
getFilteredRowModel,
getPaginationRowModel,
getSortedRowModel,
useReactTable,
} from "@tanstack/react-table";
import { Button } from "@/components/ui/button";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import {
ChevronLeft,
ChevronRight,
Eye,
MoreVertical,
Search,
SquarePen,
Trash2,
TrendingDown,
TrendingUp,
UserIcon,
} from "lucide-react";
import { cn } from "@/lib/utils";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuRadioGroup,
DropdownMenuRadioItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Input } from "@/components/ui/input";
import { InputGroup, InputGroupText } from "@/components/ui/input-group";
import { paginationBlog } from "@/service/blog/blog";
import { ticketingPagination } from "@/service/ticketing/ticketing";
import { Badge } from "@/components/ui/badge";
import { useRouter, useSearchParams } from "next/navigation";
import TablePagination from "@/components/table/table-pagination";
import columns from "./column";
import { getPlanningPagination } from "@/service/agenda-setting/agenda-setting";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import { listDataMedia } from "@/service/broadcast/broadcast";
import { listEnableCategory } from "@/service/content/content";
import { Checkbox } from "@/components/ui/checkbox";
import { close, loading } from "@/config/swal";
import { Link } from "@/i18n/routing";
import { Label } from "@/components/ui/label";
const NewsTable = () => {
const router = useRouter();
const searchParams = useSearchParams();
const [search, setSearch] = React.useState("");
const [showData, setShowData] = React.useState("10");
const [categories, setCategories] = React.useState<any>();
const [dataTable, setDataTable] = React.useState<any[]>([]);
const [totalData, setTotalData] = React.useState<number>(1);
const [sorting, setSorting] = React.useState<SortingState>([]);
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
[]
);
const [showTable, setShowTable] = React.useState(false);
const [columnVisibility, setColumnVisibility] =
React.useState<VisibilityState>({});
const [rowSelection, setRowSelection] = React.useState({});
const [pagination, setPagination] = React.useState<PaginationState>({
pageIndex: 0,
pageSize: Number(showData),
});
const [categoryFilter, setCategoryFilter] = React.useState<number[]>([]);
const [statusFilter, setStatusFilter] = React.useState<number[]>([]);
const [page, setPage] = React.useState(1);
const [totalPage, setTotalPage] = React.useState(1);
const table = useReactTable({
data: dataTable,
columns,
onSortingChange: setSorting,
onColumnFiltersChange: setColumnFilters,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
getSortedRowModel: getSortedRowModel(),
getFilteredRowModel: getFilteredRowModel(),
onColumnVisibilityChange: setColumnVisibility,
onRowSelectionChange: setRowSelection,
onPaginationChange: setPagination,
state: {
sorting,
columnFilters,
columnVisibility,
rowSelection,
pagination,
},
});
let typingTimer: any;
const doneTypingInterval = 1500;
// const handleKeyUp = () => {
// clearTimeout(typingTimer);
// typingTimer = setTimeout(doneTyping, doneTypingInterval);
// };
const handleKeyDown = () => {
clearTimeout(typingTimer);
};
// async function doneTyping() {
// fetchData();
// }
React.useEffect(() => {
const pageFromUrl = searchParams?.get("page");
if (pageFromUrl) {
setPage(Number(pageFromUrl));
}
}, [searchParams]);
// React.useEffect(() => {
// fetchData();
// setPagination({
// pageIndex: 0,
// pageSize: Number(showData),
// });
// }, [page, showData]);
// async function fetchData() {
// try {
// loading();
// const res = await listDataMedia(
// page - 1,
// showData,
// search,
// categoryFilter?.sort().join(","),
// statusFilter?.sort().join(",")
// );
// const data = res?.data?.data;
// const contentData = data?.content;
// contentData.forEach((item: any, index: number) => {
// item.no = (page - 1) * Number(showData) + index + 1;
// });
// console.log("contentData : ", data);
// setDataTable(contentData);
// setTotalData(data?.totalElements);
// setTotalPage(data?.totalPages);
// close();
// } catch (error) {
// console.error("Error fetching tasks:", error);
// }
// }
React.useEffect(() => {
getCategories();
}, []);
async function getCategories() {
const category = await listEnableCategory("");
const resCategory = category?.data?.data?.content;
setCategories(resCategory);
}
const handleChange = (type: string, id: number, checked: boolean) => {
if (type === "category") {
if (checked) {
const temp: number[] = [...categoryFilter];
temp.push(id);
setCategoryFilter(temp);
} else {
const temp = categoryFilter.filter((a) => a !== id);
setCategoryFilter(temp);
}
} else {
if (checked) {
const temp: number[] = [...statusFilter];
temp.push(id);
setStatusFilter(temp);
} else {
const temp = statusFilter.filter((a) => a !== id);
setStatusFilter(temp);
}
}
};
return (
<div className="w-full overflow-x-auto bg-white p-4 rounded-sm space-y-3 border">
<div className="flex justify-between mb-10 items-center">
<p className="text-xl font-medium text-default-900">
Tracking Berita hari ini!
</p>
</div>
<div>
<Label>
Masukan Link <span className="text-red-500">*</span>
</Label>
<Input></Input>
<p className="text-sm">Sisa kuota harian: 30 Artikel</p>
</div>
{!showTable && (
<div className="flex justify-end">
<Button
color="primary"
size="md"
className="text-sm mr-3"
variant="outline"
>
Batal
</Button>
<Button
color="primary"
size="md"
className="text-sm mr-3"
onClick={() => setShowTable(true)}
>
Tracking Berita
</Button>
</div>
)}
{showTable && (
<>
<Table className="overflow-hidden mt-4">
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id} className="bg-default-200">
{headerGroup.headers.map((header) => (
<TableHead key={header.id}>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext()
)}
</TableHead>
))}
</TableRow>
))}
</TableHeader>
<TableBody>
{table.getRowModel().rows?.length ? (
table.getRowModel().rows.map((row) => (
<TableRow key={row.id} className="h-[75px]">
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
{flexRender(
cell.column.columnDef.cell,
cell.getContext()
)}
</TableCell>
))}
</TableRow>
))
) : (
<TableRow>
<TableCell
colSpan={columns.length}
className="h-24 text-center"
>
No results.
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
<TablePagination
table={table}
totalData={totalData}
totalPage={totalPage}
/>
<div className="flex justify-end mt-4">
<Button
color="primary"
size="md"
className="text-sm mr-3"
onClick={() => setShowTable(false)}
>
Tracking Berita Baru
</Button>
</div>
</>
)}
</div>
);
};
export default NewsTable;

View File

@ -0,0 +1,11 @@
import SiteBreadcrumb from "@/components/site-breadcrumb";
import NewsTable from "./component/table";
export default function AdminNews() {
return (
<div>
<SiteBreadcrumb />
<NewsTable />
</div>
);
}

View File

@ -10,7 +10,15 @@ import { Label } from "@/components/ui/label";
import ExternalDraggingevent from "./dragging-events";
import { Calendar } from "@/components/ui/calendar";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Book, CheckSquare2, CheckSquare2Icon, Plus } from "lucide-react";
import {
Book,
CheckCheck,
CheckSquare2,
CheckSquare2Icon,
Plus,
Timer,
TimerIcon,
} from "lucide-react";
import { Checkbox } from "@/components/ui/checkbox";
import { EventContentArg } from "@fullcalendar/core";
import EventModal from "./event-modal";
@ -46,6 +54,7 @@ export type CalendarEvent = {
end: Date;
createBy: string;
createdByName: string;
isPublish: boolean | null;
allDay: boolean;
extendedProps: {
calendar: string;
@ -94,7 +103,7 @@ interface ListItemProps {
item: any;
text: string;
createdBy: string;
isPublish: boolean;
isPublish: boolean | null;
bgColor: string;
}
@ -176,12 +185,15 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
end: new Date(event.endDate),
allDay: true,
extendedProps: {
isPublish: event.isPublish,
calendar: event.agendaType,
description: event.description,
createdByName: event.createdByName,
},
}));
console.log("Dataaa event : ", events);
setCalendarEvents(events);
}
};
@ -239,6 +251,7 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
title: item.title,
createBy: "Mabes Polri - Approver",
createdByName: item.createdByName,
isPublish: item.isPublish,
start: new Date(item.startDate),
end: new Date(item.endDate),
allDay: true, // Sesuaikan jika memang ada event sepanjang hari
@ -335,14 +348,13 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
const renderEventContent = (eventInfo: any) => {
const { title } = eventInfo.event;
const { isPublish } = eventInfo.event.extendedProps;
const { createdByName } = eventInfo.event.extendedProps;
const { createdByName, isPublish } = eventInfo.event.extendedProps;
return (
<>
<div className="flex flex-row">
{" "}
{isPublish == true && <CheckSquare2 />}
{isPublish === true ? <CheckCheck size={15} /> : <Timer size={15} />}
<p className="ml-1">{title}</p>
</div>
@ -417,6 +429,7 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
end: new Date(item.endDate),
createBy: "Mabes Polri - Approver", // Sesuaikan dengan data yang sebenarnya jika ada
createdByName: item.createdByName,
isPublish: item.isPublish,
allDay: true,
extendedProps: {
calendar: item.agendaType,
@ -447,7 +460,7 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
onClick={() => handleClickListItem(item)}
>
<div className="flex flex-row items-center">
{isPublish == true && <CheckSquare2Icon />}
{isPublish ? <CheckCheck size={15} /> : <Timer size={15} />}
<p className="ml-1">{text}</p>
</div>
<p className="ml-1 text-xs text-start mt-2">Created By: {createdBy}</p>

View File

@ -0,0 +1,18 @@
import SiteBreadcrumb from "@/components/site-breadcrumb";
import FormImageDetail from "@/components/form/content/image-detail-form";
import FormImageUpdate from "@/components/form/content/image-update-form";
import FormAudioUpdate from "@/components/form/content/audio-update-form";
import FormAudioSeo from "@/components/form/content/audio-update-seo";
const AudioUpdatePage = async () => {
return (
<div>
<SiteBreadcrumb />
<div className="space-y-4">
<FormAudioSeo />
</div>
</div>
);
};
export default AudioUpdatePage;

View File

@ -0,0 +1,17 @@
import SiteBreadcrumb from "@/components/site-breadcrumb";
import FormImageDetail from "@/components/form/content/image-detail-form";
import FormImageUpdate from "@/components/form/content/image-update-form";
import FormImageSeo from "@/components/form/content/image-update-seo";
const ImageUpdateSeoPage = async () => {
return (
<div>
<SiteBreadcrumb />
<div className="space-y-4">
<FormImageSeo />
</div>
</div>
);
};
export default ImageUpdateSeoPage;

View File

@ -0,0 +1,16 @@
import SiteBreadcrumb from "@/components/site-breadcrumb";
import FormTeksUpdate from "@/components/form/content/teks-update-form";
import FormTeksSeo from "@/components/form/content/teks-update-seo";
const TeksUpdatePage = async () => {
return (
<div>
<SiteBreadcrumb />
<div className="space-y-4">
<FormTeksSeo />
</div>
</div>
);
};
export default TeksUpdatePage;

View File

@ -0,0 +1,18 @@
import SiteBreadcrumb from "@/components/site-breadcrumb";
import FormImageDetail from "@/components/form/content/image-detail-form";
import FormImageUpdate from "@/components/form/content/image-update-form";
import FormVideoUpdate from "@/components/form/content/video-update-form";
import FormVideoSeo from "@/components/form/content/video-update-seo";
const VideoUpdatePage = async () => {
return (
<div>
<SiteBreadcrumb />
<div className="space-y-4">
<FormVideoSeo />
</div>
</div>
);
};
export default VideoUpdatePage;

View File

@ -26,6 +26,7 @@ import {
} from "@/components/ui/table";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import {
ChevronDown,
ChevronLeft,
ChevronRight,
Eye,
@ -52,6 +53,8 @@ import { useRouter, useSearchParams } from "next/navigation";
import TablePagination from "@/components/table/table-pagination";
import columns from "./columns";
import { listTask } from "@/service/task";
import { Label } from "@/components/ui/label";
import { format } from "date-fns";
const TaskTable = () => {
const router = useRouter();
@ -70,6 +73,10 @@ const TaskTable = () => {
pageIndex: 0,
pageSize: 10,
});
const [statusFilter, setStatusFilter] = React.useState<number[]>([]);
const [dateFilter, setDateFilter] = React.useState("");
const [endDate, setEndDate] = React.useState("");
const [filterByCode, setFilterByCode] = React.useState<string>("");
const [page, setPage] = React.useState(1);
const [totalPage, setTotalPage] = React.useState(1);
const [limit, setLimit] = React.useState(10);
@ -106,18 +113,43 @@ const TaskTable = () => {
React.useEffect(() => {
fetchData();
}, [page, limit, isSpecificAttention, search]);
}, [
page,
limit,
isSpecificAttention,
search,
dateFilter,
filterByCode,
statusFilter,
]);
async function fetchData() {
const formattedStartDate = dateFilter
? format(new Date(dateFilter), "yyyy-MM-dd")
: "";
try {
const res = await listTask(
search,
page - 1,
search,
limit,
isSpecificAttention ? "atensi-khusus" : "tugas-harian"
filterByCode,
formattedStartDate,
isSpecificAttention ? "atensi-khusus" : "tugas-harian",
statusFilter
);
const data = res?.data?.data;
const contentData = data?.content;
// let contentDataFilter = res?.data?.data?.content || [];
// Filter berdasarkan status
// contentDataFilter = contentDataFilter.filter((item: any) => {
// const isSelesai = statusFilter.includes(1) ? item.isDone : true;
// const isAktif = statusFilter.includes(2) ? item.isActive : true;
// return isSelesai && isAktif;
// });
contentData.forEach((item: any, index: number) => {
item.no = (page - 1) * limit + index + 1;
});
@ -133,10 +165,26 @@ const TaskTable = () => {
}
const handleSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
setSearch(e.target.value); // Perbarui state search
table.getColumn("judul")?.setFilterValue(e.target.value); // Set filter tabel
setFilterByCode(e.target.value);
setSearch(e.target.value);
table.getColumn("judul")?.setFilterValue(e.target.value);
};
function handleStatusCheckboxChange(value: number) {
setStatusFilter((prev) =>
prev.includes(value)
? prev.filter((status) => status !== value)
: [...prev, value]
);
}
// const handleSearchFilterByCode = (e: React.ChangeEvent<HTMLInputElement>) => {
// const value = e.target.value;
// console.log("code :", value);
// setFilterByCode(value);
// fetchData();
// };
return (
<div className="w-full overflow-x-auto">
<div className="mx-5 mb-3">
@ -184,24 +232,88 @@ const TaskTable = () => {
</InputGroupText>
<Input
type="text"
placeholder="Search Judul..."
placeholder="Search Judul dan Code"
className="bg-transparent dark:border-secondary dark:placeholder-white/80 dark:focus:border-secondary dark:text-white w-full"
value={search}
onChange={handleSearch}
/>
</InputGroup>
</div>
<div className="flex-none">
<Input
placeholder="Filter Status..."
value={
(table.getColumn("status")?.getFilterValue() as string) ?? ""
}
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
table.getColumn("status")?.setFilterValue(event.target.value)
}
className="max-w-sm "
/>
<div className="flex flex-row items-center gap-2">
<div className="flex flex-row items-center gap-3">
<div className="flex items-center py-4">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline" className="ml-auto" size="md">
Filter <ChevronDown />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent
align="end"
className="w-64 h-[200px] overflow-y-auto"
>
<div className="flex flex-row justify-between my-1 mx-1">
<p>Filter</p>
</div>
<div className="mx-2 my-1">
<Label>Tanggal Awal</Label>
<Input
type="date"
value={dateFilter}
onChange={(e) => setDateFilter(e.target.value)}
className="max-w-sm"
/>
</div>
{/* <div className="mx-2 my-1">
<Label>Code</Label>
<Input
placeholder="Filter Status..."
value={filterByCode}
// onChange={handleSearchFilterByCode}
className="max-w-sm"
/>
</div> */}
<Label className="ml-2 mt-2">Status</Label>
<div className="flex items-center px-4 py-1">
<input
type="checkbox"
id="status-1"
className="mr-2"
checked={statusFilter.includes(1)}
onChange={() => handleStatusCheckboxChange(1)}
/>
<label htmlFor="status-1" className="text-sm">
Selesai
</label>
</div>
<div className="flex items-center px-4 py-1">
<input
type="checkbox"
id="status-2"
className="mr-2"
checked={statusFilter.includes(2)}
onChange={() => handleStatusCheckboxChange(2)}
/>
<label htmlFor="status-2" className="text-sm">
Aktif
</label>
</div>
</DropdownMenuContent>
</DropdownMenu>
</div>
</div>
{/* <div className="flex-none">
<Input
placeholder="Filter Status..."
value={
(table.getColumn("status")?.getFilterValue() as string) ?? ""
}
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
table.getColumn("status")?.setFilterValue(event.target.value)
}
className="max-w-sm "
/>
</div> */}
</div>
</div>
<Table className="overflow-hidden mt-3">

View File

@ -53,6 +53,7 @@ import { error, loading } from "@/config/swal";
import { Item } from "@radix-ui/react-dropdown-menu";
import dynamic from "next/dynamic";
import { getCsrfToken } from "@/service/auth";
import { Link } from "@/i18n/routing";
interface FileWithPreview extends File {
preview: string;
@ -921,7 +922,7 @@ export default function FormAudio() {
}`}
onClick={() => handleArticleIdClick(id)}
>
{id}
{"Narasi " + (index + 1)}
</p>
))}
</div>
@ -930,18 +931,8 @@ export default function FormAudio() {
<div className="pt-3">
<div className="flex flex-row justify-between items-center">
{selectedArticleId && (
<a
href={`/admin/media/${
fileTypeId === "1"
? "image"
: fileTypeId === "2"
? "video"
: fileTypeId === "3"
? "text"
: "audio"
}/update-new/${selectedArticleId}`}
target="_blank"
rel="noopener noreferrer"
<Link
href={`/contributor/content/audio/update-seo/${selectedArticleId}`}
>
<Button
className="mb-2"
@ -951,7 +942,7 @@ export default function FormAudio() {
>
Edit
</Button>
</a>
</Link>
)}
</div>
</div>

View File

@ -0,0 +1,757 @@
"use client";
import React, {
ChangeEvent,
Fragment,
useEffect,
useRef,
useState,
} from "react";
import { useForm, Controller } from "react-hook-form";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import { Label } from "@/components/ui/label";
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { zodResolver } from "@hookform/resolvers/zod";
import * as z from "zod";
import Swal from "sweetalert2";
import withReactContent from "sweetalert2-react-content";
import { useParams, useRouter } from "next/navigation";
import Cookies from "js-cookie";
import {
createMedia,
deleteFile,
deleteMedia,
getTagsBySubCategoryId,
listEnableCategory,
uploadThumbnail,
} from "@/service/content/content";
import { detailMedia } from "@/service/curated-content/curated-content";
import { Badge } from "@/components/ui/badge";
import { CloudUpload, MailIcon, PieChart, XIcon } from "lucide-react";
import dynamic from "next/dynamic";
import { useDropzone } from "react-dropzone";
import { Icon } from "@iconify/react/dist/iconify.js";
import Image from "next/image";
import { error, loading } from "@/lib/swal";
import { getCsrfToken } from "@/service/auth";
import { Upload } from "tus-js-client";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { Textarea } from "@/components/ui/textarea";
import ViewEditor from "@/components/editor/view-editor";
import { getDetailArticle, getSeoScore } from "@/service/content/ai";
import { Gauge } from "@mui/x-charts/Gauge";
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from "@/components/ui/accordion";
import { Pie } from "react-chartjs-2";
import {
Chart as ChartJS,
ArcElement,
Tooltip,
Legend,
ChartOptions,
} from "chart.js";
ChartJS.register(ArcElement, Tooltip, Legend);
const imageSchema = z.object({
title: z.string().min(1, { message: "Judul diperlukan" }),
description: z
.string()
.min(2, { message: "Narasi Penugasan harus lebih dari 2 karakter." }),
creatorName: z.string().min(1, { message: "Creator diperlukan" }),
// tags: z.string().min(1, { message: "Judul diperlukan" }),
});
type Category = {
id: string;
name: string;
};
type Detail = {
id: string;
title: string;
description: string;
slug: string;
category: {
id: string;
name: string;
};
publishedFor: string;
publishedForObject: {
id: number;
name: string;
};
htmlDescription: string;
creatorName: string;
categoryName: string;
thumbnailLink: string;
tags: string;
};
type Option = {
id: string;
name: string;
};
const CustomEditor = dynamic(
() => {
return import("@/components/editor/custom-editor");
},
{ ssr: false }
);
interface FileWithPreview extends File {
preview: string;
}
export default function FormAudioSeo() {
const MySwal = withReactContent(Swal);
const router = useRouter();
const { id } = useParams() as { id: string };
console.log(id);
const editor = useRef(null);
type ImageSchema = z.infer<typeof imageSchema>;
let progressInfo: any = [];
let counterUpdateProgress = 0;
const [progressList, setProgressList] = useState<any>([]);
let uploadPersen = 0;
const [isStartUpload, setIsStartUpload] = useState(false);
const [counterProgress, setCounterProgress] = useState(0);
const [selectedFiles, setSelectedFiles] = useState<File[]>([]);
const taskId = Cookies.get("taskId");
const scheduleId = Cookies.get("scheduleId");
const scheduleType = Cookies.get("scheduleType");
const [categories, setCategories] = useState<Category[]>([]);
const [selectedCategory, setSelectedCategory] = useState<any>();
const [tags, setTags] = useState<any[]>([]);
const [detail, setDetail] = useState<Detail>();
const [refresh, setRefresh] = useState(false);
const [selectedPublishers, setSelectedPublishers] = useState<number[]>([]);
const [articleBody, setArticleBody] = useState<string>("");
const [files, setFiles] = useState<FileWithPreview[]>([]);
const [filesTemp, setFilesTemp] = useState<File[]>([]);
const [publishedFor, setPublishedFor] = useState<string[]>([]);
const inputRef = useRef<HTMLInputElement>(null);
const [selectedOptions, setSelectedOptions] = useState<{
[fileId: number]: string;
}>({});
const [articleData, setArticleData] = useState({
title: "",
mainKeyword: "",
additionalKeywords: "",
metaTitle: "",
metaDescription: "",
});
const [totalScoreSEO, setTotalScoreSEO] = useState<number>(0);
const [errorSEO, setErrorSEO] = useState<string[]>([]);
const [warningSEO, setWarningSEO] = useState<string[]>([]);
const [optimizedSEO, setOptimizedSEO] = useState<string[]>([]);
// const [errorData, setErrorData] = useState<string[]>([]);
const [warningData, setWarningData] = useState<string[]>([]);
const [optimizedData, setOptimizedData] = useState<string[]>([]);
const [errorsData, setErrorData] = useState<string[]>([]);
const [selectedTarget, setSelectedTarget] = useState<string | undefined>(
detail?.category.id
);
const [unitSelection, setUnitSelection] = useState({
allUnit: false,
mabes: false,
polda: false,
polres: false,
});
let fileTypeId = "1";
const { getRootProps, getInputProps } = useDropzone({
onDrop: (acceptedFiles) => {
setFiles(acceptedFiles.map((file) => Object.assign(file)));
},
});
const {
control,
handleSubmit,
setValue,
formState: { errors },
} = useForm<ImageSchema>({
resolver: zodResolver(imageSchema),
});
// const handleKeyDown = (e: any) => {
// const newTag = e.target.value.trim(); // Ambil nilai input
// if (e.key === "Enter" && newTag) {
// e.preventDefault(); // Hentikan submit form
// if (!tags.includes(newTag)) {
// setTags((prevTags) => [...prevTags, newTag]); // Tambah tag baru
// setValue("tags", ""); // Kosongkan input
// }
// }
// };
const handleImageChange = (event: ChangeEvent<HTMLInputElement>) => {
if (event.target.files) {
const files = Array.from(event.target.files);
setSelectedFiles((prevImages: any) => [...prevImages, ...files]);
console.log("DATAFILE::", selectedFiles);
}
};
const handleRemoveImage = (index: number) => {
setSelectedFiles((prevImages) => prevImages.filter((_, i) => i !== index));
};
// const handleCheckboxChange = (id: number) => {
// setSelectedPublishers((prev) =>
// prev.includes(id) ? prev.filter((item) => item !== id) : [...prev, id]
// );
// };
useEffect(() => {
async function initState() {
getCategories();
}
initState();
}, []);
const getCategories = async () => {
try {
const category = await listEnableCategory(fileTypeId);
const resCategory: Category[] = category?.data?.data?.content;
setCategories(resCategory);
console.log("data category", resCategory);
if (scheduleId && scheduleType === "3") {
const findCategory = resCategory.find((o) =>
o.name.toLowerCase().includes("pers rilis")
);
if (findCategory) {
// setValue("categoryId", findCategory.id);
setSelectedCategory(findCategory.id); // Set the selected category
const response = await getTagsBySubCategoryId(findCategory.id);
setTags(response?.data?.data);
}
}
} catch (error) {
console.error("Failed to fetch categories:", error);
}
};
useEffect(() => {
const fetchArticleData = async () => {
try {
const response = await getDetailArticle(id);
const data = response?.data?.data;
const cleanArticleBody = data.articleBody.replace(/<img[^>]*>/g, "");
setArticleData({
title: data.title || "",
mainKeyword: data.mainKeyword || "",
additionalKeywords: data.additionalKeywords || "",
metaTitle: data.metaTitle || "",
metaDescription: data.metaDescription || "",
});
setArticleBody(cleanArticleBody || "");
// reset({
// title: data.title,
// mainKeyword: data.mainKeyword,
// additionalKeywords: data.additionalKeywords,
// metaTitle: data.metaTitle,
// metaDescription: data.metaDescription,
// });
setArticleBody(cleanArticleBody || "");
} catch (error) {
console.error("Failed to fetch article data:", error);
}
};
if (id) {
fetchArticleData();
}
}, [id]);
useEffect(() => {
const fetchSeoScore = async () => {
const res = await getSeoScore(id);
if (res.error) {
error(res.message);
return false;
}
setTotalScoreSEO(res.data.data?.seo_analysis?.score || 0);
let errorList = [
...res.data.data?.seo_analysis?.analysis?.keyword_optimization?.error,
...res.data.data?.seo_analysis?.analysis?.content_quality?.error,
];
setErrorSEO(errorList);
let warningList = [
...res.data.data?.seo_analysis?.analysis?.keyword_optimization?.warning,
...res.data.data?.seo_analysis?.analysis?.content_quality?.warning,
];
setWarningSEO(warningList);
let optimizedList = [
...res.data.data?.seo_analysis?.analysis?.keyword_optimization
?.optimized,
...res.data.data?.seo_analysis?.analysis?.content_quality?.optimized,
];
setOptimizedSEO(optimizedList);
setErrorData(errorList);
setWarningData(warningList);
setOptimizedData(optimizedList);
};
fetchSeoScore();
});
const data = {
labels: ["SEO Score (" + totalScoreSEO + "%)"],
datasets: [
{
data: [totalScoreSEO],
backgroundColor: ["#4CAF50"],
hoverBackgroundColor: ["#388E3C"],
},
],
};
const options: ChartOptions<"pie"> = {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: "bottom", // TypeScript now correctly recognizes this as a valid option
},
},
};
const save = async (data: ImageSchema) => {
loading();
const finalTags = tags.join(", ");
const requestData = {
...data,
id: detail?.id,
title: data.title,
description: data.description,
htmlDescription: data.description,
fileTypeId,
categoryId: selectedTarget,
subCategoryId: selectedTarget,
uploadedBy: "2b7c8d83-d298-4b19-9f74-b07924506b58",
statusId: "1",
publishedFor: publishedFor.join(","),
creatorName: data.creatorName,
tags: finalTags,
isYoutube: false,
isInternationalMedia: false,
};
const response = await createMedia(requestData);
console.log("Form Data Submitted:", requestData);
const formMedia = new FormData();
const thumbnail = files[0];
formMedia.append("file", thumbnail);
const responseThumbnail = await uploadThumbnail(id, formMedia);
if (responseThumbnail?.error == true) {
error(responseThumbnail?.message);
return false;
}
const progressInfoArr = [];
for (const item of files) {
progressInfoArr.push({ percentage: 0, fileName: item.name });
}
progressInfo = progressInfoArr;
setIsStartUpload(true);
setProgressList(progressInfoArr);
close();
// showProgress();
files.map(async (item: any, index: number) => {
await uploadResumableFile(
index,
String(id),
item,
fileTypeId == "2" || fileTypeId == "4" ? item.duration : "0"
);
});
MySwal.fire({
title: "Sukses",
text: "Data berhasil disimpan.",
icon: "success",
confirmButtonColor: "#3085d6",
confirmButtonText: "OK",
}).then(() => {
router.push("/en/contributor/content/image");
});
};
async function uploadResumableFile(
idx: number,
id: string,
file: any,
duration: string
) {
console.log(idx, id, file, duration);
// const placements = getPlacement(file.placements);
// console.log("Placementttt: : ", placements);
const resCsrf = await getCsrfToken();
const csrfToken = resCsrf?.data?.token;
console.log("CSRF TOKEN : ", csrfToken);
const headers = {
"X-XSRF-TOKEN": csrfToken,
};
const upload = new Upload(file, {
endpoint: `${process.env.NEXT_PUBLIC_API}/media/file/upload`,
headers: headers,
retryDelays: [0, 3000, 6000, 12_000, 24_000],
chunkSize: 20_000,
metadata: {
mediaid: id,
filename: file.name,
filetype: file.type,
duration,
isWatermark: "true", // hardcode
},
onBeforeRequest: function (req) {
var xhr = req.getUnderlyingObject();
xhr.withCredentials = true;
},
onError: async (e: any) => {
console.log("Error upload :", e);
error(e);
},
onChunkComplete: (
chunkSize: any,
bytesAccepted: any,
bytesTotal: any
) => {
const uploadPersen = Math.floor((bytesAccepted / bytesTotal) * 100);
progressInfo[idx].percentage = uploadPersen;
counterUpdateProgress++;
console.log(counterUpdateProgress);
setProgressList(progressInfo);
setCounterProgress(counterUpdateProgress);
},
onSuccess: async () => {
uploadPersen = 100;
progressInfo[idx].percentage = 100;
counterUpdateProgress++;
setCounterProgress(counterUpdateProgress);
successTodo();
},
});
upload.start();
}
const onSubmit = (data: ImageSchema) => {
MySwal.fire({
title: "Simpan Data",
text: "Apakah Anda yakin ingin menyimpan data ini?",
icon: "warning",
showCancelButton: true,
cancelButtonColor: "#d33",
confirmButtonColor: "#3085d6",
confirmButtonText: "Simpan",
}).then((result) => {
if (result.isConfirmed) {
save(data);
}
});
};
const successSubmit = (redirect: string) => {
MySwal.fire({
title: "Sukses",
text: "Data berhasil disimpan.",
icon: "success",
confirmButtonColor: "#3085d6",
confirmButtonText: "OK",
}).then(() => {
router.push(redirect);
});
};
function successTodo() {
let counter = 0;
for (const element of progressInfo) {
if (element.percentage == 100) {
counter++;
}
}
if (counter == progressInfo.length) {
setIsStartUpload(false);
// hideProgress();
Cookies.remove("idCreate");
successSubmit("/in/contributor/content/image/");
}
}
const handleRemoveFile = (file: FileWithPreview) => {
const uploadedFiles = files;
const filtered = uploadedFiles.filter((i) => i.name !== file.name);
setFiles([...filtered]);
};
function success() {
MySwal.fire({
title: "Sukses",
icon: "success",
confirmButtonColor: "#3085d6",
confirmButtonText: "OK",
}).then((result) => {
if (result.isConfirmed) {
// window.location.reload();
}
});
}
const handleDeleteFile = (id: number) => {
MySwal.fire({
title: "Hapus file",
text: "Apakah Anda yakin ingin menghapus file ini?",
icon: "warning",
showCancelButton: true,
cancelButtonColor: "#3085d6",
confirmButtonColor: "#d33",
confirmButtonText: "Hapus",
}).then((result) => {
if (result.isConfirmed) {
doDelete(id);
}
});
};
async function doDelete(id: number) {
const data = { id };
try {
const response = await deleteFile(data);
if (response?.error) {
error(response.message);
return;
}
// Jika berhasil, hapus file dari state lokal
setFiles((prevFiles: any) =>
prevFiles.filter((file: any) => file.id !== id)
);
success();
} catch (err) {
error("Terjadi kesalahan saat menghapus file");
}
}
return (
<div>
<div className="mx-5 mb-3">
<Card>
<Tabs defaultValue="content" className="">
<TabsList className="grid w-[300px] grid-cols-2 bg-slate-400 my-3 mx-3 ">
<TabsTrigger
value="content"
className="data-[state=active]:text-black text-gray-500 data-[state=active]:rounded-md"
>
Konten
</TabsTrigger>
<TabsTrigger
value="checker"
className="data-[state=active]:text-black text-gray-500 data-[state=active]:rounded-md"
>
Checker
</TabsTrigger>
</TabsList>
<TabsContent value="content">
{articleData !== undefined ? (
<CardContent className="space-y-2 my-3">
<div className="space-y-1">
<Label htmlFor="name">Judul</Label>
<Input id="name" defaultValue={articleData?.title} />
</div>
<div className="flex flex-row gap-3 w-full">
<div className="w-full">
<Label htmlFor="username">Main Keyword</Label>
<Textarea
id="mainKeyword"
value={articleData?.mainKeyword}
/>
</div>
<div className="w-full">
<Label htmlFor="username">Additional Keyword</Label>
<Textarea
id="additionalKeywords"
value={articleData?.additionalKeywords}
/>
</div>
</div>
<div className="flex flex-row gap-3 w-full">
<div className="w-full">
<Label htmlFor="username">Meta Title</Label>
<Textarea id="metaTitle" value={articleData?.metaTitle} />
</div>
<div className="w-full">
<Label htmlFor="username">Meta Description</Label>
<Textarea
id="metaDescription"
value={articleData?.metaDescription}
/>
</div>
</div>
<div className="py-3">
<Label>Article</Label>
<Controller
control={control}
name="description"
render={({ field: { onChange, value } }) => (
<CustomEditor
onChange={onChange}
initialData={articleBody}
/>
)}
/>
{errors.description?.message && (
<p className="text-red-400 text-sm">
{errors.description.message}
</p>
)}
</div>
</CardContent>
) : (
""
)}
</TabsContent>
<TabsContent value="checker">
<CardContent className="space-y-2">
<div className="flex items-start justify-start">
<Pie
data={data}
options={options}
className="text-left flex items-start justify-start"
/>
</div>
<div className="space-y-1">
<Accordion type="single" collapsible className="w-full ">
<AccordionItem
value="error"
className="border border-red-600"
>
<AccordionTrigger>
<div className="flex items-center">
<XIcon className="text-red-600" />
Errors ({errorSEO.length})
</div>
</AccordionTrigger>
<AccordionContent>
{errorSEO.length > 0 ? (
<ul className="list-disc list-inside">
{errorSEO.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
) : (
<p>No errors found.</p>
)}
</AccordionContent>
</AccordionItem>
<AccordionItem
value="warning"
className="border border-yellow-600"
>
<AccordionTrigger>
<div className="flex items-center">
<XIcon className="text-yellow-600" />
Warnings ({warningSEO.length})
</div>
</AccordionTrigger>
<AccordionContent>
{warningSEO.length > 0 ? (
<ul className="list-disc list-inside">
{warningSEO.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
) : (
<p>No warnings found.</p>
)}
</AccordionContent>
</AccordionItem>
<AccordionItem
value="optimized"
className="border border-green-600"
>
<AccordionTrigger>
<div className="flex items-center">
<XIcon className="text-green-600" />
Optimized ({optimizedSEO.length})
</div>
</AccordionTrigger>
<AccordionContent>
{optimizedSEO.length > 0 ? (
<ul className="list-disc list-inside">
{optimizedSEO.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
) : (
<p>No optimizations found.</p>
)}
</AccordionContent>
</AccordionItem>
</Accordion>
</div>
<div className="py-3">
<div className="flex flex-row justify-between items-center mb-3">
<Label>Article</Label>
<Button size="md" className="bg-blue-500">
Select Image From Content Bank
</Button>
</div>
<Controller
control={control}
name="description"
render={({ field: { onChange, value } }) => (
<CustomEditor
onChange={onChange}
initialData={articleBody}
/>
)}
/>
{errors.description?.message && (
<p className="text-red-400 text-sm">
{errors.description.message}
</p>
)}
</div>
</CardContent>
</TabsContent>
</Tabs>
</Card>
</div>
</div>
);
}

View File

@ -55,6 +55,7 @@ import { data } from "jquery";
import { options } from "@fullcalendar/core/preact.js";
import dynamic from "next/dynamic";
import { getCsrfToken } from "@/service/auth";
import { Link } from "@/i18n/routing";
interface FileWithPreview extends File {
preview: string;
@ -930,7 +931,7 @@ export default function FormImage() {
}`}
onClick={() => handleArticleIdClick(id)}
>
{id}
{"Narasi " + (index + 1)}
</p>
))}
</div>
@ -939,18 +940,8 @@ export default function FormImage() {
<div className="pt-3">
<div className="flex flex-row justify-between items-center">
{selectedArticleId && (
<a
href={`/admin/media/${
fileTypeId === "1"
? "image"
: fileTypeId === "2"
? "video"
: fileTypeId === "3"
? "text"
: "audio"
}/update-new/${selectedArticleId}`}
target="_blank"
rel="noopener noreferrer"
<Link
href={`/contributor/content/image/update-seo/${selectedArticleId}`}
>
<Button
className="mb-2"
@ -960,7 +951,7 @@ export default function FormImage() {
>
Edit
</Button>
</a>
</Link>
)}
</div>
</div>

View File

@ -0,0 +1,757 @@
"use client";
import React, {
ChangeEvent,
Fragment,
useEffect,
useRef,
useState,
} from "react";
import { useForm, Controller } from "react-hook-form";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import { Label } from "@/components/ui/label";
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { zodResolver } from "@hookform/resolvers/zod";
import * as z from "zod";
import Swal from "sweetalert2";
import withReactContent from "sweetalert2-react-content";
import { useParams, useRouter } from "next/navigation";
import Cookies from "js-cookie";
import {
createMedia,
deleteFile,
deleteMedia,
getTagsBySubCategoryId,
listEnableCategory,
uploadThumbnail,
} from "@/service/content/content";
import { detailMedia } from "@/service/curated-content/curated-content";
import { Badge } from "@/components/ui/badge";
import { CloudUpload, MailIcon, PieChart, XIcon } from "lucide-react";
import dynamic from "next/dynamic";
import { useDropzone } from "react-dropzone";
import { Icon } from "@iconify/react/dist/iconify.js";
import Image from "next/image";
import { error, loading } from "@/lib/swal";
import { getCsrfToken } from "@/service/auth";
import { Upload } from "tus-js-client";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { Textarea } from "@/components/ui/textarea";
import ViewEditor from "@/components/editor/view-editor";
import { getDetailArticle, getSeoScore } from "@/service/content/ai";
import { Gauge } from "@mui/x-charts/Gauge";
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from "@/components/ui/accordion";
import { Pie } from "react-chartjs-2";
import {
Chart as ChartJS,
ArcElement,
Tooltip,
Legend,
ChartOptions,
} from "chart.js";
ChartJS.register(ArcElement, Tooltip, Legend);
const imageSchema = z.object({
title: z.string().min(1, { message: "Judul diperlukan" }),
description: z
.string()
.min(2, { message: "Narasi Penugasan harus lebih dari 2 karakter." }),
creatorName: z.string().min(1, { message: "Creator diperlukan" }),
// tags: z.string().min(1, { message: "Judul diperlukan" }),
});
type Category = {
id: string;
name: string;
};
type Detail = {
id: string;
title: string;
description: string;
slug: string;
category: {
id: string;
name: string;
};
publishedFor: string;
publishedForObject: {
id: number;
name: string;
};
htmlDescription: string;
creatorName: string;
categoryName: string;
thumbnailLink: string;
tags: string;
};
type Option = {
id: string;
name: string;
};
const CustomEditor = dynamic(
() => {
return import("@/components/editor/custom-editor");
},
{ ssr: false }
);
interface FileWithPreview extends File {
preview: string;
}
export default function FormImageSeo() {
const MySwal = withReactContent(Swal);
const router = useRouter();
const { id } = useParams() as { id: string };
console.log(id);
const editor = useRef(null);
type ImageSchema = z.infer<typeof imageSchema>;
let progressInfo: any = [];
let counterUpdateProgress = 0;
const [progressList, setProgressList] = useState<any>([]);
let uploadPersen = 0;
const [isStartUpload, setIsStartUpload] = useState(false);
const [counterProgress, setCounterProgress] = useState(0);
const [selectedFiles, setSelectedFiles] = useState<File[]>([]);
const taskId = Cookies.get("taskId");
const scheduleId = Cookies.get("scheduleId");
const scheduleType = Cookies.get("scheduleType");
const [categories, setCategories] = useState<Category[]>([]);
const [selectedCategory, setSelectedCategory] = useState<any>();
const [tags, setTags] = useState<any[]>([]);
const [detail, setDetail] = useState<Detail>();
const [refresh, setRefresh] = useState(false);
const [selectedPublishers, setSelectedPublishers] = useState<number[]>([]);
const [articleBody, setArticleBody] = useState<string>("");
const [files, setFiles] = useState<FileWithPreview[]>([]);
const [filesTemp, setFilesTemp] = useState<File[]>([]);
const [publishedFor, setPublishedFor] = useState<string[]>([]);
const inputRef = useRef<HTMLInputElement>(null);
const [selectedOptions, setSelectedOptions] = useState<{
[fileId: number]: string;
}>({});
const [articleData, setArticleData] = useState({
title: "",
mainKeyword: "",
additionalKeywords: "",
metaTitle: "",
metaDescription: "",
});
const [totalScoreSEO, setTotalScoreSEO] = useState<number>(0);
const [errorSEO, setErrorSEO] = useState<string[]>([]);
const [warningSEO, setWarningSEO] = useState<string[]>([]);
const [optimizedSEO, setOptimizedSEO] = useState<string[]>([]);
// const [errorData, setErrorData] = useState<string[]>([]);
const [warningData, setWarningData] = useState<string[]>([]);
const [optimizedData, setOptimizedData] = useState<string[]>([]);
const [errorsData, setErrorData] = useState<string[]>([]);
const [selectedTarget, setSelectedTarget] = useState<string | undefined>(
detail?.category.id
);
const [unitSelection, setUnitSelection] = useState({
allUnit: false,
mabes: false,
polda: false,
polres: false,
});
let fileTypeId = "1";
const { getRootProps, getInputProps } = useDropzone({
onDrop: (acceptedFiles) => {
setFiles(acceptedFiles.map((file) => Object.assign(file)));
},
});
const {
control,
handleSubmit,
setValue,
formState: { errors },
} = useForm<ImageSchema>({
resolver: zodResolver(imageSchema),
});
// const handleKeyDown = (e: any) => {
// const newTag = e.target.value.trim(); // Ambil nilai input
// if (e.key === "Enter" && newTag) {
// e.preventDefault(); // Hentikan submit form
// if (!tags.includes(newTag)) {
// setTags((prevTags) => [...prevTags, newTag]); // Tambah tag baru
// setValue("tags", ""); // Kosongkan input
// }
// }
// };
const handleImageChange = (event: ChangeEvent<HTMLInputElement>) => {
if (event.target.files) {
const files = Array.from(event.target.files);
setSelectedFiles((prevImages: any) => [...prevImages, ...files]);
console.log("DATAFILE::", selectedFiles);
}
};
const handleRemoveImage = (index: number) => {
setSelectedFiles((prevImages) => prevImages.filter((_, i) => i !== index));
};
// const handleCheckboxChange = (id: number) => {
// setSelectedPublishers((prev) =>
// prev.includes(id) ? prev.filter((item) => item !== id) : [...prev, id]
// );
// };
useEffect(() => {
async function initState() {
getCategories();
}
initState();
}, []);
const getCategories = async () => {
try {
const category = await listEnableCategory(fileTypeId);
const resCategory: Category[] = category?.data?.data?.content;
setCategories(resCategory);
console.log("data category", resCategory);
if (scheduleId && scheduleType === "3") {
const findCategory = resCategory.find((o) =>
o.name.toLowerCase().includes("pers rilis")
);
if (findCategory) {
// setValue("categoryId", findCategory.id);
setSelectedCategory(findCategory.id); // Set the selected category
const response = await getTagsBySubCategoryId(findCategory.id);
setTags(response?.data?.data);
}
}
} catch (error) {
console.error("Failed to fetch categories:", error);
}
};
useEffect(() => {
const fetchArticleData = async () => {
try {
const response = await getDetailArticle(id);
const data = response?.data?.data;
const cleanArticleBody = data.articleBody.replace(/<img[^>]*>/g, "");
setArticleData({
title: data.title || "",
mainKeyword: data.mainKeyword || "",
additionalKeywords: data.additionalKeywords || "",
metaTitle: data.metaTitle || "",
metaDescription: data.metaDescription || "",
});
setArticleBody(cleanArticleBody || "");
// reset({
// title: data.title,
// mainKeyword: data.mainKeyword,
// additionalKeywords: data.additionalKeywords,
// metaTitle: data.metaTitle,
// metaDescription: data.metaDescription,
// });
setArticleBody(cleanArticleBody || "");
} catch (error) {
console.error("Failed to fetch article data:", error);
}
};
if (id) {
fetchArticleData();
}
}, [id]);
useEffect(() => {
const fetchSeoScore = async () => {
const res = await getSeoScore(id);
if (res.error) {
error(res.message);
return false;
}
setTotalScoreSEO(res.data.data?.seo_analysis?.score || 0);
let errorList = [
...res.data.data?.seo_analysis?.analysis?.keyword_optimization?.error,
...res.data.data?.seo_analysis?.analysis?.content_quality?.error,
];
setErrorSEO(errorList);
let warningList = [
...res.data.data?.seo_analysis?.analysis?.keyword_optimization?.warning,
...res.data.data?.seo_analysis?.analysis?.content_quality?.warning,
];
setWarningSEO(warningList);
let optimizedList = [
...res.data.data?.seo_analysis?.analysis?.keyword_optimization
?.optimized,
...res.data.data?.seo_analysis?.analysis?.content_quality?.optimized,
];
setOptimizedSEO(optimizedList);
setErrorData(errorList);
setWarningData(warningList);
setOptimizedData(optimizedList);
};
fetchSeoScore();
});
const data = {
labels: ["SEO Score (" + totalScoreSEO + "%)"],
datasets: [
{
data: [totalScoreSEO],
backgroundColor: ["#4CAF50"],
hoverBackgroundColor: ["#388E3C"],
},
],
};
const options: ChartOptions<"pie"> = {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: "bottom", // TypeScript now correctly recognizes this as a valid option
},
},
};
const save = async (data: ImageSchema) => {
loading();
const finalTags = tags.join(", ");
const requestData = {
...data,
id: detail?.id,
title: data.title,
description: data.description,
htmlDescription: data.description,
fileTypeId,
categoryId: selectedTarget,
subCategoryId: selectedTarget,
uploadedBy: "2b7c8d83-d298-4b19-9f74-b07924506b58",
statusId: "1",
publishedFor: publishedFor.join(","),
creatorName: data.creatorName,
tags: finalTags,
isYoutube: false,
isInternationalMedia: false,
};
const response = await createMedia(requestData);
console.log("Form Data Submitted:", requestData);
const formMedia = new FormData();
const thumbnail = files[0];
formMedia.append("file", thumbnail);
const responseThumbnail = await uploadThumbnail(id, formMedia);
if (responseThumbnail?.error == true) {
error(responseThumbnail?.message);
return false;
}
const progressInfoArr = [];
for (const item of files) {
progressInfoArr.push({ percentage: 0, fileName: item.name });
}
progressInfo = progressInfoArr;
setIsStartUpload(true);
setProgressList(progressInfoArr);
close();
// showProgress();
files.map(async (item: any, index: number) => {
await uploadResumableFile(
index,
String(id),
item,
fileTypeId == "2" || fileTypeId == "4" ? item.duration : "0"
);
});
MySwal.fire({
title: "Sukses",
text: "Data berhasil disimpan.",
icon: "success",
confirmButtonColor: "#3085d6",
confirmButtonText: "OK",
}).then(() => {
router.push("/en/contributor/content/image");
});
};
async function uploadResumableFile(
idx: number,
id: string,
file: any,
duration: string
) {
console.log(idx, id, file, duration);
// const placements = getPlacement(file.placements);
// console.log("Placementttt: : ", placements);
const resCsrf = await getCsrfToken();
const csrfToken = resCsrf?.data?.token;
console.log("CSRF TOKEN : ", csrfToken);
const headers = {
"X-XSRF-TOKEN": csrfToken,
};
const upload = new Upload(file, {
endpoint: `${process.env.NEXT_PUBLIC_API}/media/file/upload`,
headers: headers,
retryDelays: [0, 3000, 6000, 12_000, 24_000],
chunkSize: 20_000,
metadata: {
mediaid: id,
filename: file.name,
filetype: file.type,
duration,
isWatermark: "true", // hardcode
},
onBeforeRequest: function (req) {
var xhr = req.getUnderlyingObject();
xhr.withCredentials = true;
},
onError: async (e: any) => {
console.log("Error upload :", e);
error(e);
},
onChunkComplete: (
chunkSize: any,
bytesAccepted: any,
bytesTotal: any
) => {
const uploadPersen = Math.floor((bytesAccepted / bytesTotal) * 100);
progressInfo[idx].percentage = uploadPersen;
counterUpdateProgress++;
console.log(counterUpdateProgress);
setProgressList(progressInfo);
setCounterProgress(counterUpdateProgress);
},
onSuccess: async () => {
uploadPersen = 100;
progressInfo[idx].percentage = 100;
counterUpdateProgress++;
setCounterProgress(counterUpdateProgress);
successTodo();
},
});
upload.start();
}
const onSubmit = (data: ImageSchema) => {
MySwal.fire({
title: "Simpan Data",
text: "Apakah Anda yakin ingin menyimpan data ini?",
icon: "warning",
showCancelButton: true,
cancelButtonColor: "#d33",
confirmButtonColor: "#3085d6",
confirmButtonText: "Simpan",
}).then((result) => {
if (result.isConfirmed) {
save(data);
}
});
};
const successSubmit = (redirect: string) => {
MySwal.fire({
title: "Sukses",
text: "Data berhasil disimpan.",
icon: "success",
confirmButtonColor: "#3085d6",
confirmButtonText: "OK",
}).then(() => {
router.push(redirect);
});
};
function successTodo() {
let counter = 0;
for (const element of progressInfo) {
if (element.percentage == 100) {
counter++;
}
}
if (counter == progressInfo.length) {
setIsStartUpload(false);
// hideProgress();
Cookies.remove("idCreate");
successSubmit("/in/contributor/content/image/");
}
}
const handleRemoveFile = (file: FileWithPreview) => {
const uploadedFiles = files;
const filtered = uploadedFiles.filter((i) => i.name !== file.name);
setFiles([...filtered]);
};
function success() {
MySwal.fire({
title: "Sukses",
icon: "success",
confirmButtonColor: "#3085d6",
confirmButtonText: "OK",
}).then((result) => {
if (result.isConfirmed) {
// window.location.reload();
}
});
}
const handleDeleteFile = (id: number) => {
MySwal.fire({
title: "Hapus file",
text: "Apakah Anda yakin ingin menghapus file ini?",
icon: "warning",
showCancelButton: true,
cancelButtonColor: "#3085d6",
confirmButtonColor: "#d33",
confirmButtonText: "Hapus",
}).then((result) => {
if (result.isConfirmed) {
doDelete(id);
}
});
};
async function doDelete(id: number) {
const data = { id };
try {
const response = await deleteFile(data);
if (response?.error) {
error(response.message);
return;
}
// Jika berhasil, hapus file dari state lokal
setFiles((prevFiles: any) =>
prevFiles.filter((file: any) => file.id !== id)
);
success();
} catch (err) {
error("Terjadi kesalahan saat menghapus file");
}
}
return (
<div>
<div className="mx-5 mb-3">
<Card>
<Tabs defaultValue="content" className="">
<TabsList className="grid w-[300px] grid-cols-2 bg-slate-400 my-3 mx-3 ">
<TabsTrigger
value="content"
className="data-[state=active]:text-black text-gray-500 data-[state=active]:rounded-md"
>
Konten
</TabsTrigger>
<TabsTrigger
value="checker"
className="data-[state=active]:text-black text-gray-500 data-[state=active]:rounded-md"
>
Checker
</TabsTrigger>
</TabsList>
<TabsContent value="content">
{articleData !== undefined ? (
<CardContent className="space-y-2 my-3">
<div className="space-y-1">
<Label htmlFor="name">Judul</Label>
<Input id="name" defaultValue={articleData?.title} />
</div>
<div className="flex flex-row gap-3 w-full">
<div className="w-full">
<Label htmlFor="username">Main Keyword</Label>
<Textarea
id="mainKeyword"
value={articleData?.mainKeyword}
/>
</div>
<div className="w-full">
<Label htmlFor="username">Additional Keyword</Label>
<Textarea
id="additionalKeywords"
value={articleData?.additionalKeywords}
/>
</div>
</div>
<div className="flex flex-row gap-3 w-full">
<div className="w-full">
<Label htmlFor="username">Meta Title</Label>
<Textarea id="metaTitle" value={articleData?.metaTitle} />
</div>
<div className="w-full">
<Label htmlFor="username">Meta Description</Label>
<Textarea
id="metaDescription"
value={articleData?.metaDescription}
/>
</div>
</div>
<div className="py-3">
<Label>Article</Label>
<Controller
control={control}
name="description"
render={({ field: { onChange, value } }) => (
<CustomEditor
onChange={onChange}
initialData={articleBody}
/>
)}
/>
{errors.description?.message && (
<p className="text-red-400 text-sm">
{errors.description.message}
</p>
)}
</div>
</CardContent>
) : (
""
)}
</TabsContent>
<TabsContent value="checker">
<CardContent className="space-y-2">
<div className="flex items-start justify-start">
<Pie
data={data}
options={options}
className="text-left flex items-start justify-start"
/>
</div>
<div className="space-y-1">
<Accordion type="single" collapsible className="w-full ">
<AccordionItem
value="error"
className="border border-red-600"
>
<AccordionTrigger>
<div className="flex items-center">
<XIcon className="text-red-600" />
Errors ({errorSEO.length})
</div>
</AccordionTrigger>
<AccordionContent>
{errorSEO.length > 0 ? (
<ul className="list-disc list-inside">
{errorSEO.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
) : (
<p>No errors found.</p>
)}
</AccordionContent>
</AccordionItem>
<AccordionItem
value="warning"
className="border border-yellow-600"
>
<AccordionTrigger>
<div className="flex items-center">
<XIcon className="text-yellow-600" />
Warnings ({warningSEO.length})
</div>
</AccordionTrigger>
<AccordionContent>
{warningSEO.length > 0 ? (
<ul className="list-disc list-inside">
{warningSEO.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
) : (
<p>No warnings found.</p>
)}
</AccordionContent>
</AccordionItem>
<AccordionItem
value="optimized"
className="border border-green-600"
>
<AccordionTrigger>
<div className="flex items-center">
<XIcon className="text-green-600" />
Optimized ({optimizedSEO.length})
</div>
</AccordionTrigger>
<AccordionContent>
{optimizedSEO.length > 0 ? (
<ul className="list-disc list-inside">
{optimizedSEO.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
) : (
<p>No optimizations found.</p>
)}
</AccordionContent>
</AccordionItem>
</Accordion>
</div>
<div className="py-3">
<div className="flex flex-row justify-between items-center mb-3">
<Label>Article</Label>
<Button size="md" className="bg-blue-500">
Select Image From Content Bank
</Button>
</div>
<Controller
control={control}
name="description"
render={({ field: { onChange, value } }) => (
<CustomEditor
onChange={onChange}
initialData={articleBody}
/>
)}
/>
{errors.description?.message && (
<p className="text-red-400 text-sm">
{errors.description.message}
</p>
)}
</div>
</CardContent>
</TabsContent>
</Tabs>
</Card>
</div>
</div>
);
}

View File

@ -26,6 +26,7 @@ import Cookies from "js-cookie";
import {
convertSPIT,
createMedia,
deleteSPIT,
detailSPIT,
getTagsBySubCategoryId,
listCategory,
@ -47,6 +48,7 @@ import { request } from "http";
import { generateDataArticle, getDetailArticle } from "@/service/content/ai";
import { getCookiesDecrypt } from "@/lib/utils";
import dynamic from "next/dynamic";
import { error } from "@/lib/swal";
const imageSchema = z.object({
contentTitle: z.string().min(1, { message: "Judul diperlukan" }),
@ -538,6 +540,49 @@ export default function FormConvertSPIT() {
}
};
function deleteSpitContent() {
MySwal.fire({
title: "Apakah anda ingin menghapus konten?",
showCancelButton: true,
confirmButtonColor: "#dc3545",
confirmButtonText: "Iya",
cancelButtonText: "Tidak",
}).then((result) => {
if (result.isConfirmed) {
doDeleteSPIT();
}
});
}
async function doDeleteSPIT() {
const response = await deleteSPIT(id);
if (response?.error) {
error(response.message);
return false;
}
successBack();
}
function successBack() {
MySwal?.fire({
title: "Sukses",
icon: "success",
confirmButtonColor: "#3085d6",
confirmButtonText: "OK",
}).then((result) => {
if (result.isConfirmed) {
if (window.history.state && window.history.state.idx > 0) {
console.log("backkkkk");
console.log(window.history.state);
router.back();
} else {
router.push("/in/contributor/content/spit");
}
}
});
}
return (
<form onSubmit={handleSubmit(onSubmit)}>
{detail !== undefined ? (
@ -653,7 +698,7 @@ export default function FormConvertSPIT() {
}`}
onClick={() => handleArticleIdClick(id)}
>
{id}
{"Narasi " + (index + 1)}
</button>
))}
</div>
@ -737,6 +782,9 @@ export default function FormConvertSPIT() {
</div>
</div>
</div>
<div className="mt-3">
<Label className="text-xl">Penempatan file</Label>
</div>
{files?.map((file, index) => (
<div
key={file.contentId}
@ -919,8 +967,12 @@ export default function FormConvertSPIT() {
</Button>
</div>
<div className="mt-4">
<Button type="submit" color="primary" variant="outline">
Cancel
<Button
type="submit"
className="bg-red-500 hover:bg-red-700"
onClick={() => deleteSpitContent()}
>
Delete
</Button>
</div>
</div>

View File

@ -53,6 +53,7 @@ import { error, loading } from "@/config/swal";
import { Item } from "@radix-ui/react-dropdown-menu";
import dynamic from "next/dynamic";
import { getCsrfToken } from "@/service/auth";
import { Link } from "@/i18n/routing";
interface FileWithPreview extends File {
preview: string;
@ -919,7 +920,7 @@ export default function FormTeks() {
}`}
onClick={() => handleArticleIdClick(id)}
>
{id}
{"Narasi " + (index + 1)}
</p>
))}
</div>
@ -928,18 +929,8 @@ export default function FormTeks() {
<div className="pt-3">
<div className="flex flex-row justify-between items-center">
{selectedArticleId && (
<a
href={`/admin/media/${
fileTypeId === "1"
? "image"
: fileTypeId === "2"
? "video"
: fileTypeId === "3"
? "text"
: "audio"
}/update-new/${selectedArticleId}`}
target="_blank"
rel="noopener noreferrer"
<Link
href={`/contributor/content/teks/update-seo/${selectedArticleId}`}
>
<Button
className="mb-2"
@ -949,7 +940,7 @@ export default function FormTeks() {
>
Edit
</Button>
</a>
</Link>
)}
</div>
</div>

View File

@ -0,0 +1,757 @@
"use client";
import React, {
ChangeEvent,
Fragment,
useEffect,
useRef,
useState,
} from "react";
import { useForm, Controller } from "react-hook-form";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import { Label } from "@/components/ui/label";
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { zodResolver } from "@hookform/resolvers/zod";
import * as z from "zod";
import Swal from "sweetalert2";
import withReactContent from "sweetalert2-react-content";
import { useParams, useRouter } from "next/navigation";
import Cookies from "js-cookie";
import {
createMedia,
deleteFile,
deleteMedia,
getTagsBySubCategoryId,
listEnableCategory,
uploadThumbnail,
} from "@/service/content/content";
import { detailMedia } from "@/service/curated-content/curated-content";
import { Badge } from "@/components/ui/badge";
import { CloudUpload, MailIcon, PieChart, XIcon } from "lucide-react";
import dynamic from "next/dynamic";
import { useDropzone } from "react-dropzone";
import { Icon } from "@iconify/react/dist/iconify.js";
import Image from "next/image";
import { error, loading } from "@/lib/swal";
import { getCsrfToken } from "@/service/auth";
import { Upload } from "tus-js-client";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { Textarea } from "@/components/ui/textarea";
import ViewEditor from "@/components/editor/view-editor";
import { getDetailArticle, getSeoScore } from "@/service/content/ai";
import { Gauge } from "@mui/x-charts/Gauge";
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from "@/components/ui/accordion";
import { Pie } from "react-chartjs-2";
import {
Chart as ChartJS,
ArcElement,
Tooltip,
Legend,
ChartOptions,
} from "chart.js";
ChartJS.register(ArcElement, Tooltip, Legend);
const imageSchema = z.object({
title: z.string().min(1, { message: "Judul diperlukan" }),
description: z
.string()
.min(2, { message: "Narasi Penugasan harus lebih dari 2 karakter." }),
creatorName: z.string().min(1, { message: "Creator diperlukan" }),
// tags: z.string().min(1, { message: "Judul diperlukan" }),
});
type Category = {
id: string;
name: string;
};
type Detail = {
id: string;
title: string;
description: string;
slug: string;
category: {
id: string;
name: string;
};
publishedFor: string;
publishedForObject: {
id: number;
name: string;
};
htmlDescription: string;
creatorName: string;
categoryName: string;
thumbnailLink: string;
tags: string;
};
type Option = {
id: string;
name: string;
};
const CustomEditor = dynamic(
() => {
return import("@/components/editor/custom-editor");
},
{ ssr: false }
);
interface FileWithPreview extends File {
preview: string;
}
export default function FormTeksSeo() {
const MySwal = withReactContent(Swal);
const router = useRouter();
const { id } = useParams() as { id: string };
console.log(id);
const editor = useRef(null);
type ImageSchema = z.infer<typeof imageSchema>;
let progressInfo: any = [];
let counterUpdateProgress = 0;
const [progressList, setProgressList] = useState<any>([]);
let uploadPersen = 0;
const [isStartUpload, setIsStartUpload] = useState(false);
const [counterProgress, setCounterProgress] = useState(0);
const [selectedFiles, setSelectedFiles] = useState<File[]>([]);
const taskId = Cookies.get("taskId");
const scheduleId = Cookies.get("scheduleId");
const scheduleType = Cookies.get("scheduleType");
const [categories, setCategories] = useState<Category[]>([]);
const [selectedCategory, setSelectedCategory] = useState<any>();
const [tags, setTags] = useState<any[]>([]);
const [detail, setDetail] = useState<Detail>();
const [refresh, setRefresh] = useState(false);
const [selectedPublishers, setSelectedPublishers] = useState<number[]>([]);
const [articleBody, setArticleBody] = useState<string>("");
const [files, setFiles] = useState<FileWithPreview[]>([]);
const [filesTemp, setFilesTemp] = useState<File[]>([]);
const [publishedFor, setPublishedFor] = useState<string[]>([]);
const inputRef = useRef<HTMLInputElement>(null);
const [selectedOptions, setSelectedOptions] = useState<{
[fileId: number]: string;
}>({});
const [articleData, setArticleData] = useState({
title: "",
mainKeyword: "",
additionalKeywords: "",
metaTitle: "",
metaDescription: "",
});
const [totalScoreSEO, setTotalScoreSEO] = useState<number>(0);
const [errorSEO, setErrorSEO] = useState<string[]>([]);
const [warningSEO, setWarningSEO] = useState<string[]>([]);
const [optimizedSEO, setOptimizedSEO] = useState<string[]>([]);
// const [errorData, setErrorData] = useState<string[]>([]);
const [warningData, setWarningData] = useState<string[]>([]);
const [optimizedData, setOptimizedData] = useState<string[]>([]);
const [errorsData, setErrorData] = useState<string[]>([]);
const [selectedTarget, setSelectedTarget] = useState<string | undefined>(
detail?.category.id
);
const [unitSelection, setUnitSelection] = useState({
allUnit: false,
mabes: false,
polda: false,
polres: false,
});
let fileTypeId = "1";
const { getRootProps, getInputProps } = useDropzone({
onDrop: (acceptedFiles) => {
setFiles(acceptedFiles.map((file) => Object.assign(file)));
},
});
const {
control,
handleSubmit,
setValue,
formState: { errors },
} = useForm<ImageSchema>({
resolver: zodResolver(imageSchema),
});
// const handleKeyDown = (e: any) => {
// const newTag = e.target.value.trim(); // Ambil nilai input
// if (e.key === "Enter" && newTag) {
// e.preventDefault(); // Hentikan submit form
// if (!tags.includes(newTag)) {
// setTags((prevTags) => [...prevTags, newTag]); // Tambah tag baru
// setValue("tags", ""); // Kosongkan input
// }
// }
// };
const handleImageChange = (event: ChangeEvent<HTMLInputElement>) => {
if (event.target.files) {
const files = Array.from(event.target.files);
setSelectedFiles((prevImages: any) => [...prevImages, ...files]);
console.log("DATAFILE::", selectedFiles);
}
};
const handleRemoveImage = (index: number) => {
setSelectedFiles((prevImages) => prevImages.filter((_, i) => i !== index));
};
// const handleCheckboxChange = (id: number) => {
// setSelectedPublishers((prev) =>
// prev.includes(id) ? prev.filter((item) => item !== id) : [...prev, id]
// );
// };
useEffect(() => {
async function initState() {
getCategories();
}
initState();
}, []);
const getCategories = async () => {
try {
const category = await listEnableCategory(fileTypeId);
const resCategory: Category[] = category?.data?.data?.content;
setCategories(resCategory);
console.log("data category", resCategory);
if (scheduleId && scheduleType === "3") {
const findCategory = resCategory.find((o) =>
o.name.toLowerCase().includes("pers rilis")
);
if (findCategory) {
// setValue("categoryId", findCategory.id);
setSelectedCategory(findCategory.id); // Set the selected category
const response = await getTagsBySubCategoryId(findCategory.id);
setTags(response?.data?.data);
}
}
} catch (error) {
console.error("Failed to fetch categories:", error);
}
};
useEffect(() => {
const fetchArticleData = async () => {
try {
const response = await getDetailArticle(id);
const data = response?.data?.data;
const cleanArticleBody = data.articleBody.replace(/<img[^>]*>/g, "");
setArticleData({
title: data.title || "",
mainKeyword: data.mainKeyword || "",
additionalKeywords: data.additionalKeywords || "",
metaTitle: data.metaTitle || "",
metaDescription: data.metaDescription || "",
});
setArticleBody(cleanArticleBody || "");
// reset({
// title: data.title,
// mainKeyword: data.mainKeyword,
// additionalKeywords: data.additionalKeywords,
// metaTitle: data.metaTitle,
// metaDescription: data.metaDescription,
// });
setArticleBody(cleanArticleBody || "");
} catch (error) {
console.error("Failed to fetch article data:", error);
}
};
if (id) {
fetchArticleData();
}
}, [id]);
useEffect(() => {
const fetchSeoScore = async () => {
const res = await getSeoScore(id);
if (res.error) {
error(res.message);
return false;
}
setTotalScoreSEO(res.data.data?.seo_analysis?.score || 0);
let errorList = [
...res.data.data?.seo_analysis?.analysis?.keyword_optimization?.error,
...res.data.data?.seo_analysis?.analysis?.content_quality?.error,
];
setErrorSEO(errorList);
let warningList = [
...res.data.data?.seo_analysis?.analysis?.keyword_optimization?.warning,
...res.data.data?.seo_analysis?.analysis?.content_quality?.warning,
];
setWarningSEO(warningList);
let optimizedList = [
...res.data.data?.seo_analysis?.analysis?.keyword_optimization
?.optimized,
...res.data.data?.seo_analysis?.analysis?.content_quality?.optimized,
];
setOptimizedSEO(optimizedList);
setErrorData(errorList);
setWarningData(warningList);
setOptimizedData(optimizedList);
};
fetchSeoScore();
});
const data = {
labels: ["SEO Score (" + totalScoreSEO + "%)"],
datasets: [
{
data: [totalScoreSEO],
backgroundColor: ["#4CAF50"],
hoverBackgroundColor: ["#388E3C"],
},
],
};
const options: ChartOptions<"pie"> = {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: "bottom", // TypeScript now correctly recognizes this as a valid option
},
},
};
const save = async (data: ImageSchema) => {
loading();
const finalTags = tags.join(", ");
const requestData = {
...data,
id: detail?.id,
title: data.title,
description: data.description,
htmlDescription: data.description,
fileTypeId,
categoryId: selectedTarget,
subCategoryId: selectedTarget,
uploadedBy: "2b7c8d83-d298-4b19-9f74-b07924506b58",
statusId: "1",
publishedFor: publishedFor.join(","),
creatorName: data.creatorName,
tags: finalTags,
isYoutube: false,
isInternationalMedia: false,
};
const response = await createMedia(requestData);
console.log("Form Data Submitted:", requestData);
const formMedia = new FormData();
const thumbnail = files[0];
formMedia.append("file", thumbnail);
const responseThumbnail = await uploadThumbnail(id, formMedia);
if (responseThumbnail?.error == true) {
error(responseThumbnail?.message);
return false;
}
const progressInfoArr = [];
for (const item of files) {
progressInfoArr.push({ percentage: 0, fileName: item.name });
}
progressInfo = progressInfoArr;
setIsStartUpload(true);
setProgressList(progressInfoArr);
close();
// showProgress();
files.map(async (item: any, index: number) => {
await uploadResumableFile(
index,
String(id),
item,
fileTypeId == "2" || fileTypeId == "4" ? item.duration : "0"
);
});
MySwal.fire({
title: "Sukses",
text: "Data berhasil disimpan.",
icon: "success",
confirmButtonColor: "#3085d6",
confirmButtonText: "OK",
}).then(() => {
router.push("/en/contributor/content/image");
});
};
async function uploadResumableFile(
idx: number,
id: string,
file: any,
duration: string
) {
console.log(idx, id, file, duration);
// const placements = getPlacement(file.placements);
// console.log("Placementttt: : ", placements);
const resCsrf = await getCsrfToken();
const csrfToken = resCsrf?.data?.token;
console.log("CSRF TOKEN : ", csrfToken);
const headers = {
"X-XSRF-TOKEN": csrfToken,
};
const upload = new Upload(file, {
endpoint: `${process.env.NEXT_PUBLIC_API}/media/file/upload`,
headers: headers,
retryDelays: [0, 3000, 6000, 12_000, 24_000],
chunkSize: 20_000,
metadata: {
mediaid: id,
filename: file.name,
filetype: file.type,
duration,
isWatermark: "true", // hardcode
},
onBeforeRequest: function (req) {
var xhr = req.getUnderlyingObject();
xhr.withCredentials = true;
},
onError: async (e: any) => {
console.log("Error upload :", e);
error(e);
},
onChunkComplete: (
chunkSize: any,
bytesAccepted: any,
bytesTotal: any
) => {
const uploadPersen = Math.floor((bytesAccepted / bytesTotal) * 100);
progressInfo[idx].percentage = uploadPersen;
counterUpdateProgress++;
console.log(counterUpdateProgress);
setProgressList(progressInfo);
setCounterProgress(counterUpdateProgress);
},
onSuccess: async () => {
uploadPersen = 100;
progressInfo[idx].percentage = 100;
counterUpdateProgress++;
setCounterProgress(counterUpdateProgress);
successTodo();
},
});
upload.start();
}
const onSubmit = (data: ImageSchema) => {
MySwal.fire({
title: "Simpan Data",
text: "Apakah Anda yakin ingin menyimpan data ini?",
icon: "warning",
showCancelButton: true,
cancelButtonColor: "#d33",
confirmButtonColor: "#3085d6",
confirmButtonText: "Simpan",
}).then((result) => {
if (result.isConfirmed) {
save(data);
}
});
};
const successSubmit = (redirect: string) => {
MySwal.fire({
title: "Sukses",
text: "Data berhasil disimpan.",
icon: "success",
confirmButtonColor: "#3085d6",
confirmButtonText: "OK",
}).then(() => {
router.push(redirect);
});
};
function successTodo() {
let counter = 0;
for (const element of progressInfo) {
if (element.percentage == 100) {
counter++;
}
}
if (counter == progressInfo.length) {
setIsStartUpload(false);
// hideProgress();
Cookies.remove("idCreate");
successSubmit("/in/contributor/content/image/");
}
}
const handleRemoveFile = (file: FileWithPreview) => {
const uploadedFiles = files;
const filtered = uploadedFiles.filter((i) => i.name !== file.name);
setFiles([...filtered]);
};
function success() {
MySwal.fire({
title: "Sukses",
icon: "success",
confirmButtonColor: "#3085d6",
confirmButtonText: "OK",
}).then((result) => {
if (result.isConfirmed) {
// window.location.reload();
}
});
}
const handleDeleteFile = (id: number) => {
MySwal.fire({
title: "Hapus file",
text: "Apakah Anda yakin ingin menghapus file ini?",
icon: "warning",
showCancelButton: true,
cancelButtonColor: "#3085d6",
confirmButtonColor: "#d33",
confirmButtonText: "Hapus",
}).then((result) => {
if (result.isConfirmed) {
doDelete(id);
}
});
};
async function doDelete(id: number) {
const data = { id };
try {
const response = await deleteFile(data);
if (response?.error) {
error(response.message);
return;
}
// Jika berhasil, hapus file dari state lokal
setFiles((prevFiles: any) =>
prevFiles.filter((file: any) => file.id !== id)
);
success();
} catch (err) {
error("Terjadi kesalahan saat menghapus file");
}
}
return (
<div>
<div className="mx-5 mb-3">
<Card>
<Tabs defaultValue="content" className="">
<TabsList className="grid w-[300px] grid-cols-2 bg-slate-400 my-3 mx-3 ">
<TabsTrigger
value="content"
className="data-[state=active]:text-black text-gray-500 data-[state=active]:rounded-md"
>
Konten
</TabsTrigger>
<TabsTrigger
value="checker"
className="data-[state=active]:text-black text-gray-500 data-[state=active]:rounded-md"
>
Checker
</TabsTrigger>
</TabsList>
<TabsContent value="content">
{articleData !== undefined ? (
<CardContent className="space-y-2 my-3">
<div className="space-y-1">
<Label htmlFor="name">Judul</Label>
<Input id="name" defaultValue={articleData?.title} />
</div>
<div className="flex flex-row gap-3 w-full">
<div className="w-full">
<Label htmlFor="username">Main Keyword</Label>
<Textarea
id="mainKeyword"
value={articleData?.mainKeyword}
/>
</div>
<div className="w-full">
<Label htmlFor="username">Additional Keyword</Label>
<Textarea
id="additionalKeywords"
value={articleData?.additionalKeywords}
/>
</div>
</div>
<div className="flex flex-row gap-3 w-full">
<div className="w-full">
<Label htmlFor="username">Meta Title</Label>
<Textarea id="metaTitle" value={articleData?.metaTitle} />
</div>
<div className="w-full">
<Label htmlFor="username">Meta Description</Label>
<Textarea
id="metaDescription"
value={articleData?.metaDescription}
/>
</div>
</div>
<div className="py-3">
<Label>Article</Label>
<Controller
control={control}
name="description"
render={({ field: { onChange, value } }) => (
<CustomEditor
onChange={onChange}
initialData={articleBody}
/>
)}
/>
{errors.description?.message && (
<p className="text-red-400 text-sm">
{errors.description.message}
</p>
)}
</div>
</CardContent>
) : (
""
)}
</TabsContent>
<TabsContent value="checker">
<CardContent className="space-y-2">
<div className="flex items-start justify-start">
<Pie
data={data}
options={options}
className="text-left flex items-start justify-start"
/>
</div>
<div className="space-y-1">
<Accordion type="single" collapsible className="w-full ">
<AccordionItem
value="error"
className="border border-red-600"
>
<AccordionTrigger>
<div className="flex items-center">
<XIcon className="text-red-600" />
Errors ({errorSEO.length})
</div>
</AccordionTrigger>
<AccordionContent>
{errorSEO.length > 0 ? (
<ul className="list-disc list-inside">
{errorSEO.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
) : (
<p>No errors found.</p>
)}
</AccordionContent>
</AccordionItem>
<AccordionItem
value="warning"
className="border border-yellow-600"
>
<AccordionTrigger>
<div className="flex items-center">
<XIcon className="text-yellow-600" />
Warnings ({warningSEO.length})
</div>
</AccordionTrigger>
<AccordionContent>
{warningSEO.length > 0 ? (
<ul className="list-disc list-inside">
{warningSEO.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
) : (
<p>No warnings found.</p>
)}
</AccordionContent>
</AccordionItem>
<AccordionItem
value="optimized"
className="border border-green-600"
>
<AccordionTrigger>
<div className="flex items-center">
<XIcon className="text-green-600" />
Optimized ({optimizedSEO.length})
</div>
</AccordionTrigger>
<AccordionContent>
{optimizedSEO.length > 0 ? (
<ul className="list-disc list-inside">
{optimizedSEO.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
) : (
<p>No optimizations found.</p>
)}
</AccordionContent>
</AccordionItem>
</Accordion>
</div>
<div className="py-3">
<div className="flex flex-row justify-between items-center mb-3">
<Label>Article</Label>
<Button size="md" className="bg-blue-500">
Select Image From Content Bank
</Button>
</div>
<Controller
control={control}
name="description"
render={({ field: { onChange, value } }) => (
<CustomEditor
onChange={onChange}
initialData={articleBody}
/>
)}
/>
{errors.description?.message && (
<p className="text-red-400 text-sm">
{errors.description.message}
</p>
)}
</div>
</CardContent>
</TabsContent>
</Tabs>
</Card>
</div>
</div>
);
}

View File

@ -53,6 +53,7 @@ import { error, loading } from "@/config/swal";
import { Item } from "@radix-ui/react-dropdown-menu";
import dynamic from "next/dynamic";
import { getCsrfToken } from "@/service/auth";
import { Link } from "@/i18n/routing";
const CustomEditor = dynamic(
() => {
@ -917,7 +918,7 @@ export default function FormVideo() {
}`}
onClick={() => handleArticleIdClick(id)}
>
{id}
{"Narasi " + (index + 1)}
</p>
))}
</div>
@ -926,18 +927,8 @@ export default function FormVideo() {
<div className="pt-3">
<div className="flex flex-row justify-between items-center">
{selectedArticleId && (
<a
href={`/admin/media/${
fileTypeId === "1"
? "image"
: fileTypeId === "2"
? "video"
: fileTypeId === "3"
? "text"
: "audio"
}/update-new/${selectedArticleId}`}
target="_blank"
rel="noopener noreferrer"
<Link
href={`/contributor/content/video/update-seo/${selectedArticleId}`}
>
<Button
className="mb-2"
@ -947,7 +938,7 @@ export default function FormVideo() {
>
Edit
</Button>
</a>
</Link>
)}
</div>
</div>

View File

@ -0,0 +1,757 @@
"use client";
import React, {
ChangeEvent,
Fragment,
useEffect,
useRef,
useState,
} from "react";
import { useForm, Controller } from "react-hook-form";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import { Label } from "@/components/ui/label";
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { zodResolver } from "@hookform/resolvers/zod";
import * as z from "zod";
import Swal from "sweetalert2";
import withReactContent from "sweetalert2-react-content";
import { useParams, useRouter } from "next/navigation";
import Cookies from "js-cookie";
import {
createMedia,
deleteFile,
deleteMedia,
getTagsBySubCategoryId,
listEnableCategory,
uploadThumbnail,
} from "@/service/content/content";
import { detailMedia } from "@/service/curated-content/curated-content";
import { Badge } from "@/components/ui/badge";
import { CloudUpload, MailIcon, PieChart, XIcon } from "lucide-react";
import dynamic from "next/dynamic";
import { useDropzone } from "react-dropzone";
import { Icon } from "@iconify/react/dist/iconify.js";
import Image from "next/image";
import { error, loading } from "@/lib/swal";
import { getCsrfToken } from "@/service/auth";
import { Upload } from "tus-js-client";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { Textarea } from "@/components/ui/textarea";
import ViewEditor from "@/components/editor/view-editor";
import { getDetailArticle, getSeoScore } from "@/service/content/ai";
import { Gauge } from "@mui/x-charts/Gauge";
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from "@/components/ui/accordion";
import { Pie } from "react-chartjs-2";
import {
Chart as ChartJS,
ArcElement,
Tooltip,
Legend,
ChartOptions,
} from "chart.js";
ChartJS.register(ArcElement, Tooltip, Legend);
const imageSchema = z.object({
title: z.string().min(1, { message: "Judul diperlukan" }),
description: z
.string()
.min(2, { message: "Narasi Penugasan harus lebih dari 2 karakter." }),
creatorName: z.string().min(1, { message: "Creator diperlukan" }),
// tags: z.string().min(1, { message: "Judul diperlukan" }),
});
type Category = {
id: string;
name: string;
};
type Detail = {
id: string;
title: string;
description: string;
slug: string;
category: {
id: string;
name: string;
};
publishedFor: string;
publishedForObject: {
id: number;
name: string;
};
htmlDescription: string;
creatorName: string;
categoryName: string;
thumbnailLink: string;
tags: string;
};
type Option = {
id: string;
name: string;
};
const CustomEditor = dynamic(
() => {
return import("@/components/editor/custom-editor");
},
{ ssr: false }
);
interface FileWithPreview extends File {
preview: string;
}
export default function FormVideoSeo() {
const MySwal = withReactContent(Swal);
const router = useRouter();
const { id } = useParams() as { id: string };
console.log(id);
const editor = useRef(null);
type ImageSchema = z.infer<typeof imageSchema>;
let progressInfo: any = [];
let counterUpdateProgress = 0;
const [progressList, setProgressList] = useState<any>([]);
let uploadPersen = 0;
const [isStartUpload, setIsStartUpload] = useState(false);
const [counterProgress, setCounterProgress] = useState(0);
const [selectedFiles, setSelectedFiles] = useState<File[]>([]);
const taskId = Cookies.get("taskId");
const scheduleId = Cookies.get("scheduleId");
const scheduleType = Cookies.get("scheduleType");
const [categories, setCategories] = useState<Category[]>([]);
const [selectedCategory, setSelectedCategory] = useState<any>();
const [tags, setTags] = useState<any[]>([]);
const [detail, setDetail] = useState<Detail>();
const [refresh, setRefresh] = useState(false);
const [selectedPublishers, setSelectedPublishers] = useState<number[]>([]);
const [articleBody, setArticleBody] = useState<string>("");
const [files, setFiles] = useState<FileWithPreview[]>([]);
const [filesTemp, setFilesTemp] = useState<File[]>([]);
const [publishedFor, setPublishedFor] = useState<string[]>([]);
const inputRef = useRef<HTMLInputElement>(null);
const [selectedOptions, setSelectedOptions] = useState<{
[fileId: number]: string;
}>({});
const [articleData, setArticleData] = useState({
title: "",
mainKeyword: "",
additionalKeywords: "",
metaTitle: "",
metaDescription: "",
});
const [totalScoreSEO, setTotalScoreSEO] = useState<number>(0);
const [errorSEO, setErrorSEO] = useState<string[]>([]);
const [warningSEO, setWarningSEO] = useState<string[]>([]);
const [optimizedSEO, setOptimizedSEO] = useState<string[]>([]);
// const [errorData, setErrorData] = useState<string[]>([]);
const [warningData, setWarningData] = useState<string[]>([]);
const [optimizedData, setOptimizedData] = useState<string[]>([]);
const [errorsData, setErrorData] = useState<string[]>([]);
const [selectedTarget, setSelectedTarget] = useState<string | undefined>(
detail?.category.id
);
const [unitSelection, setUnitSelection] = useState({
allUnit: false,
mabes: false,
polda: false,
polres: false,
});
let fileTypeId = "1";
const { getRootProps, getInputProps } = useDropzone({
onDrop: (acceptedFiles) => {
setFiles(acceptedFiles.map((file) => Object.assign(file)));
},
});
const {
control,
handleSubmit,
setValue,
formState: { errors },
} = useForm<ImageSchema>({
resolver: zodResolver(imageSchema),
});
// const handleKeyDown = (e: any) => {
// const newTag = e.target.value.trim(); // Ambil nilai input
// if (e.key === "Enter" && newTag) {
// e.preventDefault(); // Hentikan submit form
// if (!tags.includes(newTag)) {
// setTags((prevTags) => [...prevTags, newTag]); // Tambah tag baru
// setValue("tags", ""); // Kosongkan input
// }
// }
// };
const handleImageChange = (event: ChangeEvent<HTMLInputElement>) => {
if (event.target.files) {
const files = Array.from(event.target.files);
setSelectedFiles((prevImages: any) => [...prevImages, ...files]);
console.log("DATAFILE::", selectedFiles);
}
};
const handleRemoveImage = (index: number) => {
setSelectedFiles((prevImages) => prevImages.filter((_, i) => i !== index));
};
// const handleCheckboxChange = (id: number) => {
// setSelectedPublishers((prev) =>
// prev.includes(id) ? prev.filter((item) => item !== id) : [...prev, id]
// );
// };
useEffect(() => {
async function initState() {
getCategories();
}
initState();
}, []);
const getCategories = async () => {
try {
const category = await listEnableCategory(fileTypeId);
const resCategory: Category[] = category?.data?.data?.content;
setCategories(resCategory);
console.log("data category", resCategory);
if (scheduleId && scheduleType === "3") {
const findCategory = resCategory.find((o) =>
o.name.toLowerCase().includes("pers rilis")
);
if (findCategory) {
// setValue("categoryId", findCategory.id);
setSelectedCategory(findCategory.id); // Set the selected category
const response = await getTagsBySubCategoryId(findCategory.id);
setTags(response?.data?.data);
}
}
} catch (error) {
console.error("Failed to fetch categories:", error);
}
};
useEffect(() => {
const fetchArticleData = async () => {
try {
const response = await getDetailArticle(id);
const data = response?.data?.data;
const cleanArticleBody = data.articleBody.replace(/<img[^>]*>/g, "");
setArticleData({
title: data.title || "",
mainKeyword: data.mainKeyword || "",
additionalKeywords: data.additionalKeywords || "",
metaTitle: data.metaTitle || "",
metaDescription: data.metaDescription || "",
});
setArticleBody(cleanArticleBody || "");
// reset({
// title: data.title,
// mainKeyword: data.mainKeyword,
// additionalKeywords: data.additionalKeywords,
// metaTitle: data.metaTitle,
// metaDescription: data.metaDescription,
// });
setArticleBody(cleanArticleBody || "");
} catch (error) {
console.error("Failed to fetch article data:", error);
}
};
if (id) {
fetchArticleData();
}
}, [id]);
useEffect(() => {
const fetchSeoScore = async () => {
const res = await getSeoScore(id);
if (res.error) {
error(res.message);
return false;
}
setTotalScoreSEO(res.data.data?.seo_analysis?.score || 0);
let errorList = [
...res.data.data?.seo_analysis?.analysis?.keyword_optimization?.error,
...res.data.data?.seo_analysis?.analysis?.content_quality?.error,
];
setErrorSEO(errorList);
let warningList = [
...res.data.data?.seo_analysis?.analysis?.keyword_optimization?.warning,
...res.data.data?.seo_analysis?.analysis?.content_quality?.warning,
];
setWarningSEO(warningList);
let optimizedList = [
...res.data.data?.seo_analysis?.analysis?.keyword_optimization
?.optimized,
...res.data.data?.seo_analysis?.analysis?.content_quality?.optimized,
];
setOptimizedSEO(optimizedList);
setErrorData(errorList);
setWarningData(warningList);
setOptimizedData(optimizedList);
};
fetchSeoScore();
});
const data = {
labels: ["SEO Score (" + totalScoreSEO + "%)"],
datasets: [
{
data: [totalScoreSEO],
backgroundColor: ["#4CAF50"],
hoverBackgroundColor: ["#388E3C"],
},
],
};
const options: ChartOptions<"pie"> = {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: "bottom", // TypeScript now correctly recognizes this as a valid option
},
},
};
const save = async (data: ImageSchema) => {
loading();
const finalTags = tags.join(", ");
const requestData = {
...data,
id: detail?.id,
title: data.title,
description: data.description,
htmlDescription: data.description,
fileTypeId,
categoryId: selectedTarget,
subCategoryId: selectedTarget,
uploadedBy: "2b7c8d83-d298-4b19-9f74-b07924506b58",
statusId: "1",
publishedFor: publishedFor.join(","),
creatorName: data.creatorName,
tags: finalTags,
isYoutube: false,
isInternationalMedia: false,
};
const response = await createMedia(requestData);
console.log("Form Data Submitted:", requestData);
const formMedia = new FormData();
const thumbnail = files[0];
formMedia.append("file", thumbnail);
const responseThumbnail = await uploadThumbnail(id, formMedia);
if (responseThumbnail?.error == true) {
error(responseThumbnail?.message);
return false;
}
const progressInfoArr = [];
for (const item of files) {
progressInfoArr.push({ percentage: 0, fileName: item.name });
}
progressInfo = progressInfoArr;
setIsStartUpload(true);
setProgressList(progressInfoArr);
close();
// showProgress();
files.map(async (item: any, index: number) => {
await uploadResumableFile(
index,
String(id),
item,
fileTypeId == "2" || fileTypeId == "4" ? item.duration : "0"
);
});
MySwal.fire({
title: "Sukses",
text: "Data berhasil disimpan.",
icon: "success",
confirmButtonColor: "#3085d6",
confirmButtonText: "OK",
}).then(() => {
router.push("/en/contributor/content/image");
});
};
async function uploadResumableFile(
idx: number,
id: string,
file: any,
duration: string
) {
console.log(idx, id, file, duration);
// const placements = getPlacement(file.placements);
// console.log("Placementttt: : ", placements);
const resCsrf = await getCsrfToken();
const csrfToken = resCsrf?.data?.token;
console.log("CSRF TOKEN : ", csrfToken);
const headers = {
"X-XSRF-TOKEN": csrfToken,
};
const upload = new Upload(file, {
endpoint: `${process.env.NEXT_PUBLIC_API}/media/file/upload`,
headers: headers,
retryDelays: [0, 3000, 6000, 12_000, 24_000],
chunkSize: 20_000,
metadata: {
mediaid: id,
filename: file.name,
filetype: file.type,
duration,
isWatermark: "true", // hardcode
},
onBeforeRequest: function (req) {
var xhr = req.getUnderlyingObject();
xhr.withCredentials = true;
},
onError: async (e: any) => {
console.log("Error upload :", e);
error(e);
},
onChunkComplete: (
chunkSize: any,
bytesAccepted: any,
bytesTotal: any
) => {
const uploadPersen = Math.floor((bytesAccepted / bytesTotal) * 100);
progressInfo[idx].percentage = uploadPersen;
counterUpdateProgress++;
console.log(counterUpdateProgress);
setProgressList(progressInfo);
setCounterProgress(counterUpdateProgress);
},
onSuccess: async () => {
uploadPersen = 100;
progressInfo[idx].percentage = 100;
counterUpdateProgress++;
setCounterProgress(counterUpdateProgress);
successTodo();
},
});
upload.start();
}
const onSubmit = (data: ImageSchema) => {
MySwal.fire({
title: "Simpan Data",
text: "Apakah Anda yakin ingin menyimpan data ini?",
icon: "warning",
showCancelButton: true,
cancelButtonColor: "#d33",
confirmButtonColor: "#3085d6",
confirmButtonText: "Simpan",
}).then((result) => {
if (result.isConfirmed) {
save(data);
}
});
};
const successSubmit = (redirect: string) => {
MySwal.fire({
title: "Sukses",
text: "Data berhasil disimpan.",
icon: "success",
confirmButtonColor: "#3085d6",
confirmButtonText: "OK",
}).then(() => {
router.push(redirect);
});
};
function successTodo() {
let counter = 0;
for (const element of progressInfo) {
if (element.percentage == 100) {
counter++;
}
}
if (counter == progressInfo.length) {
setIsStartUpload(false);
// hideProgress();
Cookies.remove("idCreate");
successSubmit("/in/contributor/content/image/");
}
}
const handleRemoveFile = (file: FileWithPreview) => {
const uploadedFiles = files;
const filtered = uploadedFiles.filter((i) => i.name !== file.name);
setFiles([...filtered]);
};
function success() {
MySwal.fire({
title: "Sukses",
icon: "success",
confirmButtonColor: "#3085d6",
confirmButtonText: "OK",
}).then((result) => {
if (result.isConfirmed) {
// window.location.reload();
}
});
}
const handleDeleteFile = (id: number) => {
MySwal.fire({
title: "Hapus file",
text: "Apakah Anda yakin ingin menghapus file ini?",
icon: "warning",
showCancelButton: true,
cancelButtonColor: "#3085d6",
confirmButtonColor: "#d33",
confirmButtonText: "Hapus",
}).then((result) => {
if (result.isConfirmed) {
doDelete(id);
}
});
};
async function doDelete(id: number) {
const data = { id };
try {
const response = await deleteFile(data);
if (response?.error) {
error(response.message);
return;
}
// Jika berhasil, hapus file dari state lokal
setFiles((prevFiles: any) =>
prevFiles.filter((file: any) => file.id !== id)
);
success();
} catch (err) {
error("Terjadi kesalahan saat menghapus file");
}
}
return (
<div>
<div className="mx-5 mb-3">
<Card>
<Tabs defaultValue="content" className="">
<TabsList className="grid w-[300px] grid-cols-2 bg-slate-400 my-3 mx-3 ">
<TabsTrigger
value="content"
className="data-[state=active]:text-black text-gray-500 data-[state=active]:rounded-md"
>
Konten
</TabsTrigger>
<TabsTrigger
value="checker"
className="data-[state=active]:text-black text-gray-500 data-[state=active]:rounded-md"
>
Checker
</TabsTrigger>
</TabsList>
<TabsContent value="content">
{articleData !== undefined ? (
<CardContent className="space-y-2 my-3">
<div className="space-y-1">
<Label htmlFor="name">Judul</Label>
<Input id="name" defaultValue={articleData?.title} />
</div>
<div className="flex flex-row gap-3 w-full">
<div className="w-full">
<Label htmlFor="username">Main Keyword</Label>
<Textarea
id="mainKeyword"
value={articleData?.mainKeyword}
/>
</div>
<div className="w-full">
<Label htmlFor="username">Additional Keyword</Label>
<Textarea
id="additionalKeywords"
value={articleData?.additionalKeywords}
/>
</div>
</div>
<div className="flex flex-row gap-3 w-full">
<div className="w-full">
<Label htmlFor="username">Meta Title</Label>
<Textarea id="metaTitle" value={articleData?.metaTitle} />
</div>
<div className="w-full">
<Label htmlFor="username">Meta Description</Label>
<Textarea
id="metaDescription"
value={articleData?.metaDescription}
/>
</div>
</div>
<div className="py-3">
<Label>Article</Label>
<Controller
control={control}
name="description"
render={({ field: { onChange, value } }) => (
<CustomEditor
onChange={onChange}
initialData={articleBody}
/>
)}
/>
{errors.description?.message && (
<p className="text-red-400 text-sm">
{errors.description.message}
</p>
)}
</div>
</CardContent>
) : (
""
)}
</TabsContent>
<TabsContent value="checker">
<CardContent className="space-y-2">
<div className="flex items-start justify-start">
<Pie
data={data}
options={options}
className="text-left flex items-start justify-start"
/>
</div>
<div className="space-y-1">
<Accordion type="single" collapsible className="w-full ">
<AccordionItem
value="error"
className="border border-red-600"
>
<AccordionTrigger>
<div className="flex items-center">
<XIcon className="text-red-600" />
Errors ({errorSEO.length})
</div>
</AccordionTrigger>
<AccordionContent>
{errorSEO.length > 0 ? (
<ul className="list-disc list-inside">
{errorSEO.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
) : (
<p>No errors found.</p>
)}
</AccordionContent>
</AccordionItem>
<AccordionItem
value="warning"
className="border border-yellow-600"
>
<AccordionTrigger>
<div className="flex items-center">
<XIcon className="text-yellow-600" />
Warnings ({warningSEO.length})
</div>
</AccordionTrigger>
<AccordionContent>
{warningSEO.length > 0 ? (
<ul className="list-disc list-inside">
{warningSEO.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
) : (
<p>No warnings found.</p>
)}
</AccordionContent>
</AccordionItem>
<AccordionItem
value="optimized"
className="border border-green-600"
>
<AccordionTrigger>
<div className="flex items-center">
<XIcon className="text-green-600" />
Optimized ({optimizedSEO.length})
</div>
</AccordionTrigger>
<AccordionContent>
{optimizedSEO.length > 0 ? (
<ul className="list-disc list-inside">
{optimizedSEO.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
) : (
<p>No optimizations found.</p>
)}
</AccordionContent>
</AccordionItem>
</Accordion>
</div>
<div className="py-3">
<div className="flex flex-row justify-between items-center mb-3">
<Label>Article</Label>
<Button size="md" className="bg-blue-500">
Select Image From Content Bank
</Button>
</div>
<Controller
control={control}
name="description"
render={({ field: { onChange, value } }) => (
<CustomEditor
onChange={onChange}
initialData={articleBody}
/>
)}
/>
{errors.description?.message && (
<p className="text-red-400 text-sm">
{errors.description.message}
</p>
)}
</div>
</CardContent>
</TabsContent>
</Tabs>
</Card>
</div>
</div>
);
}

View File

@ -172,6 +172,7 @@ interface UploadResult {
title: string;
description: string;
createdAt: string;
creatorGroupLevelName: string;
category: { name: string };
fileType: { name: string };
uploadStatus: { name: string };
@ -194,7 +195,7 @@ export default function FormTaskDetail() {
const userLevelNumber = getCookiesDecrypt("ulne");
const userId = getCookiesDecrypt("uie");
const userLevelId = getCookiesDecrypt("ulie");
const userLevelId = "";
// State for various form fields
const [taskOutput, setTaskOutput] = useState({
@ -328,17 +329,25 @@ export default function FormTaskDetail() {
fetchPoldaPolres();
}, []);
const fetchFilteredData = async (selectedLevels?: any[]) => {
const fetchAllData = async () => {
try {
if (selectedLevels) {
const levels =
selectedLevels.length === 0 ? userLevelId : selectedLevels.join(",");
const response = await getMediaUpload(id, levels);
setUploadResults(response?.data?.data || []);
} else {
const response = await getMediaUpload(id, '');
setUploadResults(response?.data?.data || []);
const response = await getMediaUpload(id, userLevelId);
setUploadResults(response?.data?.data || []);
} catch (error) {
console.error("Error fetching all data:", error);
}
};
const fetchFilteredData = async (selectedLevels: any[]) => {
try {
if (selectedLevels.length === 0) {
fetchAllData(); // Jika tidak ada filter, panggil semua data
return;
}
const levels = selectedLevels.join(",");
const response = await getMediaUpload(id, levels);
setUploadResults(response?.data?.data || []);
} catch (error) {
console.error("Error fetching filtered data:", error);
}
@ -387,7 +396,7 @@ export default function FormTaskDetail() {
}
}
initState();
fetchFilteredData();
// fetchFilteredData();
}, [id, refresh]);
const handleUrlChange = (index: number, newUrl: string) => {
@ -467,7 +476,6 @@ export default function FormTaskDetail() {
console.log("Checked Levels:", Array.from(updatedLevels));
// Fetch data dengan filter userLevelId
fetchFilteredData(Array.from(updatedLevels));
return updatedLevels;
@ -1418,7 +1426,10 @@ export default function FormTaskDetail() {
type="button"
color="primary"
variant={"default"}
onClick={() => setIsTableResult(!isTableResult)}
onClick={() => {
setIsTableResult(!isTableResult);
if (!isTableResult) fetchAllData(); // Panggil API saat tombol diklik
}}
>
Hasil Upload {Number(userId)}
</Button>
@ -1459,7 +1470,7 @@ export default function FormTaskDetail() {
<div key={polda.id} className="border p-2">
<Label className="flex items-center">
<Checkbox
checked={checkedLevels.has(polda.id)}
// checked={checkedLevels.has(polda.id)}
onCheckedChange={() =>
handleCheckboxChangeFilter(polda.id)
}
@ -1481,10 +1492,10 @@ export default function FormTaskDetail() {
<div className="ml-6 mt-2">
<Label className="block">
<Checkbox
checked={polda?.subDestination?.every(
(polres: any) =>
checkedLevels.has(polres.id)
)}
// checked={polda?.subDestination?.every(
// (polres: any) =>
// checkedLevels.has(polres.id)
// )}
onCheckedChange={(isChecked) => {
const updatedLevels = new Set(
checkedLevels
@ -1507,7 +1518,7 @@ export default function FormTaskDetail() {
{polda?.subDestination?.map((polres: any) => (
<Label key={polres.id} className="block mt-1">
<Checkbox
checked={checkedLevels.has(polres.id)}
// checked={checkedLevels.has(polres.id)}
onCheckedChange={() =>
handleCheckboxChangeFilter(polres.id)
}
@ -1529,7 +1540,8 @@ export default function FormTaskDetail() {
<tr className="bg-gray-100 border-b">
<th className="px-4 py-2 text-left">Judul</th>
<th className="px-4 py-2 text-left">Konten</th>
<th className="px-4 py-2 text-left">Kategori</th>
<th className="px-4 py-2 text-left">Kategory</th>
<th className="px-4 py-2 text-left">Kreator</th>
<th className="px-4 py-2 text-left">Diupload Oleh</th>
</tr>
</thead>
@ -1541,7 +1553,10 @@ export default function FormTaskDetail() {
</td>
<td className="px-4 py-2">{item.fileType.name}</td>
<td className="px-4 py-2">{item.category.name}</td>
<td className="px-4 py-2">{item.creatorGroup}</td>
<td className="px-4 py-2">
{item.creatorGroupLevelName}
</td>
<td className="px-4 py-2">{item.creatorName}</td>
</tr>
))}
</tbody>

View File

@ -2856,7 +2856,7 @@ export function getMenuList(pathname: string, t: any): Group[] {
children: [],
},
{
href: "/admin/media-tracking/news",
href: "/admin/media-tracking/tb-news",
label: "Tracking Beritra Hari Ini",
active: pathname === "/media-tracking/news",
icon: "heroicons:arrow-trending-up",

2181
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -24,6 +24,7 @@
"@fullcalendar/timegrid": "^6.1.15",
"@hookform/resolvers": "^3.9.0",
"@iconify/react": "^5.0.2",
"@mui/x-charts": "^7.25.0",
"@radix-ui/react-accordion": "^1.1.2",
"@radix-ui/react-alert-dialog": "^1.0.5",
"@radix-ui/react-aspect-ratio": "^1.0.3",
@ -68,8 +69,10 @@
"@types/sanitize-html": "^2.13.0",
"@vercel/analytics": "^1.3.1",
"@wavesurfer/react": "^1.0.8",
"add": "^2.0.6",
"apexcharts": "^3.49.2",
"axios": "^1.7.8",
"chart": "^0.1.2",
"chart.js": "^4.4.3",
"ckeditor5-custom-build": "file:vendor/ckeditor5",
"class-variance-authority": "^0.7.0",
@ -133,6 +136,7 @@
"recharts": "^2.12.7",
"rtl-detect": "^1.1.2",
"sanitize-html": "^2.14.0",
"shadcn": "^2.3.0",
"sharp": "^0.33.4",
"sonner": "^1.5.0",
"sweetalert2": "^11.10.5",

File diff suppressed because it is too large Load Diff

View File

@ -214,3 +214,8 @@ export async function deleteFile(data: any) {
const url = "media/file";
return httpDeleteInterceptor(url, data);
}
export async function deleteSPIT(id: any) {
const url = `media/spit?id=${id}`;
return httpDeleteInterceptor(url);
}

View File

@ -12,13 +12,21 @@ import {
// }
export async function listTask(
title: string = "",
page: any,
title: string = "",
size: any,
taskType: string
code: any,
createdAt: any,
taskType: string,
status: number[]
) {
const url = `assignment/list?enablePage=1&size=${size}&page=${page}&title=${title}&taskType=${taskType}`;
return httpGetInterceptor(url);
const statusQuery = status.length
? `&isDone=${status.includes(1)}&isActive=${status.includes(2)}`
: "";
return httpGetInterceptor(
`assignment/list?enablePage=1&size=${size}&page=${page}&title=${title}&taskType=${taskType}&uniqueCode=${code}&createdAt=${createdAt}${statusQuery}`
);
}
// export async function createTask(data: any) {

View File

@ -2,7 +2,7 @@
* @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/
import type Editor from './editor/editor.js';
import type Editor from '@ckeditor/ckeditor5-core/src/editor/editor.js';
export declare const DEFAULT_GROUP_ID: "common";
/**
* A common namespace for various accessibility features of the editor.

View File

@ -6,7 +6,7 @@
* @module core/command
*/
import { type DecoratedMethodEvent } from '@ckeditor/ckeditor5-utils';
import type Editor from './editor/editor.js';
import type Editor from '@ckeditor/ckeditor5-core/src/editor/editor.js';
declare const Command_base: {
new (): import("@ckeditor/ckeditor5-utils").Observable;
prototype: import("@ckeditor/ckeditor5-utils").Observable;

View File

@ -2,7 +2,7 @@
* @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/
import type Command from './command.js';
import type Command from '@ckeditor/ckeditor5-core/src/command.js';
/**
* Collection of commands. Its instance is available in {@link module:core/editor/editor~Editor#commands `editor.commands`}.
*/

View File

@ -6,10 +6,10 @@
* @module core/context
*/
import { Config, Collection, Locale, type LocaleTranslate } from '@ckeditor/ckeditor5-utils';
import PluginCollection from './plugincollection.js';
import type Editor from './editor/editor.js';
import type { LoadedPlugins, PluginConstructor } from './plugin.js';
import type { EditorConfig } from './editor/editorconfig.js';
import PluginCollection from '@ckeditor/ckeditor5-core/src/plugincollection.js';
import type Editor from '@ckeditor/ckeditor5-core/src/editor/editor.js';
import type { LoadedPlugins, PluginConstructor } from '@ckeditor/ckeditor5-core/src/plugin.js';
import type { EditorConfig } from '@ckeditor/ckeditor5-core/src/editor/editorconfig.js';
/**
* Provides a common, higher-level environment for solutions that use multiple {@link module:core/editor/editor~Editor editors}
* or plugins that work outside the editor. Use it instead of {@link module:core/editor/editor~Editor.create `Editor.create()`}

View File

@ -6,11 +6,11 @@
* @module core/contextplugin
*/
import { type Collection, type Config, type Locale, type LocaleTranslate } from '@ckeditor/ckeditor5-utils';
import type Editor from './editor/editor.js';
import type { EditorConfig } from './editor/editorconfig.js';
import type Context from './context.js';
import type { PluginDependencies, PluginInterface } from './plugin.js';
import type PluginCollection from './plugincollection.js';
import type Editor from '@ckeditor/ckeditor5-core/src/editor/editor.js';
import type { EditorConfig } from '@ckeditor/ckeditor5-core/src/editor/editorconfig.js';
import type Context from '@ckeditor/ckeditor5-core/src/context.js';
import type { PluginDependencies, PluginInterface } from '@ckeditor/ckeditor5-core/src/plugin.js';
import type PluginCollection from '@ckeditor/ckeditor5-core/src/plugincollection.js';
declare const ContextPlugin_base: {
new (): import("@ckeditor/ckeditor5-utils").Observable;
prototype: import("@ckeditor/ckeditor5-utils").Observable;

View File

@ -6,7 +6,7 @@
* @module core/editingkeystrokehandler
*/
import { KeystrokeHandler, type PriorityString } from '@ckeditor/ckeditor5-utils';
import type Editor from './editor/editor.js';
import type Editor from '@ckeditor/ckeditor5-core/src/editor/editor.js';
/**
* A keystroke handler for editor editing. Its instance is available
* in {@link module:core/editor/editor~Editor#keystrokes} so plugins

View File

@ -8,13 +8,13 @@
import { Config, type Locale, type LocaleTranslate } from '@ckeditor/ckeditor5-utils';
import { Conversion, DataController, EditingController, Model } from '@ckeditor/ckeditor5-engine';
import type { EditorUI } from '@ckeditor/ckeditor5-ui';
import Context from '../context.js';
import PluginCollection from '../plugincollection.js';
import CommandCollection, { type CommandsMap } from '../commandcollection.js';
import EditingKeystrokeHandler from '../editingkeystrokehandler.js';
import Accessibility from '../accessibility.js';
import type { LoadedPlugins, PluginConstructor } from '../plugin.js';
import type { EditorConfig } from './editorconfig.js';
import Context from '@ckeditor/ckeditor5-core/src/context.js';
import PluginCollection from '@ckeditor/ckeditor5-core/src/plugincollection.js';
import CommandCollection, { type CommandsMap } from '@ckeditor/ckeditor5-core/src/commandcollection.js';
import EditingKeystrokeHandler from '@ckeditor/ckeditor5-core/src/editingkeystrokehandler.js';
import Accessibility from '@ckeditor/ckeditor5-core/src/accessibility.js';
import type { LoadedPlugins, PluginConstructor } from '@ckeditor/ckeditor5-core/src/plugin.js';
import type { EditorConfig } from '@ckeditor/ckeditor5-core/src/editor/editorconfig.js';
declare const Editor_base: {
new (): import("@ckeditor/ckeditor5-utils").Observable;
prototype: import("@ckeditor/ckeditor5-utils").Observable;

View File

@ -6,9 +6,9 @@
* @module core/editor/editorconfig
*/
import type { ArrayOrItem, Translations } from '@ckeditor/ckeditor5-utils';
import type Context from '../context.js';
import type { PluginConstructor } from '../plugin.js';
import type Editor from './editor.js';
import type Context from '@ckeditor/ckeditor5-core/src/context.js';
import type { PluginConstructor } from '@ckeditor/ckeditor5-core/src/plugin.js';
import type Editor from '@ckeditor/ckeditor5-core/src/editor/editor.js';
import type { MenuBarConfig } from '@ckeditor/ckeditor5-ui';
/**
* CKEditor configuration options.

View File

@ -2,8 +2,8 @@
* @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/
import type { default as Editor } from '../editor.js';
import type { ElementApi } from './elementapimixin.js';
import type { default as Editor } from '@ckeditor/ckeditor5-core/src/editor/editor.js';
import type { ElementApi } from '@ckeditor/ckeditor5-core/src/editor/utils/elementapimixin.js';
/**
* Checks if the editor is initialized on a `<textarea>` element that belongs to a form. If yes, it updates the editor's element
* content before submitting the form.

View File

@ -5,7 +5,7 @@
/**
* @module core/editor/utils/dataapimixin
*/
import type Editor from '../editor.js';
import type Editor from '@ckeditor/ckeditor5-core/src/editor/editor.js';
import type { Constructor } from '@ckeditor/ckeditor5-utils';
/**
* Implementation of the {@link module:core/editor/utils/dataapimixin~DataApi}.

View File

@ -6,7 +6,7 @@
* @module core/editor/utils/elementapimixin
*/
import { type Constructor, type Mixed } from '@ckeditor/ckeditor5-utils';
import type Editor from '../editor.js';
import type Editor from '@ckeditor/ckeditor5-core/src/editor/editor.js';
/**
* Implementation of the {@link module:core/editor/utils/elementapimixin~ElementApi}.
*/

View File

@ -2,7 +2,7 @@
* @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/
import type { default as Editor } from '../editor.js';
import type { default as Editor } from '@ckeditor/ckeditor5-core/src/editor/editor.js';
/**
* Marks the source element on which the editor was initialized. This prevents other editor instances from using this element.
*

View File

@ -5,23 +5,23 @@
/**
* @module core
*/
export { default as Plugin, type PluginDependencies, type PluginConstructor } from './plugin.js';
export { default as Command, type CommandExecuteEvent } from './command.js';
export { default as MultiCommand } from './multicommand.js';
export type { CommandsMap } from './commandcollection.js';
export type { PluginsMap, default as PluginCollection } from './plugincollection.js';
export { default as Context, type ContextConfig } from './context.js';
export { default as ContextPlugin, type ContextPluginDependencies } from './contextplugin.js';
export { type EditingKeystrokeCallback } from './editingkeystrokehandler.js';
export type { PartialBy, NonEmptyArray } from './typings.js';
export { default as Editor, type EditorReadyEvent, type EditorDestroyEvent } from './editor/editor.js';
export type { EditorConfig, LanguageConfig, ToolbarConfig, ToolbarConfigItem, UiConfig } from './editor/editorconfig.js';
export { default as attachToForm } from './editor/utils/attachtoform.js';
export { default as DataApiMixin, type DataApi } from './editor/utils/dataapimixin.js';
export { default as ElementApiMixin, type ElementApi } from './editor/utils/elementapimixin.js';
export { default as secureSourceElement } from './editor/utils/securesourceelement.js';
export { default as PendingActions, type PendingAction } from './pendingactions.js';
export type { KeystrokeInfos as KeystrokeInfoDefinitions, KeystrokeInfoGroup as KeystrokeInfoGroupDefinition, KeystrokeInfoCategory as KeystrokeInfoCategoryDefinition, KeystrokeInfoDefinition as KeystrokeInfoDefinition } from './accessibility.js';
export { default as Plugin, type PluginDependencies, type PluginConstructor } from '@ckeditor/ckeditor5-core/src/plugin.js';
export { default as Command, type CommandExecuteEvent } from '@ckeditor/ckeditor5-core/src/command.js';
export { default as MultiCommand } from '@ckeditor/ckeditor5-core/src/multicommand.js';
export type { CommandsMap } from '@ckeditor/ckeditor5-core/src/commandcollection.js';
export type { PluginsMap, default as PluginCollection } from '@ckeditor/ckeditor5-core/src/plugincollection.js';
export { default as Context, type ContextConfig } from '@ckeditor/ckeditor5-core/src/context.js';
export { default as ContextPlugin, type ContextPluginDependencies } from '@ckeditor/ckeditor5-core/src/contextplugin.js';
export { type EditingKeystrokeCallback } from '@ckeditor/ckeditor5-core/src/editingkeystrokehandler.js';
export type { PartialBy, NonEmptyArray } from '@ckeditor/ckeditor5-core/src/typings.js';
export { default as Editor, type EditorReadyEvent, type EditorDestroyEvent } from '@ckeditor/ckeditor5-core/src/editor/editor.js';
export type { EditorConfig, LanguageConfig, ToolbarConfig, ToolbarConfigItem, UiConfig } from '@ckeditor/ckeditor5-core/src/editor/editorconfig.js';
export { default as attachToForm } from '@ckeditor/ckeditor5-core/src/editor/utils/attachtoform.js';
export { default as DataApiMixin, type DataApi } from '@ckeditor/ckeditor5-core/src/editor/utils/dataapimixin.js';
export { default as ElementApiMixin, type ElementApi } from '@ckeditor/ckeditor5-core/src/editor/utils/elementapimixin.js';
export { default as secureSourceElement } from '@ckeditor/ckeditor5-core/src/editor/utils/securesourceelement.js';
export { default as PendingActions, type PendingAction } from '@ckeditor/ckeditor5-core/src/pendingactions.js';
export type { KeystrokeInfos as KeystrokeInfoDefinitions, KeystrokeInfoGroup as KeystrokeInfoGroupDefinition, KeystrokeInfoCategory as KeystrokeInfoCategoryDefinition, KeystrokeInfoDefinition as KeystrokeInfoDefinition } from '@ckeditor/ckeditor5-core/src/accessibility.js';
export declare const icons: {
bold: string;
cancel: string;
@ -86,4 +86,4 @@ export declare const icons: {
outdent: string;
table: string;
};
import './augmentation.js';
import '@ckeditor/ckeditor5-core/src/augmentation.js';

View File

@ -5,7 +5,7 @@
/**
* @module core/multicommand
*/
import Command from './command.js';
import Command from '@ckeditor/ckeditor5-core/src/command.js';
import { type PriorityString } from '@ckeditor/ckeditor5-utils';
/**
* A CKEditor command that aggregates other commands.

View File

@ -5,7 +5,7 @@
/**
* @module core/pendingactions
*/
import ContextPlugin from './contextplugin.js';
import ContextPlugin from '@ckeditor/ckeditor5-core/src/contextplugin.js';
import { type CollectionAddEvent, type CollectionRemoveEvent, type Observable } from '@ckeditor/ckeditor5-utils';
/**
* The list of pending editor actions.

View File

@ -2,7 +2,7 @@
* @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/
import type Editor from './editor/editor.js';
import type Editor from '@ckeditor/ckeditor5-core/src/editor/editor.js';
declare const Plugin_base: {
new (): import("@ckeditor/ckeditor5-utils").Observable;
prototype: import("@ckeditor/ckeditor5-utils").Observable;

View File

@ -2,7 +2,7 @@
* @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/
import type { LoadedPlugins, PluginClassConstructor, PluginConstructor, PluginInterface } from './plugin.js';
import type { LoadedPlugins, PluginClassConstructor, PluginConstructor, PluginInterface } from '@ckeditor/ckeditor5-core/src/plugin.js';
declare const PluginCollection_base: {
new (): import("@ckeditor/ckeditor5-utils").Emitter;
prototype: import("@ckeditor/ckeditor5-utils").Emitter;

View File

@ -2,21 +2,21 @@
* @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/
import Mapper from '../conversion/mapper.js';
import DowncastDispatcher from '../conversion/downcastdispatcher.js';
import UpcastDispatcher from '../conversion/upcastdispatcher.js';
import ViewDocumentFragment from '../view/documentfragment.js';
import ViewDocument from '../view/document.js';
import type ViewElement from '../view/element.js';
import type { StylesProcessor } from '../view/stylesmap.js';
import type { MatcherPattern } from '../view/matcher.js';
import type Model from '../model/model.js';
import type ModelElement from '../model/element.js';
import type ModelDocumentFragment from '../model/documentfragment.js';
import type { SchemaContextDefinition } from '../model/schema.js';
import type { BatchType } from '../model/batch.js';
import HtmlDataProcessor from '../dataprocessor/htmldataprocessor.js';
import type DataProcessor from '../dataprocessor/dataprocessor.js';
import Mapper from '@ckeditor/ckeditor5-engine/src/conversion/mapper.js';
import DowncastDispatcher from '@ckeditor/ckeditor5-engine/src/conversion/downcastdispatcher.js';
import UpcastDispatcher from '@ckeditor/ckeditor5-engine/src/conversion/upcastdispatcher.js';
import ViewDocumentFragment from '@ckeditor/ckeditor5-engine/src/view/documentfragment.js';
import ViewDocument from '@ckeditor/ckeditor5-engine/src/view/document.js';
import type ViewElement from '@ckeditor/ckeditor5-engine/src/view/element.js';
import type { StylesProcessor } from '@ckeditor/ckeditor5-engine/src/view/stylesmap.js';
import type { MatcherPattern } from '@ckeditor/ckeditor5-engine/src/view/matcher.js';
import type Model from '@ckeditor/ckeditor5-engine/src/model/model.js';
import type ModelElement from '@ckeditor/ckeditor5-engine/src/model/element.js';
import type ModelDocumentFragment from '@ckeditor/ckeditor5-engine/src/model/documentfragment.js';
import type { SchemaContextDefinition } from '@ckeditor/ckeditor5-engine/src/model/schema.js';
import type { BatchType } from '@ckeditor/ckeditor5-engine/src/model/batch.js';
import HtmlDataProcessor from '@ckeditor/ckeditor5-engine/src/dataprocessor/htmldataprocessor.js';
import type DataProcessor from '@ckeditor/ckeditor5-engine/src/dataprocessor/dataprocessor.js';
declare const DataController_base: {
new (): import("@ckeditor/ckeditor5-utils").Emitter;
prototype: import("@ckeditor/ckeditor5-utils").Emitter;

View File

@ -2,13 +2,13 @@
* @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/
import View from '../view/view.js';
import Mapper from '../conversion/mapper.js';
import DowncastDispatcher from '../conversion/downcastdispatcher.js';
import type { default as Model } from '../model/model.js';
import type ModelItem from '../model/item.js';
import type { Marker } from '../model/markercollection.js';
import type { StylesProcessor } from '../view/stylesmap.js';
import View from '@ckeditor/ckeditor5-engine/src/view/view.js';
import Mapper from '@ckeditor/ckeditor5-engine/src/conversion/mapper.js';
import DowncastDispatcher from '@ckeditor/ckeditor5-engine/src/conversion/downcastdispatcher.js';
import type { default as Model } from '@ckeditor/ckeditor5-engine/src/model/model.js';
import type ModelItem from '@ckeditor/ckeditor5-engine/src/model/item.js';
import type { Marker } from '@ckeditor/ckeditor5-engine/src/model/markercollection.js';
import type { StylesProcessor } from '@ckeditor/ckeditor5-engine/src/view/stylesmap.js';
declare const EditingController_base: {
new (): import("@ckeditor/ckeditor5-utils").Observable;
prototype: import("@ckeditor/ckeditor5-utils").Observable;

View File

@ -6,12 +6,12 @@
* @module engine/conversion/conversion
*/
import { type ArrayOrItem, type PriorityString } from '@ckeditor/ckeditor5-utils';
import UpcastHelpers from './upcasthelpers.js';
import DowncastHelpers, { type AttributeCreatorFunction, type AttributeDescriptor } from './downcasthelpers.js';
import type DowncastDispatcher from './downcastdispatcher.js';
import type UpcastDispatcher from './upcastdispatcher.js';
import type ElementDefinition from '../view/elementdefinition.js';
import type { MatcherPattern } from '../view/matcher.js';
import UpcastHelpers from '@ckeditor/ckeditor5-engine/src/conversion/upcasthelpers.js';
import DowncastHelpers, { type AttributeCreatorFunction, type AttributeDescriptor } from '@ckeditor/ckeditor5-engine/src/conversion/downcasthelpers.js';
import type DowncastDispatcher from '@ckeditor/ckeditor5-engine/src/conversion/downcastdispatcher.js';
import type UpcastDispatcher from '@ckeditor/ckeditor5-engine/src/conversion/upcastdispatcher.js';
import type ElementDefinition from '@ckeditor/ckeditor5-engine/src/view/elementdefinition.js';
import type { MatcherPattern } from '@ckeditor/ckeditor5-engine/src/view/matcher.js';
/**
* A utility class that helps add converters to upcast and downcast dispatchers.
*

View File

@ -5,19 +5,19 @@
/**
* @module engine/conversion/downcastdispatcher
*/
import Consumable from './modelconsumable.js';
import Range from '../model/range.js';
import type { default as Differ, DiffItem } from '../model/differ.js';
import type { default as MarkerCollection } from '../model/markercollection.js';
import type DocumentSelection from '../model/documentselection.js';
import type DowncastWriter from '../view/downcastwriter.js';
import type Element from '../model/element.js';
import type Item from '../model/item.js';
import type Mapper from './mapper.js';
import type Position from '../model/position.js';
import type Schema from '../model/schema.js';
import type Selection from '../model/selection.js';
import type ViewElement from '../view/element.js';
import Consumable from '@ckeditor/ckeditor5-engine/src/conversion/modelconsumable.js';
import Range from '@ckeditor/ckeditor5-engine/src/model/range.js';
import type { default as Differ, DiffItem } from '@ckeditor/ckeditor5-engine/src/model/differ.js';
import type { default as MarkerCollection } from '@ckeditor/ckeditor5-engine/src/model/markercollection.js';
import type DocumentSelection from '@ckeditor/ckeditor5-engine/src/model/documentselection.js';
import type DowncastWriter from '@ckeditor/ckeditor5-engine/src/view/downcastwriter.js';
import type Element from '@ckeditor/ckeditor5-engine/src/model/element.js';
import type Item from '@ckeditor/ckeditor5-engine/src/model/item.js';
import type Mapper from '@ckeditor/ckeditor5-engine/src/conversion/mapper.js';
import type Position from '@ckeditor/ckeditor5-engine/src/model/position.js';
import type Schema from '@ckeditor/ckeditor5-engine/src/model/schema.js';
import type Selection from '@ckeditor/ckeditor5-engine/src/model/selection.js';
import type ViewElement from '@ckeditor/ckeditor5-engine/src/view/element.js';
declare const DowncastDispatcher_base: {
new (): import("@ckeditor/ckeditor5-utils").Emitter;
prototype: import("@ckeditor/ckeditor5-utils").Emitter;

View File

@ -7,23 +7,23 @@
*
* @module engine/conversion/downcasthelpers
*/
import ModelRange from '../model/range.js';
import ModelSelection from '../model/selection.js';
import ModelDocumentSelection from '../model/documentselection.js';
import ModelElement from '../model/element.js';
import ModelPosition from '../model/position.js';
import ViewAttributeElement from '../view/attributeelement.js';
import ConversionHelpers from './conversionhelpers.js';
import type { default as DowncastDispatcher, DowncastConversionApi } from './downcastdispatcher.js';
import type ModelConsumable from './modelconsumable.js';
import type ModelNode from '../model/node.js';
import type ModelItem from '../model/item.js';
import type ModelTextProxy from '../model/textproxy.js';
import type ModelText from '../model/text.js';
import type DowncastWriter from '../view/downcastwriter.js';
import type ElementDefinition from '../view/elementdefinition.js';
import type UIElement from '../view/uielement.js';
import type ViewElement from '../view/element.js';
import ModelRange from '@ckeditor/ckeditor5-engine/src/model/range.js';
import ModelSelection from '@ckeditor/ckeditor5-engine/src/model/selection.js';
import ModelDocumentSelection from '@ckeditor/ckeditor5-engine/src/model/documentselection.js';
import ModelElement from '@ckeditor/ckeditor5-engine/src/model/element.js';
import ModelPosition from '@ckeditor/ckeditor5-engine/src/model/position.js';
import ViewAttributeElement from '@ckeditor/ckeditor5-engine/src/view/attributeelement.js';
import ConversionHelpers from '@ckeditor/ckeditor5-engine/src/conversion/conversionhelpers.js';
import type { default as DowncastDispatcher, DowncastConversionApi } from '@ckeditor/ckeditor5-engine/src/conversion/downcastdispatcher.js';
import type ModelConsumable from '@ckeditor/ckeditor5-engine/src/conversion/modelconsumable.js';
import type ModelNode from '@ckeditor/ckeditor5-engine/src/model/node.js';
import type ModelItem from '@ckeditor/ckeditor5-engine/src/model/item.js';
import type ModelTextProxy from '@ckeditor/ckeditor5-engine/src/model/textproxy.js';
import type ModelText from '@ckeditor/ckeditor5-engine/src/model/text.js';
import type DowncastWriter from '@ckeditor/ckeditor5-engine/src/view/downcastwriter.js';
import type ElementDefinition from '@ckeditor/ckeditor5-engine/src/view/elementdefinition.js';
import type UIElement from '@ckeditor/ckeditor5-engine/src/view/uielement.js';
import type ViewElement from '@ckeditor/ckeditor5-engine/src/view/element.js';
import { type EventInfo, type PriorityString } from '@ckeditor/ckeditor5-utils';
/**
* Downcast conversion helper functions.

View File

@ -5,15 +5,15 @@
/**
* @module engine/conversion/mapper
*/
import ModelPosition from '../model/position.js';
import ModelRange from '../model/range.js';
import ViewPosition from '../view/position.js';
import ViewRange from '../view/range.js';
import type ViewDocumentFragment from '../view/documentfragment.js';
import type ViewElement from '../view/element.js';
import type ModelElement from '../model/element.js';
import type ModelDocumentFragment from '../model/documentfragment.js';
import type ViewNode from '../view/node.js';
import ModelPosition from '@ckeditor/ckeditor5-engine/src/model/position.js';
import ModelRange from '@ckeditor/ckeditor5-engine/src/model/range.js';
import ViewPosition from '@ckeditor/ckeditor5-engine/src/view/position.js';
import ViewRange from '@ckeditor/ckeditor5-engine/src/view/range.js';
import type ViewDocumentFragment from '@ckeditor/ckeditor5-engine/src/view/documentfragment.js';
import type ViewElement from '@ckeditor/ckeditor5-engine/src/view/element.js';
import type ModelElement from '@ckeditor/ckeditor5-engine/src/model/element.js';
import type ModelDocumentFragment from '@ckeditor/ckeditor5-engine/src/model/documentfragment.js';
import type ViewNode from '@ckeditor/ckeditor5-engine/src/view/node.js';
declare const Mapper_base: {
new (): import("@ckeditor/ckeditor5-utils").Emitter;
prototype: import("@ckeditor/ckeditor5-utils").Emitter;

View File

@ -5,11 +5,11 @@
/**
* @module engine/conversion/modelconsumable
*/
import TextProxy from '../model/textproxy.js';
import type Item from '../model/item.js';
import type Selection from '../model/selection.js';
import type DocumentSelection from '../model/documentselection.js';
import type Range from '../model/range.js';
import TextProxy from '@ckeditor/ckeditor5-engine/src/model/textproxy.js';
import type Item from '@ckeditor/ckeditor5-engine/src/model/item.js';
import type Selection from '@ckeditor/ckeditor5-engine/src/model/selection.js';
import type DocumentSelection from '@ckeditor/ckeditor5-engine/src/model/documentselection.js';
import type Range from '@ckeditor/ckeditor5-engine/src/model/range.js';
/**
* Manages a list of consumable values for the {@link module:engine/model/item~Item model items}.
*

View File

@ -5,18 +5,18 @@
/**
* @module engine/conversion/upcastdispatcher
*/
import ViewConsumable from './viewconsumable.js';
import ModelRange from '../model/range.js';
import ModelPosition from '../model/position.js';
import type ModelElement from '../model/element.js';
import type ModelNode from '../model/node.js';
import type ViewElement from '../view/element.js';
import type ViewText from '../view/text.js';
import type ViewDocumentFragment from '../view/documentfragment.js';
import type ModelDocumentFragment from '../model/documentfragment.js';
import type { default as Schema, SchemaContextDefinition } from '../model/schema.js';
import type ModelWriter from '../model/writer.js';
import type ViewItem from '../view/item.js';
import ViewConsumable from '@ckeditor/ckeditor5-engine/src/conversion/viewconsumable.js';
import ModelRange from '@ckeditor/ckeditor5-engine/src/model/range.js';
import ModelPosition from '@ckeditor/ckeditor5-engine/src/model/position.js';
import type ModelElement from '@ckeditor/ckeditor5-engine/src/model/element.js';
import type ModelNode from '@ckeditor/ckeditor5-engine/src/model/node.js';
import type ViewElement from '@ckeditor/ckeditor5-engine/src/view/element.js';
import type ViewText from '@ckeditor/ckeditor5-engine/src/view/text.js';
import type ViewDocumentFragment from '@ckeditor/ckeditor5-engine/src/view/documentfragment.js';
import type ModelDocumentFragment from '@ckeditor/ckeditor5-engine/src/model/documentfragment.js';
import type { default as Schema, SchemaContextDefinition } from '@ckeditor/ckeditor5-engine/src/model/schema.js';
import type ModelWriter from '@ckeditor/ckeditor5-engine/src/model/writer.js';
import type ViewItem from '@ckeditor/ckeditor5-engine/src/view/item.js';
declare const UpcastDispatcher_base: {
new (): import("@ckeditor/ckeditor5-utils").Emitter;
prototype: import("@ckeditor/ckeditor5-utils").Emitter;

View File

@ -2,15 +2,15 @@
* @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/
import { type ClassPatterns, type MatcherPattern, type PropertyPatterns } from '../view/matcher.js';
import ConversionHelpers from './conversionhelpers.js';
import type { default as UpcastDispatcher, UpcastConversionApi, UpcastConversionData } from './upcastdispatcher.js';
import type ModelElement from '../model/element.js';
import type { ViewDocumentFragment, ViewElement, ViewText } from '../index.js';
import type Mapper from './mapper.js';
import type Model from '../model/model.js';
import type ViewSelection from '../view/selection.js';
import type ViewDocumentSelection from '../view/documentselection.js';
import { type ClassPatterns, type MatcherPattern, type PropertyPatterns } from '@ckeditor/ckeditor5-engine/src/view/matcher.js';
import ConversionHelpers from '@ckeditor/ckeditor5-engine/src/conversion/conversionhelpers.js';
import type { default as UpcastDispatcher, UpcastConversionApi, UpcastConversionData } from '@ckeditor/ckeditor5-engine/src/conversion/upcastdispatcher.js';
import type ModelElement from '@ckeditor/ckeditor5-engine/src/model/element.js';
import type { ViewDocumentFragment, ViewElement, ViewText } from '@ckeditor/ckeditor5-engine';
import type Mapper from '@ckeditor/ckeditor5-engine/src/conversion/mapper.js';
import type Model from '@ckeditor/ckeditor5-engine/src/model/model.js';
import type ViewSelection from '@ckeditor/ckeditor5-engine/src/view/selection.js';
import type ViewDocumentSelection from '@ckeditor/ckeditor5-engine/src/view/documentselection.js';
import { type EventInfo, type PriorityString } from '@ckeditor/ckeditor5-utils';
/**
* Contains the {@link module:engine/view/view view} to {@link module:engine/model/model model} converters for

View File

@ -2,11 +2,11 @@
* @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/
import type Element from '../view/element.js';
import type Node from '../view/node.js';
import type Text from '../view/text.js';
import type DocumentFragment from '../view/documentfragment.js';
import type { Match } from '../view/matcher.js';
import type Element from '@ckeditor/ckeditor5-engine/src/view/element.js';
import type Node from '@ckeditor/ckeditor5-engine/src/view/node.js';
import type Text from '@ckeditor/ckeditor5-engine/src/view/text.js';
import type DocumentFragment from '@ckeditor/ckeditor5-engine/src/view/documentfragment.js';
import type { Match } from '@ckeditor/ckeditor5-engine/src/view/matcher.js';
/**
* Class used for handling consumption of view {@link module:engine/view/element~Element elements},
* {@link module:engine/view/text~Text text nodes} and {@link module:engine/view/documentfragment~DocumentFragment document fragments}.

View File

@ -5,8 +5,8 @@
/**
* @module engine/dataprocessor/dataprocessor
*/
import type ViewDocumentFragment from '../view/documentfragment.js';
import type { MatcherPattern } from '../view/matcher.js';
import type ViewDocumentFragment from '@ckeditor/ckeditor5-engine/src/view/documentfragment.js';
import type { MatcherPattern } from '@ckeditor/ckeditor5-engine/src/view/matcher.js';
/**
* The data processor interface. It should be implemented by actual data processors.
*

View File

@ -2,12 +2,12 @@
* @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/
import DomConverter from '../view/domconverter.js';
import type DataProcessor from './dataprocessor.js';
import type HtmlWriter from './htmlwriter.js';
import type ViewDocument from '../view/document.js';
import type ViewDocumentFragment from '../view/documentfragment.js';
import type { MatcherPattern } from '../view/matcher.js';
import DomConverter from '@ckeditor/ckeditor5-engine/src/view/domconverter.js';
import type DataProcessor from '@ckeditor/ckeditor5-engine/src/dataprocessor/dataprocessor.js';
import type HtmlWriter from '@ckeditor/ckeditor5-engine/src/dataprocessor/htmlwriter.js';
import type ViewDocument from '@ckeditor/ckeditor5-engine/src/view/document.js';
import type ViewDocumentFragment from '@ckeditor/ckeditor5-engine/src/view/documentfragment.js';
import type { MatcherPattern } from '@ckeditor/ckeditor5-engine/src/view/matcher.js';
/**
* The HTML data processor class.
* This data processor implementation uses HTML as input and output data.

View File

@ -5,109 +5,109 @@
/**
* @module engine
*/
export * from './view/placeholder.js';
export { default as EditingController } from './controller/editingcontroller.js';
export { default as DataController, type DataControllerInitEvent, type DataControllerSetEvent, type DataControllerToModelEvent, type DataControllerToViewEvent } from './controller/datacontroller.js';
export { default as Conversion } from './conversion/conversion.js';
export type { default as DowncastDispatcher, DowncastAddMarkerEvent, DowncastAttributeEvent, DowncastConversionApi, DowncastInsertEvent, DowncastRemoveEvent, DowncastRemoveMarkerEvent, DowncastSelectionEvent } from './conversion/downcastdispatcher.js';
export type { default as UpcastDispatcher, UpcastConversionApi, UpcastConversionData, UpcastElementEvent, UpcastTextEvent } from './conversion/upcastdispatcher.js';
export type { AddHighlightCallback, AttributeDescriptor, ElementCreatorFunction, HighlightDescriptor, RemoveHighlightCallback, MarkerElementCreatorFunction, SlotFilter } from './conversion/downcasthelpers.js';
export type { default as Mapper, MapperModelToViewPositionEvent, MapperViewToModelPositionEvent } from './conversion/mapper.js';
export type { default as ModelConsumable } from './conversion/modelconsumable.js';
export type { Consumables, default as ViewConsumable } from './conversion/viewconsumable.js';
export type { default as DataProcessor } from './dataprocessor/dataprocessor.js';
export { default as HtmlDataProcessor } from './dataprocessor/htmldataprocessor.js';
export type { default as Operation } from './model/operation/operation.js';
export { default as InsertOperation } from './model/operation/insertoperation.js';
export { default as MoveOperation } from './model/operation/moveoperation.js';
export { default as MergeOperation } from './model/operation/mergeoperation.js';
export { default as SplitOperation } from './model/operation/splitoperation.js';
export { default as MarkerOperation } from './model/operation/markeroperation.js';
export { default as OperationFactory } from './model/operation/operationfactory.js';
export { default as AttributeOperation } from './model/operation/attributeoperation.js';
export { default as RenameOperation } from './model/operation/renameoperation.js';
export { default as RootAttributeOperation } from './model/operation/rootattributeoperation.js';
export { default as RootOperation } from './model/operation/rootoperation.js';
export { default as NoOperation } from './model/operation/nooperation.js';
export { transformSets } from './model/operation/transform.js';
export { default as DocumentSelection, type DocumentSelectionChangeRangeEvent, type DocumentSelectionChangeMarkerEvent, type DocumentSelectionChangeAttributeEvent } from './model/documentselection.js';
export { default as Range } from './model/range.js';
export { default as LiveRange, type LiveRangeChangeRangeEvent } from './model/liverange.js';
export { default as LivePosition } from './model/liveposition.js';
export { default as Model } from './model/model.js';
export { default as TreeWalker, type TreeWalkerValue } from './model/treewalker.js';
export { default as Element } from './model/element.js';
export { default as Position, type PositionOffset } from './model/position.js';
export { default as DocumentFragment } from './model/documentfragment.js';
export { default as History } from './model/history.js';
export { default as Text } from './model/text.js';
export { default as TextProxy } from './model/textproxy.js';
export type { default as Document, ModelPostFixer } from './model/document.js';
export type { Marker } from './model/markercollection.js';
export type { default as Batch } from './model/batch.js';
export type { default as Differ, DiffItem, DiffItemAttribute, DiffItemInsert, DiffItemRemove } from './model/differ.js';
export type { default as Item } from './model/item.js';
export type { default as Node, NodeAttributes } from './model/node.js';
export type { default as RootElement } from './model/rootelement.js';
export type { default as Schema, SchemaAttributeCheckCallback, SchemaChildCheckCallback, AttributeProperties, SchemaItemDefinition } from './model/schema.js';
export type { default as Selection, Selectable } from './model/selection.js';
export type { default as TypeCheckable } from './model/typecheckable.js';
export type { default as Writer } from './model/writer.js';
export type { DocumentChangeEvent } from './model/document.js';
export type { DocumentSelectionChangeEvent } from './model/documentselection.js';
export type { ModelApplyOperationEvent, ModelDeleteContentEvent, ModelGetSelectedContentEvent, ModelInsertContentEvent, ModelInsertObjectEvent, ModelModifySelectionEvent, ModelCanEditAtEvent } from './model/model.js';
export type { SelectionChangeRangeEvent } from './model/selection.js';
export { default as DataTransfer } from './view/datatransfer.js';
export { default as DomConverter } from './view/domconverter.js';
export { default as Renderer } from './view/renderer.js';
export { default as EditingView } from './view/view.js';
export { default as ViewDocument } from './view/document.js';
export { default as ViewText } from './view/text.js';
export { default as ViewElement, type ElementAttributes as ViewElementAttributes } from './view/element.js';
export { default as ViewContainerElement } from './view/containerelement.js';
export { default as ViewEditableElement } from './view/editableelement.js';
export { default as ViewRootEditableElement } from './view/rooteditableelement.js';
export { default as ViewAttributeElement } from './view/attributeelement.js';
export { default as ViewEmptyElement } from './view/emptyelement.js';
export { default as ViewRawElement } from './view/rawelement.js';
export { default as ViewUIElement } from './view/uielement.js';
export { default as ViewDocumentFragment } from './view/documentfragment.js';
export { default as ViewTreeWalker, type TreeWalkerValue as ViewTreeWalkerValue } from './view/treewalker.js';
export type { default as ViewElementDefinition, ElementObjectDefinition } from './view/elementdefinition.js';
export type { default as ViewDocumentSelection } from './view/documentselection.js';
export { default as AttributeElement } from './view/attributeelement.js';
export type { default as ViewItem } from './view/item.js';
export type { default as ViewNode } from './view/node.js';
export type { default as ViewPosition, PositionOffset as ViewPositionOffset } from './view/position.js';
export type { default as ViewRange } from './view/range.js';
export type { default as ViewSelection, ViewSelectionChangeEvent, Selectable as ViewSelectable } from './view/selection.js';
export type { default as ViewTypeCheckable } from './view/typecheckable.js';
export { getFillerOffset } from './view/containerelement.js';
export { default as Observer } from './view/observer/observer.js';
export { default as ClickObserver } from './view/observer/clickobserver.js';
export { default as DomEventObserver } from './view/observer/domeventobserver.js';
export { default as MouseObserver } from './view/observer/mouseobserver.js';
export { default as TabObserver } from './view/observer/tabobserver.js';
export { default as FocusObserver } from './view/observer/focusobserver.js';
export { default as DowncastWriter } from './view/downcastwriter.js';
export { default as UpcastWriter } from './view/upcastwriter.js';
export { default as Matcher, type MatcherPattern, type MatcherObjectPattern, type Match, type MatchResult } from './view/matcher.js';
export { default as BubblingEventInfo } from './view/observer/bubblingeventinfo.js';
export { default as DomEventData } from './view/observer/domeventdata.js';
export type { BubblingEvent } from './view/observer/bubblingemittermixin.js';
export type { ViewDocumentArrowKeyEvent } from './view/observer/arrowkeysobserver.js';
export type { ViewDocumentCompositionStartEvent, ViewDocumentCompositionUpdateEvent, ViewDocumentCompositionEndEvent } from './view/observer/compositionobserver.js';
export type { ViewDocumentInputEvent } from './view/observer/inputobserver.js';
export type { ViewDocumentKeyDownEvent, ViewDocumentKeyUpEvent, KeyEventData } from './view/observer/keyobserver.js';
export type { ViewDocumentLayoutChangedEvent } from './view/document.js';
export type { ViewDocumentMouseDownEvent, ViewDocumentMouseUpEvent, ViewDocumentMouseOverEvent, ViewDocumentMouseOutEvent } from './view/observer/mouseobserver.js';
export type { ViewDocumentTabEvent } from './view/observer/tabobserver.js';
export type { ViewDocumentClickEvent } from './view/observer/clickobserver.js';
export type { ViewDocumentSelectionChangeEvent } from './view/observer/selectionobserver.js';
export type { ViewRenderEvent, ViewScrollToTheSelectionEvent } from './view/view.js';
export { default as StylesMap, StylesProcessor, type BoxSides } from './view/stylesmap.js';
export * from './view/styles/background.js';
export * from './view/styles/border.js';
export * from './view/styles/margin.js';
export * from './view/styles/padding.js';
export * from './view/styles/utils.js';
export * from '@ckeditor/ckeditor5-engine/src/view/placeholder.js';
export { default as EditingController } from '@ckeditor/ckeditor5-engine/src/controller/editingcontroller.js';
export { default as DataController, type DataControllerInitEvent, type DataControllerSetEvent, type DataControllerToModelEvent, type DataControllerToViewEvent } from '@ckeditor/ckeditor5-engine/src/controller/datacontroller.js';
export { default as Conversion } from '@ckeditor/ckeditor5-engine/src/conversion/conversion.js';
export type { default as DowncastDispatcher, DowncastAddMarkerEvent, DowncastAttributeEvent, DowncastConversionApi, DowncastInsertEvent, DowncastRemoveEvent, DowncastRemoveMarkerEvent, DowncastSelectionEvent } from '@ckeditor/ckeditor5-engine/src/conversion/downcastdispatcher.js';
export type { default as UpcastDispatcher, UpcastConversionApi, UpcastConversionData, UpcastElementEvent, UpcastTextEvent } from '@ckeditor/ckeditor5-engine/src/conversion/upcastdispatcher.js';
export type { AddHighlightCallback, AttributeDescriptor, ElementCreatorFunction, HighlightDescriptor, RemoveHighlightCallback, MarkerElementCreatorFunction, SlotFilter } from '@ckeditor/ckeditor5-engine/src/conversion/downcasthelpers.js';
export type { default as Mapper, MapperModelToViewPositionEvent, MapperViewToModelPositionEvent } from '@ckeditor/ckeditor5-engine/src/conversion/mapper.js';
export type { default as ModelConsumable } from '@ckeditor/ckeditor5-engine/src/conversion/modelconsumable.js';
export type { Consumables, default as ViewConsumable } from '@ckeditor/ckeditor5-engine/src/conversion/viewconsumable.js';
export type { default as DataProcessor } from '@ckeditor/ckeditor5-engine/src/dataprocessor/dataprocessor.js';
export { default as HtmlDataProcessor } from '@ckeditor/ckeditor5-engine/src/dataprocessor/htmldataprocessor.js';
export type { default as Operation } from '@ckeditor/ckeditor5-engine/src/model/operation/operation.js';
export { default as InsertOperation } from '@ckeditor/ckeditor5-engine/src/model/operation/insertoperation.js';
export { default as MoveOperation } from '@ckeditor/ckeditor5-engine/src/model/operation/moveoperation.js';
export { default as MergeOperation } from '@ckeditor/ckeditor5-engine/src/model/operation/mergeoperation.js';
export { default as SplitOperation } from '@ckeditor/ckeditor5-engine/src/model/operation/splitoperation.js';
export { default as MarkerOperation } from '@ckeditor/ckeditor5-engine/src/model/operation/markeroperation.js';
export { default as OperationFactory } from '@ckeditor/ckeditor5-engine/src/model/operation/operationfactory.js';
export { default as AttributeOperation } from '@ckeditor/ckeditor5-engine/src/model/operation/attributeoperation.js';
export { default as RenameOperation } from '@ckeditor/ckeditor5-engine/src/model/operation/renameoperation.js';
export { default as RootAttributeOperation } from '@ckeditor/ckeditor5-engine/src/model/operation/rootattributeoperation.js';
export { default as RootOperation } from '@ckeditor/ckeditor5-engine/src/model/operation/rootoperation.js';
export { default as NoOperation } from '@ckeditor/ckeditor5-engine/src/model/operation/nooperation.js';
export { transformSets } from '@ckeditor/ckeditor5-engine/src/model/operation/transform.js';
export { default as DocumentSelection, type DocumentSelectionChangeRangeEvent, type DocumentSelectionChangeMarkerEvent, type DocumentSelectionChangeAttributeEvent } from '@ckeditor/ckeditor5-engine/src/model/documentselection.js';
export { default as Range } from '@ckeditor/ckeditor5-engine/src/model/range.js';
export { default as LiveRange, type LiveRangeChangeRangeEvent } from '@ckeditor/ckeditor5-engine/src/model/liverange.js';
export { default as LivePosition } from '@ckeditor/ckeditor5-engine/src/model/liveposition.js';
export { default as Model } from '@ckeditor/ckeditor5-engine/src/model/model.js';
export { default as TreeWalker, type TreeWalkerValue } from '@ckeditor/ckeditor5-engine/src/model/treewalker.js';
export { default as Element } from '@ckeditor/ckeditor5-engine/src/model/element.js';
export { default as Position, type PositionOffset } from '@ckeditor/ckeditor5-engine/src/model/position.js';
export { default as DocumentFragment } from '@ckeditor/ckeditor5-engine/src/model/documentfragment.js';
export { default as History } from '@ckeditor/ckeditor5-engine/src/model/history.js';
export { default as Text } from '@ckeditor/ckeditor5-engine/src/model/text.js';
export { default as TextProxy } from '@ckeditor/ckeditor5-engine/src/model/textproxy.js';
export type { default as Document, ModelPostFixer } from '@ckeditor/ckeditor5-engine/src/model/document.js';
export type { Marker } from '@ckeditor/ckeditor5-engine/src/model/markercollection.js';
export type { default as Batch } from '@ckeditor/ckeditor5-engine/src/model/batch.js';
export type { default as Differ, DiffItem, DiffItemAttribute, DiffItemInsert, DiffItemRemove } from '@ckeditor/ckeditor5-engine/src/model/differ.js';
export type { default as Item } from '@ckeditor/ckeditor5-engine/src/model/item.js';
export type { default as Node, NodeAttributes } from '@ckeditor/ckeditor5-engine/src/model/node.js';
export type { default as RootElement } from '@ckeditor/ckeditor5-engine/src/model/rootelement.js';
export type { default as Schema, SchemaAttributeCheckCallback, SchemaChildCheckCallback, AttributeProperties, SchemaItemDefinition } from '@ckeditor/ckeditor5-engine/src/model/schema.js';
export type { default as Selection, Selectable } from '@ckeditor/ckeditor5-engine/src/model/selection.js';
export type { default as TypeCheckable } from '@ckeditor/ckeditor5-engine/src/model/typecheckable.js';
export type { default as Writer } from '@ckeditor/ckeditor5-engine/src/model/writer.js';
export type { DocumentChangeEvent } from '@ckeditor/ckeditor5-engine/src/model/document.js';
export type { DocumentSelectionChangeEvent } from '@ckeditor/ckeditor5-engine/src/model/documentselection.js';
export type { ModelApplyOperationEvent, ModelDeleteContentEvent, ModelGetSelectedContentEvent, ModelInsertContentEvent, ModelInsertObjectEvent, ModelModifySelectionEvent, ModelCanEditAtEvent } from '@ckeditor/ckeditor5-engine/src/model/model.js';
export type { SelectionChangeRangeEvent } from '@ckeditor/ckeditor5-engine/src/model/selection.js';
export { default as DataTransfer } from '@ckeditor/ckeditor5-engine/src/view/datatransfer.js';
export { default as DomConverter } from '@ckeditor/ckeditor5-engine/src/view/domconverter.js';
export { default as Renderer } from '@ckeditor/ckeditor5-engine/src/view/renderer.js';
export { default as EditingView } from '@ckeditor/ckeditor5-engine/src/view/view.js';
export { default as ViewDocument } from '@ckeditor/ckeditor5-engine/src/view/document.js';
export { default as ViewText } from '@ckeditor/ckeditor5-engine/src/view/text.js';
export { default as ViewElement, type ElementAttributes as ViewElementAttributes } from '@ckeditor/ckeditor5-engine/src/view/element.js';
export { default as ViewContainerElement } from '@ckeditor/ckeditor5-engine/src/view/containerelement.js';
export { default as ViewEditableElement } from '@ckeditor/ckeditor5-engine/src/view/editableelement.js';
export { default as ViewRootEditableElement } from '@ckeditor/ckeditor5-engine/src/view/rooteditableelement.js';
export { default as ViewAttributeElement } from '@ckeditor/ckeditor5-engine/src/view/attributeelement.js';
export { default as ViewEmptyElement } from '@ckeditor/ckeditor5-engine/src/view/emptyelement.js';
export { default as ViewRawElement } from '@ckeditor/ckeditor5-engine/src/view/rawelement.js';
export { default as ViewUIElement } from '@ckeditor/ckeditor5-engine/src/view/uielement.js';
export { default as ViewDocumentFragment } from '@ckeditor/ckeditor5-engine/src/view/documentfragment.js';
export { default as ViewTreeWalker, type TreeWalkerValue as ViewTreeWalkerValue } from '@ckeditor/ckeditor5-engine/src/view/treewalker.js';
export type { default as ViewElementDefinition, ElementObjectDefinition } from '@ckeditor/ckeditor5-engine/src/view/elementdefinition.js';
export type { default as ViewDocumentSelection } from '@ckeditor/ckeditor5-engine/src/view/documentselection.js';
export { default as AttributeElement } from '@ckeditor/ckeditor5-engine/src/view/attributeelement.js';
export type { default as ViewItem } from '@ckeditor/ckeditor5-engine/src/view/item.js';
export type { default as ViewNode } from '@ckeditor/ckeditor5-engine/src/view/node.js';
export type { default as ViewPosition, PositionOffset as ViewPositionOffset } from '@ckeditor/ckeditor5-engine/src/view/position.js';
export type { default as ViewRange } from '@ckeditor/ckeditor5-engine/src/view/range.js';
export type { default as ViewSelection, ViewSelectionChangeEvent, Selectable as ViewSelectable } from '@ckeditor/ckeditor5-engine/src/view/selection.js';
export type { default as ViewTypeCheckable } from '@ckeditor/ckeditor5-engine/src/view/typecheckable.js';
export { getFillerOffset } from '@ckeditor/ckeditor5-engine/src/view/containerelement.js';
export { default as Observer } from '@ckeditor/ckeditor5-engine/src/view/observer/observer.js';
export { default as ClickObserver } from '@ckeditor/ckeditor5-engine/src/view/observer/clickobserver.js';
export { default as DomEventObserver } from '@ckeditor/ckeditor5-engine/src/view/observer/domeventobserver.js';
export { default as MouseObserver } from '@ckeditor/ckeditor5-engine/src/view/observer/mouseobserver.js';
export { default as TabObserver } from '@ckeditor/ckeditor5-engine/src/view/observer/tabobserver.js';
export { default as FocusObserver } from '@ckeditor/ckeditor5-engine/src/view/observer/focusobserver.js';
export { default as DowncastWriter } from '@ckeditor/ckeditor5-engine/src/view/downcastwriter.js';
export { default as UpcastWriter } from '@ckeditor/ckeditor5-engine/src/view/upcastwriter.js';
export { default as Matcher, type MatcherPattern, type MatcherObjectPattern, type Match, type MatchResult } from '@ckeditor/ckeditor5-engine/src/view/matcher.js';
export { default as BubblingEventInfo } from '@ckeditor/ckeditor5-engine/src/view/observer/bubblingeventinfo.js';
export { default as DomEventData } from '@ckeditor/ckeditor5-engine/src/view/observer/domeventdata.js';
export type { BubblingEvent } from '@ckeditor/ckeditor5-engine/src/view/observer/bubblingemittermixin.js';
export type { ViewDocumentArrowKeyEvent } from '@ckeditor/ckeditor5-engine/src/view/observer/arrowkeysobserver.js';
export type { ViewDocumentCompositionStartEvent, ViewDocumentCompositionUpdateEvent, ViewDocumentCompositionEndEvent } from '@ckeditor/ckeditor5-engine/src/view/observer/compositionobserver.js';
export type { ViewDocumentInputEvent } from '@ckeditor/ckeditor5-engine/src/view/observer/inputobserver.js';
export type { ViewDocumentKeyDownEvent, ViewDocumentKeyUpEvent, KeyEventData } from '@ckeditor/ckeditor5-engine/src/view/observer/keyobserver.js';
export type { ViewDocumentLayoutChangedEvent } from '@ckeditor/ckeditor5-engine/src/view/document.js';
export type { ViewDocumentMouseDownEvent, ViewDocumentMouseUpEvent, ViewDocumentMouseOverEvent, ViewDocumentMouseOutEvent } from '@ckeditor/ckeditor5-engine/src/view/observer/mouseobserver.js';
export type { ViewDocumentTabEvent } from '@ckeditor/ckeditor5-engine/src/view/observer/tabobserver.js';
export type { ViewDocumentClickEvent } from '@ckeditor/ckeditor5-engine/src/view/observer/clickobserver.js';
export type { ViewDocumentSelectionChangeEvent } from '@ckeditor/ckeditor5-engine/src/view/observer/selectionobserver.js';
export type { ViewRenderEvent, ViewScrollToTheSelectionEvent } from '@ckeditor/ckeditor5-engine/src/view/view.js';
export { default as StylesMap, StylesProcessor, type BoxSides } from '@ckeditor/ckeditor5-engine/src/view/stylesmap.js';
export * from '@ckeditor/ckeditor5-engine/src/view/styles/background.js';
export * from '@ckeditor/ckeditor5-engine/src/view/styles/border.js';
export * from '@ckeditor/ckeditor5-engine/src/view/styles/margin.js';
export * from '@ckeditor/ckeditor5-engine/src/view/styles/padding.js';
export * from '@ckeditor/ckeditor5-engine/src/view/styles/utils.js';

View File

@ -2,7 +2,7 @@
* @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/
import type Operation from './operation/operation.js';
import type Operation from '@ckeditor/ckeditor5-engine/src/model/operation/operation.js';
/**
* A batch instance groups model changes ({@link module:engine/model/operation/operation~Operation operations}). All operations
* grouped in a single batch can be reverted together, so you can also think about a batch as of a single undo step. If you want

View File

@ -5,13 +5,13 @@
/**
* @module engine/model/differ
*/
import Position from './position.js';
import Range from './range.js';
import type { default as MarkerCollection, MarkerData } from './markercollection.js';
import type Element from './element.js';
import type Item from './item.js';
import type RootElement from './rootelement.js';
import type Operation from './operation/operation.js';
import Position from '@ckeditor/ckeditor5-engine/src/model/position.js';
import Range from '@ckeditor/ckeditor5-engine/src/model/range.js';
import type { default as MarkerCollection, MarkerData } from '@ckeditor/ckeditor5-engine/src/model/markercollection.js';
import type Element from '@ckeditor/ckeditor5-engine/src/model/element.js';
import type Item from '@ckeditor/ckeditor5-engine/src/model/item.js';
import type RootElement from '@ckeditor/ckeditor5-engine/src/model/rootelement.js';
import type Operation from '@ckeditor/ckeditor5-engine/src/model/operation/operation.js';
/**
* Calculates the difference between two model states.
*

View File

@ -5,14 +5,14 @@
/**
* @module engine/model/document
*/
import Differ from './differ.js';
import DocumentSelection from './documentselection.js';
import History from './history.js';
import RootElement from './rootelement.js';
import type { default as Model } from './model.js';
import type Batch from './batch.js';
import type Range from './range.js';
import type Writer from './writer.js';
import Differ from '@ckeditor/ckeditor5-engine/src/model/differ.js';
import DocumentSelection from '@ckeditor/ckeditor5-engine/src/model/documentselection.js';
import History from '@ckeditor/ckeditor5-engine/src/model/history.js';
import RootElement from '@ckeditor/ckeditor5-engine/src/model/rootelement.js';
import type { default as Model } from '@ckeditor/ckeditor5-engine/src/model/model.js';
import type Batch from '@ckeditor/ckeditor5-engine/src/model/batch.js';
import type Range from '@ckeditor/ckeditor5-engine/src/model/range.js';
import type Writer from '@ckeditor/ckeditor5-engine/src/model/writer.js';
import { Collection } from '@ckeditor/ckeditor5-utils';
declare const Document_base: {
new (): import("@ckeditor/ckeditor5-utils").Emitter;

View File

@ -5,10 +5,10 @@
/**
* @module engine/model/documentfragment
*/
import TypeCheckable from './typecheckable.js';
import type Item from './item.js';
import type Node from './node.js';
import type Range from './range.js';
import TypeCheckable from '@ckeditor/ckeditor5-engine/src/model/typecheckable.js';
import type Item from '@ckeditor/ckeditor5-engine/src/model/item.js';
import type Node from '@ckeditor/ckeditor5-engine/src/model/node.js';
import type Range from '@ckeditor/ckeditor5-engine/src/model/range.js';
/**
* DocumentFragment represents a part of model which does not have a common root but its top-level nodes
* can be seen as siblings. In other words, it is a detached part of model tree, without a root.

View File

@ -5,14 +5,14 @@
/**
* @module engine/model/documentselection
*/
import TypeCheckable from './typecheckable.js';
import Selection, { type SelectionChangeAttributeEvent, type SelectionChangeRangeEvent } from './selection.js';
import type { default as Document } from './document.js';
import type { Marker } from './markercollection.js';
import type Element from './element.js';
import type Item from './item.js';
import type { default as Position, PositionOffset } from './position.js';
import type Range from './range.js';
import TypeCheckable from '@ckeditor/ckeditor5-engine/src/model/typecheckable.js';
import Selection, { type SelectionChangeAttributeEvent, type SelectionChangeRangeEvent } from '@ckeditor/ckeditor5-engine/src/model/selection.js';
import type { default as Document } from '@ckeditor/ckeditor5-engine/src/model/document.js';
import type { Marker } from '@ckeditor/ckeditor5-engine/src/model/markercollection.js';
import type Element from '@ckeditor/ckeditor5-engine/src/model/element.js';
import type Item from '@ckeditor/ckeditor5-engine/src/model/item.js';
import type { default as Position, PositionOffset } from '@ckeditor/ckeditor5-engine/src/model/position.js';
import type Range from '@ckeditor/ckeditor5-engine/src/model/range.js';
import { Collection } from '@ckeditor/ckeditor5-utils';
declare const DocumentSelection_base: import("@ckeditor/ckeditor5-utils").Mixed<typeof TypeCheckable, import("@ckeditor/ckeditor5-utils").Emitter>;
/**

View File

@ -5,8 +5,8 @@
/**
* @module engine/model/element
*/
import Node, { type NodeAttributes } from './node.js';
import type Item from './item.js';
import Node, { type NodeAttributes } from '@ckeditor/ckeditor5-engine/src/model/node.js';
import type Item from '@ckeditor/ckeditor5-engine/src/model/item.js';
/**
* Model element. Type of {@link module:engine/model/node~Node node} that has a {@link module:engine/model/element~Element#name name} and
* {@link module:engine/model/element~Element#getChildren child nodes}.

View File

@ -2,7 +2,7 @@
* @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/
import type Operation from './operation/operation.js';
import type Operation from '@ckeditor/ckeditor5-engine/src/model/operation/operation.js';
/**
* @module engine/model/history
*/

View File

@ -2,8 +2,8 @@
* @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/
import type Node from './node.js';
import type TextProxy from './textproxy.js';
import type Node from '@ckeditor/ckeditor5-engine/src/model/node.js';
import type TextProxy from '@ckeditor/ckeditor5-engine/src/model/textproxy.js';
/**
* @module engine/model/item
*/

View File

@ -5,10 +5,10 @@
/**
* @module engine/model/liveposition
*/
import Position, { type PositionOffset, type PositionStickiness } from './position.js';
import type DocumentFragment from './documentfragment.js';
import type Item from './item.js';
import type RootElement from './rootelement.js';
import Position, { type PositionOffset, type PositionStickiness } from '@ckeditor/ckeditor5-engine/src/model/position.js';
import type DocumentFragment from '@ckeditor/ckeditor5-engine/src/model/documentfragment.js';
import type Item from '@ckeditor/ckeditor5-engine/src/model/item.js';
import type RootElement from '@ckeditor/ckeditor5-engine/src/model/rootelement.js';
declare const LivePosition_base: import("@ckeditor/ckeditor5-utils").Mixed<typeof Position, import("@ckeditor/ckeditor5-utils").Emitter>;
/**
* `LivePosition` is a type of {@link module:engine/model/position~Position Position}

View File

@ -5,11 +5,11 @@
/**
* @module engine/model/liverange
*/
import Range from './range.js';
import type DocumentFragment from './documentfragment.js';
import type Element from './element.js';
import type Item from './item.js';
import type Position from './position.js';
import Range from '@ckeditor/ckeditor5-engine/src/model/range.js';
import type DocumentFragment from '@ckeditor/ckeditor5-engine/src/model/documentfragment.js';
import type Element from '@ckeditor/ckeditor5-engine/src/model/element.js';
import type Item from '@ckeditor/ckeditor5-engine/src/model/item.js';
import type Position from '@ckeditor/ckeditor5-engine/src/model/position.js';
declare const LiveRange_base: import("@ckeditor/ckeditor5-utils").Mixed<typeof Range, import("@ckeditor/ckeditor5-utils").Emitter>;
/**
* `LiveRange` is a type of {@link module:engine/model/range~Range Range}

View File

@ -5,10 +5,10 @@
/**
* @module engine/model/markercollection
*/
import TypeCheckable from './typecheckable.js';
import LiveRange, { type LiveRangeChangeContentEvent, type LiveRangeChangeRangeEvent, type LiveRangeChangeEvent } from './liverange.js';
import type Position from './position.js';
import type Range from './range.js';
import TypeCheckable from '@ckeditor/ckeditor5-engine/src/model/typecheckable.js';
import LiveRange, { type LiveRangeChangeContentEvent, type LiveRangeChangeRangeEvent, type LiveRangeChangeEvent } from '@ckeditor/ckeditor5-engine/src/model/liverange.js';
import type Position from '@ckeditor/ckeditor5-engine/src/model/position.js';
import type Range from '@ckeditor/ckeditor5-engine/src/model/range.js';
declare const MarkerCollection_base: {
new (): import("@ckeditor/ckeditor5-utils").Emitter;
prototype: import("@ckeditor/ckeditor5-utils").Emitter;

View File

@ -5,20 +5,20 @@
/**
* @module engine/model/model
*/
import Batch, { type BatchType } from './batch.js';
import Document from './document.js';
import MarkerCollection from './markercollection.js';
import ModelPosition, { type PositionOffset, type PositionStickiness } from './position.js';
import ModelRange from './range.js';
import ModelSelection, { type PlaceOrOffset, type Selectable } from './selection.js';
import DocumentSelection from './documentselection.js';
import Schema from './schema.js';
import Writer from './writer.js';
import Node from './node.js';
import type ModelDocumentFragment from './documentfragment.js';
import type Item from './item.js';
import type ModelElement from './element.js';
import type Operation from './operation/operation.js';
import Batch, { type BatchType } from '@ckeditor/ckeditor5-engine/src/model/batch.js';
import Document from '@ckeditor/ckeditor5-engine/src/model/document.js';
import MarkerCollection from '@ckeditor/ckeditor5-engine/src/model/markercollection.js';
import ModelPosition, { type PositionOffset, type PositionStickiness } from '@ckeditor/ckeditor5-engine/src/model/position.js';
import ModelRange from '@ckeditor/ckeditor5-engine/src/model/range.js';
import ModelSelection, { type PlaceOrOffset, type Selectable } from '@ckeditor/ckeditor5-engine/src/model/selection.js';
import DocumentSelection from '@ckeditor/ckeditor5-engine/src/model/documentselection.js';
import Schema from '@ckeditor/ckeditor5-engine/src/model/schema.js';
import Writer from '@ckeditor/ckeditor5-engine/src/model/writer.js';
import Node from '@ckeditor/ckeditor5-engine/src/model/node.js';
import type ModelDocumentFragment from '@ckeditor/ckeditor5-engine/src/model/documentfragment.js';
import type Item from '@ckeditor/ckeditor5-engine/src/model/item.js';
import type ModelElement from '@ckeditor/ckeditor5-engine/src/model/element.js';
import type Operation from '@ckeditor/ckeditor5-engine/src/model/operation/operation.js';
import { type DecoratedMethodEvent } from '@ckeditor/ckeditor5-utils';
declare const Model_base: {
new (): import("@ckeditor/ckeditor5-utils").Observable;

View File

@ -5,10 +5,10 @@
/**
* @module engine/model/node
*/
import TypeCheckable from './typecheckable.js';
import type Document from './document.js';
import type DocumentFragment from './documentfragment.js';
import type Element from './element.js';
import TypeCheckable from '@ckeditor/ckeditor5-engine/src/model/typecheckable.js';
import type Document from '@ckeditor/ckeditor5-engine/src/model/document.js';
import type DocumentFragment from '@ckeditor/ckeditor5-engine/src/model/documentfragment.js';
import type Element from '@ckeditor/ckeditor5-engine/src/model/element.js';
import '@ckeditor/ckeditor5-utils/src/version.js';
/**
* Model node. Most basic structure of model tree.

View File

@ -5,7 +5,7 @@
/**
* @module engine/model/nodelist
*/
import Node from './node.js';
import Node from '@ckeditor/ckeditor5-engine/src/model/node.js';
/**
* Provides an interface to operate on a list of {@link module:engine/model/node~Node nodes}. `NodeList` is used internally
* in classes like {@link module:engine/model/element~Element Element}

View File

@ -5,10 +5,10 @@
/**
* @module engine/model/operation/attributeoperation
*/
import Operation from './operation.js';
import Range from '../range.js';
import type Document from '../document.js';
import type { Selectable } from '../selection.js';
import Operation from '@ckeditor/ckeditor5-engine/src/model/operation/operation.js';
import Range from '@ckeditor/ckeditor5-engine/src/model/range.js';
import type Document from '@ckeditor/ckeditor5-engine/src/model/document.js';
import type { Selectable } from '@ckeditor/ckeditor5-engine/src/model/selection.js';
/**
* Operation to change nodes' attribute.
*

View File

@ -5,12 +5,12 @@
/**
* @module engine/model/operation/insertoperation
*/
import Operation from './operation.js';
import Position from '../position.js';
import NodeList from '../nodelist.js';
import { type NodeSet } from './utils.js';
import type { Selectable } from '../selection.js';
import type Document from '../document.js';
import Operation from '@ckeditor/ckeditor5-engine/src/model/operation/operation.js';
import Position from '@ckeditor/ckeditor5-engine/src/model/position.js';
import NodeList from '@ckeditor/ckeditor5-engine/src/model/nodelist.js';
import { type NodeSet } from '@ckeditor/ckeditor5-engine/src/model/operation/utils.js';
import type { Selectable } from '@ckeditor/ckeditor5-engine/src/model/selection.js';
import type Document from '@ckeditor/ckeditor5-engine/src/model/document.js';
/**
* Operation to insert one or more nodes at given position in the model.
*/

View File

@ -5,11 +5,11 @@
/**
* @module engine/model/operation/markeroperation
*/
import Operation from './operation.js';
import Range from '../range.js';
import type Document from '../document.js';
import type MarkerCollection from '../markercollection.js';
import type { Selectable } from '../selection.js';
import Operation from '@ckeditor/ckeditor5-engine/src/model/operation/operation.js';
import Range from '@ckeditor/ckeditor5-engine/src/model/range.js';
import type Document from '@ckeditor/ckeditor5-engine/src/model/document.js';
import type MarkerCollection from '@ckeditor/ckeditor5-engine/src/model/markercollection.js';
import type { Selectable } from '@ckeditor/ckeditor5-engine/src/model/selection.js';
export default class MarkerOperation extends Operation {
/**
* Marker name.

View File

@ -5,11 +5,11 @@
/**
* @module engine/model/operation/mergeoperation
*/
import Operation from './operation.js';
import Position from '../position.js';
import Range from '../range.js';
import type Document from '../document.js';
import type { Selectable } from '../selection.js';
import Operation from '@ckeditor/ckeditor5-engine/src/model/operation/operation.js';
import Position from '@ckeditor/ckeditor5-engine/src/model/position.js';
import Range from '@ckeditor/ckeditor5-engine/src/model/range.js';
import type Document from '@ckeditor/ckeditor5-engine/src/model/document.js';
import type { Selectable } from '@ckeditor/ckeditor5-engine/src/model/selection.js';
/**
* Operation to merge two {@link module:engine/model/element~Element elements}.
*

View File

@ -5,10 +5,10 @@
/**
* @module engine/model/operation/moveoperation
*/
import Operation from './operation.js';
import Position from '../position.js';
import type { Selectable } from '../selection.js';
import type Document from '../document.js';
import Operation from '@ckeditor/ckeditor5-engine/src/model/operation/operation.js';
import Position from '@ckeditor/ckeditor5-engine/src/model/position.js';
import type { Selectable } from '@ckeditor/ckeditor5-engine/src/model/selection.js';
import type Document from '@ckeditor/ckeditor5-engine/src/model/document.js';
/**
* Operation to move a range of {@link module:engine/model/item~Item model items}
* to given {@link module:engine/model/position~Position target position}.

View File

@ -5,8 +5,8 @@
/**
* @module engine/model/operation/nooperation
*/
import Operation from './operation.js';
import type { Selectable } from '../selection.js';
import Operation from '@ckeditor/ckeditor5-engine/src/model/operation/operation.js';
import type { Selectable } from '@ckeditor/ckeditor5-engine/src/model/selection.js';
/**
* Operation which is doing nothing ("empty operation", "do-nothing operation", "noop"). This is an operation,
* which when executed does not change the tree model. It still has some parameters defined for transformation purposes.

View File

@ -5,9 +5,9 @@
/**
* @module engine/model/operation/operation
*/
import type Batch from '../batch.js';
import type Document from '../document.js';
import type { Selectable } from '../selection.js';
import type Batch from '@ckeditor/ckeditor5-engine/src/model/batch.js';
import type Document from '@ckeditor/ckeditor5-engine/src/model/document.js';
import type { Selectable } from '@ckeditor/ckeditor5-engine/src/model/selection.js';
/**
* Abstract base operation class.
*/

View File

@ -2,8 +2,8 @@
* @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/
import Operation from './operation.js';
import type Document from '../document.js';
import Operation from '@ckeditor/ckeditor5-engine/src/model/operation/operation.js';
import type Document from '@ckeditor/ckeditor5-engine/src/model/document.js';
/**
* A factory class for creating operations.
*/

View File

@ -5,10 +5,10 @@
/**
* @module engine/model/operation/renameoperation
*/
import Operation from './operation.js';
import Position from '../position.js';
import type Document from '../document.js';
import type { Selectable } from '../selection.js';
import Operation from '@ckeditor/ckeditor5-engine/src/model/operation/operation.js';
import Position from '@ckeditor/ckeditor5-engine/src/model/position.js';
import type Document from '@ckeditor/ckeditor5-engine/src/model/document.js';
import type { Selectable } from '@ckeditor/ckeditor5-engine/src/model/selection.js';
/**
* Operation to change element's name.
*

View File

@ -5,10 +5,10 @@
/**
* @module engine/model/operation/rootattributeoperation
*/
import Operation from './operation.js';
import type Document from '../document.js';
import type RootElement from '../rootelement.js';
import type { Selectable } from '../selection.js';
import Operation from '@ckeditor/ckeditor5-engine/src/model/operation/operation.js';
import type Document from '@ckeditor/ckeditor5-engine/src/model/document.js';
import type RootElement from '@ckeditor/ckeditor5-engine/src/model/rootelement.js';
import type { Selectable } from '@ckeditor/ckeditor5-engine/src/model/selection.js';
/**
* Operation to change root element's attribute. Using this class you can add, remove or change value of the attribute.
*

View File

@ -5,9 +5,9 @@
/**
* @module engine/model/operation/rootoperation
*/
import Operation from './operation.js';
import type Document from '../document.js';
import type { Selectable } from '../selection.js';
import Operation from '@ckeditor/ckeditor5-engine/src/model/operation/operation.js';
import type Document from '@ckeditor/ckeditor5-engine/src/model/document.js';
import type { Selectable } from '@ckeditor/ckeditor5-engine/src/model/selection.js';
/**
* Operation that creates (or attaches) or detaches a root element.
*/

View File

@ -5,11 +5,11 @@
/**
* @module engine/model/operation/splitoperation
*/
import Operation from './operation.js';
import Position from '../position.js';
import Range from '../range.js';
import type Document from '../document.js';
import type { Selectable } from '../selection.js';
import Operation from '@ckeditor/ckeditor5-engine/src/model/operation/operation.js';
import Position from '@ckeditor/ckeditor5-engine/src/model/position.js';
import Range from '@ckeditor/ckeditor5-engine/src/model/range.js';
import type Document from '@ckeditor/ckeditor5-engine/src/model/document.js';
import type { Selectable } from '@ckeditor/ckeditor5-engine/src/model/selection.js';
/**
* Operation to split {@link module:engine/model/element~Element an element} at given
* {@link module:engine/model/operation/splitoperation~SplitOperation#splitPosition split position} into two elements,

View File

@ -2,8 +2,8 @@
* @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/
import type Operation from './operation.js';
import type Document from '../document.js';
import type Operation from '@ckeditor/ckeditor5-engine/src/model/operation/operation.js';
import type Document from '@ckeditor/ckeditor5-engine/src/model/document.js';
/**
* Transforms operation `a` by operation `b`.
*

View File

@ -5,12 +5,12 @@
/**
* @module engine/model/operation/utils
*/
import Node from '../node.js';
import Range from '../range.js';
import type DocumentFragment from '../documentfragment.js';
import type Item from '../item.js';
import type NodeList from '../nodelist.js';
import type Position from '../position.js';
import Node from '@ckeditor/ckeditor5-engine/src/model/node.js';
import Range from '@ckeditor/ckeditor5-engine/src/model/range.js';
import type DocumentFragment from '@ckeditor/ckeditor5-engine/src/model/documentfragment.js';
import type Item from '@ckeditor/ckeditor5-engine/src/model/item.js';
import type NodeList from '@ckeditor/ckeditor5-engine/src/model/nodelist.js';
import type Position from '@ckeditor/ckeditor5-engine/src/model/position.js';
/**
* Inserts given nodes at given position.
*

View File

@ -5,19 +5,19 @@
/**
* @module engine/model/position
*/
import TypeCheckable from './typecheckable.js';
import { type TreeWalkerOptions, type TreeWalkerValue } from './treewalker.js';
import type Document from './document.js';
import type DocumentFragment from './documentfragment.js';
import type Element from './element.js';
import type InsertOperation from './operation/insertoperation.js';
import type Item from './item.js';
import type MergeOperation from './operation/mergeoperation.js';
import type MoveOperation from './operation/moveoperation.js';
import type Node from './node.js';
import type Operation from './operation/operation.js';
import type SplitOperation from './operation/splitoperation.js';
import type Text from './text.js';
import TypeCheckable from '@ckeditor/ckeditor5-engine/src/model/typecheckable.js';
import { type TreeWalkerOptions, type TreeWalkerValue } from '@ckeditor/ckeditor5-engine/src/model/treewalker.js';
import type Document from '@ckeditor/ckeditor5-engine/src/model/document.js';
import type DocumentFragment from '@ckeditor/ckeditor5-engine/src/model/documentfragment.js';
import type Element from '@ckeditor/ckeditor5-engine/src/model/element.js';
import type InsertOperation from '@ckeditor/ckeditor5-engine/src/model/operation/insertoperation.js';
import type Item from '@ckeditor/ckeditor5-engine/src/model/item.js';
import type MergeOperation from '@ckeditor/ckeditor5-engine/src/model/operation/mergeoperation.js';
import type MoveOperation from '@ckeditor/ckeditor5-engine/src/model/operation/moveoperation.js';
import type Node from '@ckeditor/ckeditor5-engine/src/model/node.js';
import type Operation from '@ckeditor/ckeditor5-engine/src/model/operation/operation.js';
import type SplitOperation from '@ckeditor/ckeditor5-engine/src/model/operation/splitoperation.js';
import type Text from '@ckeditor/ckeditor5-engine/src/model/text.js';
import '@ckeditor/ckeditor5-utils/src/version.js';
/**
* Represents a position in the model tree.

View File

@ -5,18 +5,18 @@
/**
* @module engine/model/range
*/
import TypeCheckable from './typecheckable.js';
import Position from './position.js';
import TreeWalker, { type TreeWalkerOptions, type TreeWalkerValue } from './treewalker.js';
import type Document from './document.js';
import type DocumentFragment from './documentfragment.js';
import type Element from './element.js';
import type InsertOperation from './operation/insertoperation.js';
import type Item from './item.js';
import type MergeOperation from './operation/mergeoperation.js';
import type MoveOperation from './operation/moveoperation.js';
import type Operation from './operation/operation.js';
import type SplitOperation from './operation/splitoperation.js';
import TypeCheckable from '@ckeditor/ckeditor5-engine/src/model/typecheckable.js';
import Position from '@ckeditor/ckeditor5-engine/src/model/position.js';
import TreeWalker, { type TreeWalkerOptions, type TreeWalkerValue } from '@ckeditor/ckeditor5-engine/src/model/treewalker.js';
import type Document from '@ckeditor/ckeditor5-engine/src/model/document.js';
import type DocumentFragment from '@ckeditor/ckeditor5-engine/src/model/documentfragment.js';
import type Element from '@ckeditor/ckeditor5-engine/src/model/element.js';
import type InsertOperation from '@ckeditor/ckeditor5-engine/src/model/operation/insertoperation.js';
import type Item from '@ckeditor/ckeditor5-engine/src/model/item.js';
import type MergeOperation from '@ckeditor/ckeditor5-engine/src/model/operation/mergeoperation.js';
import type MoveOperation from '@ckeditor/ckeditor5-engine/src/model/operation/moveoperation.js';
import type Operation from '@ckeditor/ckeditor5-engine/src/model/operation/operation.js';
import type SplitOperation from '@ckeditor/ckeditor5-engine/src/model/operation/splitoperation.js';
/**
* Represents a range in the model tree.
*

View File

@ -5,8 +5,8 @@
/**
* @module engine/model/rootelement
*/
import Element from './element.js';
import type Document from './document.js';
import Element from '@ckeditor/ckeditor5-engine/src/model/element.js';
import type Document from '@ckeditor/ckeditor5-engine/src/model/document.js';
/**
* Type of {@link module:engine/model/element~Element} that is a root of a model tree.
*/

View File

@ -5,15 +5,15 @@
/**
* @module engine/model/schema
*/
import Element from './element.js';
import Position from './position.js';
import Range from './range.js';
import type DocumentFragment from './documentfragment.js';
import type DocumentSelection from './documentselection.js';
import type Item from './item.js';
import type Node from './node.js';
import type Selection from './selection.js';
import type Writer from './writer.js';
import Element from '@ckeditor/ckeditor5-engine/src/model/element.js';
import Position from '@ckeditor/ckeditor5-engine/src/model/position.js';
import Range from '@ckeditor/ckeditor5-engine/src/model/range.js';
import type DocumentFragment from '@ckeditor/ckeditor5-engine/src/model/documentfragment.js';
import type DocumentSelection from '@ckeditor/ckeditor5-engine/src/model/documentselection.js';
import type Item from '@ckeditor/ckeditor5-engine/src/model/item.js';
import type Node from '@ckeditor/ckeditor5-engine/src/model/node.js';
import type Selection from '@ckeditor/ckeditor5-engine/src/model/selection.js';
import type Writer from '@ckeditor/ckeditor5-engine/src/model/writer.js';
declare const Schema_base: {
new (): import("@ckeditor/ckeditor5-utils").Observable;
prototype: import("@ckeditor/ckeditor5-utils").Observable;

View File

@ -5,13 +5,13 @@
/**
* @module engine/model/selection
*/
import TypeCheckable from './typecheckable.js';
import Node from './node.js';
import Position, { type PositionOffset } from './position.js';
import Range from './range.js';
import type DocumentSelection from './documentselection.js';
import type Element from './element.js';
import type Item from './item.js';
import TypeCheckable from '@ckeditor/ckeditor5-engine/src/model/typecheckable.js';
import Node from '@ckeditor/ckeditor5-engine/src/model/node.js';
import Position, { type PositionOffset } from '@ckeditor/ckeditor5-engine/src/model/position.js';
import Range from '@ckeditor/ckeditor5-engine/src/model/range.js';
import type DocumentSelection from '@ckeditor/ckeditor5-engine/src/model/documentselection.js';
import type Element from '@ckeditor/ckeditor5-engine/src/model/element.js';
import type Item from '@ckeditor/ckeditor5-engine/src/model/item.js';
declare const Selection_base: import("@ckeditor/ckeditor5-utils").Mixed<typeof TypeCheckable, import("@ckeditor/ckeditor5-utils").Emitter>;
/**
* Selection is a set of {@link module:engine/model/range~Range ranges}. It has a direction specified by its

View File

@ -5,7 +5,7 @@
/**
* @module engine/model/text
*/
import Node, { type NodeAttributes } from './node.js';
import Node, { type NodeAttributes } from '@ckeditor/ckeditor5-engine/src/model/node.js';
/**
* Model text node. Type of {@link module:engine/model/node~Node node} that contains {@link module:engine/model/text~Text#data text data}.
*

View File

@ -5,11 +5,11 @@
/**
* @module engine/model/textproxy
*/
import TypeCheckable from './typecheckable.js';
import type DocumentFragment from './documentfragment.js';
import type Element from './element.js';
import type Node from './node.js';
import type Text from './text.js';
import TypeCheckable from '@ckeditor/ckeditor5-engine/src/model/typecheckable.js';
import type DocumentFragment from '@ckeditor/ckeditor5-engine/src/model/documentfragment.js';
import type Element from '@ckeditor/ckeditor5-engine/src/model/element.js';
import type Node from '@ckeditor/ckeditor5-engine/src/model/node.js';
import type Text from '@ckeditor/ckeditor5-engine/src/model/text.js';
/**
* `TextProxy` represents a part of {@link module:engine/model/text~Text text node}.
*

View File

@ -2,9 +2,9 @@
* @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/
import { default as Position } from './position.js';
import type Item from './item.js';
import type Range from './range.js';
import { default as Position } from '@ckeditor/ckeditor5-engine/src/model/position.js';
import type Item from '@ckeditor/ckeditor5-engine/src/model/item.js';
import type Range from '@ckeditor/ckeditor5-engine/src/model/range.js';
/**
* Position iterator class. It allows to iterate forward and backward over the document.
*/

View File

@ -5,19 +5,19 @@
/**
* @module engine/model/typecheckable
*/
import type { Marker } from './markercollection.js';
import type DocumentFragment from './documentfragment.js';
import type DocumentSelection from './documentselection.js';
import type Element from './element.js';
import type LivePosition from './liveposition.js';
import type LiveRange from './liverange.js';
import type Node from './node.js';
import type Position from './position.js';
import type Range from './range.js';
import type RootElement from './rootelement.js';
import type Selection from './selection.js';
import type Text from './text.js';
import type TextProxy from './textproxy.js';
import type { Marker } from '@ckeditor/ckeditor5-engine/src/model/markercollection.js';
import type DocumentFragment from '@ckeditor/ckeditor5-engine/src/model/documentfragment.js';
import type DocumentSelection from '@ckeditor/ckeditor5-engine/src/model/documentselection.js';
import type Element from '@ckeditor/ckeditor5-engine/src/model/element.js';
import type LivePosition from '@ckeditor/ckeditor5-engine/src/model/liveposition.js';
import type LiveRange from '@ckeditor/ckeditor5-engine/src/model/liverange.js';
import type Node from '@ckeditor/ckeditor5-engine/src/model/node.js';
import type Position from '@ckeditor/ckeditor5-engine/src/model/position.js';
import type Range from '@ckeditor/ckeditor5-engine/src/model/range.js';
import type RootElement from '@ckeditor/ckeditor5-engine/src/model/rootelement.js';
import type Selection from '@ckeditor/ckeditor5-engine/src/model/selection.js';
import type Text from '@ckeditor/ckeditor5-engine/src/model/text.js';
import type TextProxy from '@ckeditor/ckeditor5-engine/src/model/textproxy.js';
export default abstract class TypeCheckable {
/**
* Checks whether the object is of type {@link module:engine/model/node~Node} or its subclass.

View File

@ -2,18 +2,18 @@
* @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/
import DocumentFragment from './documentfragment.js';
import Element from './element.js';
import Position, { type PositionOffset, type PositionStickiness } from './position.js';
import Range from './range.js';
import RootElement from './rootelement.js';
import Text from './text.js';
import type { Marker } from './markercollection.js';
import type { default as Selection, PlaceOrOffset, Selectable } from './selection.js';
import type Batch from './batch.js';
import type Item from './item.js';
import type Model from './model.js';
import type { default as Node, NodeAttributes } from './node.js';
import DocumentFragment from '@ckeditor/ckeditor5-engine/src/model/documentfragment.js';
import Element from '@ckeditor/ckeditor5-engine/src/model/element.js';
import Position, { type PositionOffset, type PositionStickiness } from '@ckeditor/ckeditor5-engine/src/model/position.js';
import Range from '@ckeditor/ckeditor5-engine/src/model/range.js';
import RootElement from '@ckeditor/ckeditor5-engine/src/model/rootelement.js';
import Text from '@ckeditor/ckeditor5-engine/src/model/text.js';
import type { Marker } from '@ckeditor/ckeditor5-engine/src/model/markercollection.js';
import type { default as Selection, PlaceOrOffset, Selectable } from '@ckeditor/ckeditor5-engine/src/model/selection.js';
import type Batch from '@ckeditor/ckeditor5-engine/src/model/batch.js';
import type Item from '@ckeditor/ckeditor5-engine/src/model/item.js';
import type Model from '@ckeditor/ckeditor5-engine/src/model/model.js';
import type { default as Node, NodeAttributes } from '@ckeditor/ckeditor5-engine/src/model/node.js';
/**
* The model can only be modified by using the writer. It should be used whenever you want to create a node, modify
* child nodes, attributes or text, set the selection's position and its attributes.

View File

@ -5,9 +5,9 @@
/**
* @module engine/view/attributeelement
*/
import Element, { type ElementAttributes } from './element.js';
import type Document from './document.js';
import type Node from './node.js';
import Element, { type ElementAttributes } from '@ckeditor/ckeditor5-engine/src/view/element.js';
import type Document from '@ckeditor/ckeditor5-engine/src/view/document.js';
import type Node from '@ckeditor/ckeditor5-engine/src/view/node.js';
/**
* Attribute elements are used to represent formatting elements in the view (think `<b>`, `<span style="font-size: 2em">`, etc.).
* Most often they are created when downcasting model text attributes.

View File

@ -5,9 +5,9 @@
/**
* @module engine/view/containerelement
*/
import Element, { type ElementAttributes } from './element.js';
import type Document from './document.js';
import type Node from './node.js';
import Element, { type ElementAttributes } from '@ckeditor/ckeditor5-engine/src/view/element.js';
import type Document from '@ckeditor/ckeditor5-engine/src/view/document.js';
import type Node from '@ckeditor/ckeditor5-engine/src/view/node.js';
/**
* Containers are elements which define document structure. They define boundaries for
* {@link module:engine/view/attributeelement~AttributeElement attributes}. They are mostly used for block elements like `<p>` or `<div>`.

View File

@ -5,11 +5,11 @@
/**
* @module engine/view/document
*/
import DocumentSelection from './documentselection.js';
import DocumentSelection from '@ckeditor/ckeditor5-engine/src/view/documentselection.js';
import { Collection } from '@ckeditor/ckeditor5-utils';
import type { StylesProcessor } from './stylesmap.js';
import type RootEditableElement from './rooteditableelement.js';
import type DowncastWriter from './downcastwriter.js';
import type { StylesProcessor } from '@ckeditor/ckeditor5-engine/src/view/stylesmap.js';
import type RootEditableElement from '@ckeditor/ckeditor5-engine/src/view/rooteditableelement.js';
import type DowncastWriter from '@ckeditor/ckeditor5-engine/src/view/downcastwriter.js';
declare const Document_base: import("@ckeditor/ckeditor5-utils").Mixed<{
new (): import("@ckeditor/ckeditor5-utils").Observable;
prototype: import("@ckeditor/ckeditor5-utils").Observable;

Some files were not shown because too many files have changed in this diff Show More