merge main

This commit is contained in:
Rama Priyanto 2025-01-10 11:08:21 +07:00
commit c219419c39
191 changed files with 18699 additions and 130 deletions

View File

@ -244,21 +244,23 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
return (
<>
<p>{title}</p>
<p className=" text-xs text-start mt-2">Create By: {createdByName}</p>
<p className="ml-1">{title}</p>
<p className="ml-1 text-xs text-start mt-2">
Create By: {createdByName}
</p>
</>
);
};
const handleClassName = (arg: EventContentArg) => {
if (arg.event.extendedProps.calendar === "mabes") {
return "primary";
return "bg-yellow-500 border-none";
} else if (arg.event.extendedProps.calendar === "polda") {
return "success";
return "bg-blue-400 border-none";
} else if (arg.event.extendedProps.calendar === "polres") {
return "destructive";
return "bg-slate-400 border-none";
} else if (arg.event.extendedProps.calendar === "international") {
return "info";
return "bg-green-400 border-none";
} else {
return "primary";
}

View File

@ -153,7 +153,7 @@ const columns: ColumnDef<any>[] = [
View
</DropdownMenuItem>
</Link>
<Link href={`/contributor/content/Audio/update/${row.original.id}`}>
<Link href={`/contributor/content/audio/update/${row.original.id}`}>
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
<SquarePen className="w-4 h-4 me-1.5" />
Edit

View File

@ -26,6 +26,7 @@ import {
} from "@/components/ui/table";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import {
ChevronDown,
ChevronLeft,
ChevronRight,
Eye,
@ -39,6 +40,7 @@ import {
import { cn, getCookiesDecrypt } from "@/lib/utils";
import {
DropdownMenu,
DropdownMenuCheckboxItem,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
@ -176,7 +178,7 @@ const TableImage = () => {
/>
</InputGroup>
</div>
<div className="flex-none">
<div className="flex flex-row items-center gap-3">
<Input
placeholder="Filter Status..."
value={
@ -187,6 +189,34 @@ const TableImage = () => {
}
className="max-w-sm "
/>
<div className="flex items-center py-4">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline" className="ml-auto" size="md">
Columns <ChevronDown />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
{table
.getAllColumns()
.filter((column) => column.getCanHide())
.map((column) => {
return (
<DropdownMenuCheckboxItem
key={column.id}
className="capitalize"
checked={column.getIsVisible()}
onCheckedChange={(value) =>
column.toggleVisibility(!!value)
}
>
{column.id}
</DropdownMenuCheckboxItem>
);
})}
</DropdownMenuContent>
</DropdownMenu>
</div>
</div>
</div>
<Table className="overflow-hidden mt-3">

View File

@ -35,7 +35,7 @@ const imageSchema = z.object({
// tags: z.string().min(1, { message: "Judul diperlukan" }),
});
const page = (props: { states?: any }) => {
const page = (props: { states?: string }) => {
const { states } = props;
const MySwal = withReactContent(Swal);
const router = useRouter();

View File

@ -55,6 +55,7 @@ import { getCookiesDecrypt } from "@/lib/utils";
import { Icon } from "@iconify/react/dist/iconify.js";
import { error } from "@/lib/swal";
import dynamic from "next/dynamic";
import ReactAudioPlayer from "react-audio-player";
const imageSchema = z.object({
title: z.string().min(1, { message: "Judul diperlukan" }),
@ -137,6 +138,7 @@ export default function FormAudioDetail() {
const [files, setFiles] = useState<FileType[]>([]);
const [rejectedFiles, setRejectedFiles] = useState<number[]>([]);
const [isMabesApprover, setIsMabesApprover] = useState(false);
const [audioPlaying, setAudioPlaying] = useState<any>(null);
let fileTypeId = "4";
@ -336,6 +338,14 @@ export default function FormAudioDetail() {
return false;
};
const handleAudioPlayPause = (audioSrc: string) => {
if (audioPlaying === audioSrc) {
setAudioPlaying(null); // Pause if the same audio is clicked
} else {
setAudioPlaying(audioSrc); // Play the new audio
}
};
const submitApprovalSuccesss = () => {
MySwal.fire({
title: "Sukses",
@ -427,30 +437,23 @@ export default function FormAudioDetail() {
className="w-full"
>
{detailThumb?.map((data: any) => {
const isAudio =
data.endsWith(".webm") ||
data.endsWith(".mp3") ||
data.endsWith(".ogg");
return (
<SwiperSlide key={data.id}>
{isAudio ? (
<audio
className="object-fill h-full w-full rounded-md"
src={data.secondaryUrl}
controls
/>
) : (
<img
className="object-fill h-full w-full rounded-md"
src={data.secondaryUrl}
alt={` ${data.id}`}
/>
)}
<div className="relative">
<ReactAudioPlayer src={data} autoPlay controls />
{/* <button
className="absolute bottom-2 left-2 text-white bg-black p-1 rounded"
onClick={() => handleAudioPlayPause(data)}
>
{audioPlaying === data ? "Pause" : "Play"}
</button> */}
</div>
</SwiperSlide>
);
})}
</Swiper>
<div className="mt-2">
{/* <div className="mt-2">
<Swiper
onSwiper={setThumbsSwiper}
slidesPerView={6}
@ -461,30 +464,26 @@ export default function FormAudioDetail() {
modules={[Pagination, Thumbs]}
>
{detailThumb?.map((data: any) => {
const isAudio =
data.endsWith(".webm") ||
data.endsWith(".mp3") ||
data.endsWith(".ogg");
return (
<SwiperSlide key={data.id}>
{isAudio ? (
<audio
className="object-cover h-[60px] w-[80px]"
src={data.secondaryUrl}
controls
/>
) : (
<img
className="object-cover h-[60px] w-[80px]"
src={data.secondaryUrl}
alt={` ${data.id}`}
/>
)}
<div className="relative">
<ReactAudioPlayer src={data} autoPlay controls />
<button
className="absolute bottom-2 left-2 text-white bg-black p-1 rounded"
onClick={() =>
handleAudioPlayPause(data.secondaryUrl)
}
>
{audioPlaying === data.secondaryUrl
? "Pause"
: "Play"}
</button>
</div>
</SwiperSlide>
);
})}
</Swiper>
</div>
</div> */}
</div>
</div>
</div>

View File

@ -53,15 +53,6 @@ import { error, loading } from "@/config/swal";
import { Item } from "@radix-ui/react-dropdown-menu";
import dynamic from "next/dynamic";
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" }),
});
interface FileWithPreview extends File {
preview: string;
}
@ -87,7 +78,7 @@ export default function FormAudio() {
const MySwal = withReactContent(Swal);
const router = useRouter();
const editor = useRef(null);
type ImageSchema = z.infer<typeof imageSchema>;
type AudioSchema = z.infer<typeof audioSchema>;
const [selectedFiles, setSelectedFiles] = useState<File[]>([]);
const taskId = Cookies.get("taskId");
@ -155,13 +146,28 @@ export default function FormAudio() {
},
});
const audioSchema = z.object({
title: z.string().min(1, { message: "Judul diperlukan" }),
description: z
.string()
.min(2, { message: "Narasi Penugasan harus lebih dari 2 karakter." })
.or(
z.literal(articleBody || "").refine((val) => val.length > 0, {
message: "Deskripsi diperlukan.",
})
),
creatorName: z.string().min(1, { message: "Creator diperlukan" }),
// tags: z.string().min(1, { message: "Judul diperlukan" }),
});
const {
control,
handleSubmit,
getValues,
setValue,
formState: { errors },
} = useForm<ImageSchema>({
resolver: zodResolver(imageSchema),
} = useForm<AudioSchema>({
resolver: zodResolver(audioSchema),
});
const doGenerateMainKeyword = async () => {
@ -424,15 +430,21 @@ export default function FormAudio() {
}
};
const save = async (data: ImageSchema) => {
const save = async (data: AudioSchema) => {
loading();
const finalTags = tags.join(", ");
const finalTitle = isSwitchOn ? title : data.title;
const finalDescription = articleBody || data.description;
if (!finalDescription.trim()) {
MySwal.fire("Error", "Deskripsi tidak boleh kosong.", "error");
return;
}
const requestData = {
...data,
title: finalTitle,
description: data.description,
htmlDescription: data.description,
description: finalDescription,
htmlDescription: finalDescription,
fileTypeId,
categoryId: selectedCategory,
subCategoryId: selectedCategory,
@ -489,7 +501,7 @@ export default function FormAudio() {
// MySwal.fire("Sukses", "Data berhasil disimpan.", "success");
};
const onSubmit = (data: ImageSchema) => {
const onSubmit = (data: AudioSchema) => {
MySwal.fire({
title: "Simpan Data",
text: "Apakah Anda yakin ingin menyimpan data ini?",
@ -651,6 +663,13 @@ export default function FormAudio() {
setFiles([]);
};
useEffect(() => {
// Jika input title kosong, isi dengan hasil generate title
if (!getValues("title") && title) {
setValue("title", title);
}
}, [title, getValues, setValue]);
return (
<form onSubmit={handleSubmit(onSubmit)}>
<div className="flex lg:flex-row gap-10">
@ -871,6 +890,7 @@ export default function FormAudio() {
color="primary"
onClick={handleGenerateArtikel}
size="sm"
type="button"
>
Generate Article
</Button>

View File

@ -31,6 +31,9 @@ import {
import { detailMedia } from "@/service/curated-content/curated-content";
import { Badge } from "@/components/ui/badge";
import { MailIcon } from "lucide-react";
import { Swiper, SwiperSlide } from "swiper/react";
import ReactAudioPlayer from "react-audio-player";
import { FreeMode, Navigation, Thumbs } from "swiper/modules";
const imageSchema = z.object({
title: z.string().min(1, { message: "Judul diperlukan" }),
@ -81,7 +84,8 @@ export default function FormAudioUpdate() {
const [detail, setDetail] = useState<Detail>();
const [refresh, setRefresh] = useState(false);
const [selectedPublishers, setSelectedPublishers] = useState<number[]>([]);
const [detailThumb, setDetailThumb] = useState<any>([]);
const [thumbsSwiper, setThumbsSwiper] = useState<any>(null);
const [selectedTarget, setSelectedTarget] = useState("");
const [unitSelection, setUnitSelection] = useState({
allUnit: false,
@ -190,7 +194,13 @@ export default function FormAudioUpdate() {
setSelectedTarget(matchingCategory.name);
}
setSelectedTarget(details.categoryId); // Untuk dropdown
setSelectedTarget(details.categoryId);
const filesData = details.files || [];
const fileUrls = filesData.map((file: { secondaryUrl: string }) =>
file.secondaryUrl ? file.secondaryUrl : "default-image.jpg"
);
setDetailThumb(fileUrls);
}
}
initState();
@ -251,7 +261,7 @@ export default function FormAudioUpdate() {
<div className="flex lg:flex-row gap-10">
<Card className="w-full lg:w-8/12">
<div className="px-6 py-6">
<p className="text-lg font-semibold mb-3">Form Konten Foto</p>
<p className="text-lg font-semibold mb-3">Form Konten Audio</p>
<div className="gap-5 mb-5">
{/* Input Title */}
<div className="space-y-2 py-3">
@ -319,6 +329,63 @@ export default function FormAudioUpdate() {
</p>
)}
</div>
<Label className="text-xl text-black">File Mediaaa</Label>
<div className="w-full">
<Swiper
thumbs={{ swiper: thumbsSwiper }}
modules={[FreeMode, Navigation, Thumbs]}
navigation={false}
className="w-full"
>
{detailThumb?.map((data: any) => {
return (
<SwiperSlide key={data.id}>
<div className="relative">
<ReactAudioPlayer src={data} autoPlay controls />
{/* <button
className="absolute bottom-2 left-2 text-white bg-black p-1 rounded"
onClick={() => handleAudioPlayPause(data)}
>
{audioPlaying === data ? "Pause" : "Play"}
</button> */}
</div>
</SwiperSlide>
);
})}
</Swiper>
{/* <div className="mt-2">
<Swiper
onSwiper={setThumbsSwiper}
slidesPerView={6}
spaceBetween={8}
pagination={{
clickable: true,
}}
modules={[Pagination, Thumbs]}
>
{detailThumb?.map((data: any) => {
return (
<SwiperSlide key={data.id}>
<div className="relative">
<ReactAudioPlayer src={data} autoPlay controls />
<button
className="absolute bottom-2 left-2 text-white bg-black p-1 rounded"
onClick={() =>
handleAudioPlayPause(data.secondaryUrl)
}
>
{audioPlaying === data.secondaryUrl
? "Pause"
: "Play"}
</button>
</div>
</SwiperSlide>
);
})}
</Swiper>
</div> */}
</div>
</div>
</div>
</Card>

View File

@ -55,15 +55,6 @@ import { data } from "jquery";
import { options } from "@fullcalendar/core/preact.js";
import dynamic from "next/dynamic";
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" }),
});
interface FileWithPreview extends File {
preview: string;
}
@ -161,9 +152,24 @@ export default function FormImage() {
},
});
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." })
.or(
z.literal(articleBody || "").refine((val) => val.length > 0, {
message: "Deskripsi diperlukan.",
})
),
creatorName: z.string().min(1, { message: "Creator diperlukan" }),
// tags: z.string().min(1, { message: "Judul diperlukan" }),
});
const {
control,
handleSubmit,
getValues,
setValue,
formState: { errors },
} = useForm<ImageSchema>({
@ -434,11 +440,16 @@ export default function FormImage() {
loading();
const finalTags = tags.join(", ");
const finalTitle = isSwitchOn ? title : data.title;
const finalDescription = articleBody || data.description;
if (!finalDescription.trim()) {
MySwal.fire("Error", "Deskripsi tidak boleh kosong.", "error");
return;
}
const requestData = {
...data,
title: finalTitle,
description: data.description,
htmlDescription: data.description,
description: finalDescription,
htmlDescription: finalDescription,
fileTypeId,
categoryId: selectedCategory,
subCategoryId: selectedCategory,
@ -662,6 +673,13 @@ export default function FormImage() {
setFiles([]);
};
useEffect(() => {
// Jika input title kosong, isi dengan hasil generate title
if (!getValues("title") && title) {
setValue("title", title);
}
}, [title, getValues, setValue]);
return (
<form onSubmit={handleSubmit(onSubmit)}>
<div className="flex lg:flex-row gap-10">

View File

@ -243,9 +243,11 @@ export default function FormTeksDetail() {
setSelectedTarget(details.categoryId); // Untuk dropdown
const filesData = details.files || [];
const fileUrls = filesData.map((file: { url: string }) =>
file.url ? file.url : "default-image.jpg"
);
const fileUrls = filesData.map((file: any) => ({
url: file.secondaryUrl || "default-image.jpg",
format: file.format,
fileName: file.fileName,
}));
setDetailThumb(fileUrls);
}
}
@ -427,30 +429,42 @@ export default function FormTeksDetail() {
navigation={false}
className="w-full"
>
{detailThumb?.map((data: any) => (
<SwiperSlide key={data.id}>
{detailThumb?.map((data: any, index: number) => (
<SwiperSlide key={index}>
{[".jpg", ".jpeg", ".png", ".webp"].includes(
data.format
) ? (
// Menampilkan gambar
<img
className="object-fill h-full w-full rounded-md"
src={data.url}
alt={data.fileName}
alt={data.fileName || "File"}
/>
) : [".pdf", ".docx", ".txt"].includes(data.format) ? (
) : data.format === ".pdf" ? (
// Menampilkan PDF menggunakan iframe
<iframe
className="w-full h-96 rounded-md"
src={data.url}
title={data.fileName}
title={data.fileName || "PDF File"}
/>
) : [".docx", ".ppt", ".pptx"].includes(data.format) ? (
// Menampilkan file dokumen menggunakan Office Viewer
<iframe
className="w-full h-96 rounded-md"
src={`https://view.officeapps.live.com/op/embed.aspx?src=${encodeURIComponent(
data.url
)}`}
title={data.fileName || "Document"}
/>
) : (
// Menampilkan link jika format tidak dikenali
<a
href={data}
href={data.url}
target="_blank"
rel="noopener noreferrer"
className="block text-blue-500 underline"
>
View {data.fileName}
View {data.fileName || "File"}
</a>
)}
</SwiperSlide>
@ -464,8 +478,8 @@ export default function FormTeksDetail() {
pagination={{ clickable: true }}
modules={[Pagination, Thumbs]}
>
{detailThumb?.map((data: any) => (
<SwiperSlide key={data.id}>
{detailThumb?.map((data: any, index: number) => (
<SwiperSlide key={index}>
{[".jpg", ".jpeg", ".png", ".webp"].includes(
data.format
) ? (

View File

@ -53,15 +53,6 @@ import { error, loading } from "@/config/swal";
import { Item } from "@radix-ui/react-dropdown-menu";
import dynamic from "next/dynamic";
const teksSchema = 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" }),
});
interface FileWithPreview extends File {
preview: string;
}
@ -155,9 +146,24 @@ export default function FormTeks() {
},
});
const teksSchema = z.object({
title: z.string().min(1, { message: "Judul diperlukan" }),
description: z
.string()
.min(2, { message: "Narasi Penugasan harus lebih dari 2 karakter." })
.or(
z.literal(articleBody || "").refine((val) => val.length > 0, {
message: "Deskripsi diperlukan.",
})
),
creatorName: z.string().min(1, { message: "Creator diperlukan" }),
// tags: z.string().min(1, { message: "Judul diperlukan" }),
});
const {
control,
handleSubmit,
getValues,
setValue,
formState: { errors },
} = useForm<TeksSchema>({
@ -428,11 +434,16 @@ export default function FormTeks() {
loading();
const finalTags = tags.join(", ");
const finalTitle = isSwitchOn ? title : data.title;
const finalDescription = articleBody || data.description;
if (!finalDescription.trim()) {
MySwal.fire("Error", "Deskripsi tidak boleh kosong.", "error");
return;
}
const requestData = {
...data,
title: finalTitle,
description: data.description,
htmlDescription: data.description,
description: finalDescription,
htmlDescription: finalDescription,
fileTypeId,
categoryId: selectedCategory,
subCategoryId: selectedCategory,
@ -651,6 +662,13 @@ export default function FormTeks() {
setFiles([]);
};
useEffect(() => {
// Jika input title kosong, isi dengan hasil generate title
if (!getValues("title") && title) {
setValue("title", title);
}
}, [title, getValues, setValue]);
return (
<form onSubmit={handleSubmit(onSubmit)}>
<div className="flex lg:flex-row gap-10">

View File

@ -60,15 +60,6 @@ const CustomEditor = dynamic(
{ ssr: false }
);
const teksSchema = 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" }),
});
interface FileWithPreview extends File {
preview: string;
}
@ -87,7 +78,7 @@ export default function FormVideo() {
const MySwal = withReactContent(Swal);
const router = useRouter();
const editor = useRef(null);
type TeksSchema = z.infer<typeof teksSchema>;
type VideoSchema = z.infer<typeof videoSchema>;
const [selectedFiles, setSelectedFiles] = useState<File[]>([]);
const taskId = Cookies.get("taskId");
@ -155,13 +146,28 @@ export default function FormVideo() {
},
});
const videoSchema = z.object({
title: z.string().min(1, { message: "Judul diperlukan" }),
description: z
.string()
.min(2, { message: "Narasi Penugasan harus lebih dari 2 karakter." })
.or(
z.literal(articleBody || "").refine((val) => val.length > 0, {
message: "Deskripsi diperlukan.",
})
),
creatorName: z.string().min(1, { message: "Creator diperlukan" }),
// tags: z.string().min(1, { message: "Judul diperlukan" }),
});
const {
control,
handleSubmit,
getValues,
setValue,
formState: { errors },
} = useForm<TeksSchema>({
resolver: zodResolver(teksSchema),
} = useForm<VideoSchema>({
resolver: zodResolver(videoSchema),
});
const doGenerateMainKeyword = async () => {
@ -424,15 +430,20 @@ export default function FormVideo() {
}
};
const save = async (data: TeksSchema) => {
const save = async (data: VideoSchema) => {
loading();
const finalTags = tags.join(", ");
const finalTitle = isSwitchOn ? title : data.title;
const finalDescription = articleBody || data.description;
if (!finalDescription.trim()) {
MySwal.fire("Error", "Deskripsi tidak boleh kosong.", "error");
return;
}
const requestData = {
...data,
title: finalTitle,
description: data.description,
htmlDescription: data.description,
description: finalDescription,
htmlDescription: finalDescription,
fileTypeId,
categoryId: selectedCategory,
subCategoryId: selectedCategory,
@ -489,7 +500,7 @@ export default function FormVideo() {
// MySwal.fire("Sukses", "Data berhasil disimpan.", "success");
};
const onSubmit = (data: TeksSchema) => {
const onSubmit = (data: VideoSchema) => {
MySwal.fire({
title: "Simpan Data",
text: "Apakah Anda yakin ingin menyimpan data ini?",
@ -651,6 +662,13 @@ export default function FormVideo() {
setFiles([]);
};
useEffect(() => {
// Jika input title kosong, isi dengan hasil generate title
if (!getValues("title") && title) {
setValue("title", title);
}
}, [title, getValues, setValue]);
return (
<form onSubmit={handleSubmit(onSubmit)}>
<div className="flex lg:flex-row gap-10">

View File

@ -21,7 +21,9 @@ import { Checkbox } from "@/components/ui/checkbox";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import JoditEditor from "jodit-react";
import {
createAssignmentResponse,
createTask,
getAssignmentResponseList,
getTask,
getUserLevelForAssignments,
} from "@/service/task";
@ -35,6 +37,9 @@ import {
import { ChevronDown, ChevronUp } from "lucide-react";
import dynamic from "next/dynamic";
import { Link } from "@/components/navigation";
import { Textarea } from "@/components/ui/textarea";
import { loading } from "@/lib/swal";
import { getCookiesDecrypt } from "@/lib/utils";
const taskSchema = z.object({
uniqueCode: z.string().min(1, { message: "Judul diperlukan" }),
@ -63,8 +68,16 @@ export type taskDetail = {
broadcastType: string;
narration: string;
is_active: string;
isDone: any;
};
interface ListData {
id: number;
name: string;
content: string;
timestamp: string;
}
const ViewEditor = dynamic(
() => {
return import("@/components/editor/view-editor");
@ -80,6 +93,9 @@ export default function FormTaskDetail() {
const { id } = useParams() as { id: string };
console.log(id);
const userLevelNumber = getCookiesDecrypt("ulne");
const userId = getCookiesDecrypt("uie");
// State for various form fields
const [taskOutput, setTaskOutput] = useState({
all: false,
@ -102,6 +118,11 @@ export default function FormTaskDetail() {
const [checkedLevels, setCheckedLevels] = useState(new Set());
const [expandedPolda, setExpandedPolda] = useState([{}]);
const [isLoading, setIsLoading] = useState(false);
const [responses, setResponses] = useState<Response[]>([]);
const [response, setResponse] = useState<string>("");
const [showInput, setShowInput] = useState<boolean>(false);
const [listData, setListData] = useState([]);
const [message, setMessage] = useState<string>("");
const [platformTypeVisible, setPlatformTypeVisible] = useState(false);
const [unitSelection, setUnitSelection] = useState({
@ -286,6 +307,76 @@ export default function FormTaskDetail() {
}));
};
// const handleToggleInput = () => {
// setShowInput((prev: any) => !prev); // Toggle visibility of input field
// };
useEffect(() => {
async function initState() {
// loading();
const response = await getAssignmentResponseList(id);
console.log("data", response?.data?.data);
console.log("userLvl", userLevelNumber);
setListData(response?.data?.data);
close();
}
initState();
}, []);
// const handleSubmitResponse = () => {
// console.log("Response Submitted:", response);
// setShowInput(false); // Optionally hide the input after submission
// };
const handleToggleInput = (): void => {
setShowInput(!showInput);
};
const handleInputChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
setMessage(e.target.value);
};
// const handleSubmitResponse = (): void => {
// if (response.trim()) {
// const newResponse: Response = {
// id: Date.now(), // Unique ID for each response
// name: "Mabes Polri - Approver",
// content: response,
// timestamp: new Date().toLocaleString("id-ID"), // Format timestamp for Indonesia locale
// };
// setResponses([newResponse, ...responses]); // Add new response to the top
// setResponse(""); // Reset textarea
// setShowInput(false); // Hide input
// }
// };
const postData = () => {
sendSuggestionParent();
};
async function sendSuggestionParent() {
if (message?.length > 1) {
loading();
const data = {
assignmentId: id,
message, // Gunakan response di sini
parentId: null,
};
const response = await createAssignmentResponse(data);
console.log(response);
setMessage("");
const responseGet = await getAssignmentResponseList(id);
console.log(responseGet?.data?.data);
setListData(responseGet?.data?.data);
close();
}
}
return (
<Card>
<div className="px-6 py-6">
@ -559,13 +650,80 @@ export default function FormTaskDetail() {
)}
</div>
</div>
<div className="mt-4">
<Link href={"/contributor/task"}>
<Button color="primary" variant={"outline"}>
Cancel
<div className="flex flex-row justify-end gap-3">
<div className="">
<Button
color="primary"
variant={"outline"}
onClick={handleToggleInput}
>
Beri Tanggapan
</Button>
</Link>
</div>
<div className="">
<Button color="primary" variant={"default"}>
Terima Tugas
</Button>
</div>
<div className="">
<Button color="primary" variant={"default"}>
Hasil Upload
</Button>
</div>
</div>
{showInput && (
<div className="mt-4 border p-4 rounded bg-gray-50">
<Textarea
placeholder="Tulis tanggapan Anda di sini..."
value={message}
onChange={handleInputChange}
/>
<div className="flex justify-end mt-3">
<Button
color="primary"
onClick={() => postData()}
type="button"
>
Kirim Tanggapan
</Button>
</div>
</div>
)}
<div className="mt-6">
<h3 className="text-lg font-bold">Tanggapan</h3>
{listData?.map((item: any) => (
<div
key={item.id}
className="border p-4 mt-2 rounded bg-gray-100 flex flex-col"
>
<div className="flex justify-between items-center">
<span className="font-medium">{item.name}</span>
<span className="text-sm text-gray-500">
{item.timestamp}
</span>
</div>
<p className="mt-2">{item.content}</p>
<div className="flex gap-2 mt-2">
<button className="text-blue-500 hover:underline">
Balas
</button>
<button className="text-red-500 hover:underline">
Hapus
</button>
</div>
</div>
))}
</div>
<div className="flex justify-center mt-4">
{detail?.isDone !== true &&
(Number(userLevelNumber) !== 3 ||
Number(userLevelNumber) == 2) ? (
<Button color="primary" variant={"outline"}>
Forward
</Button>
) : (
""
)}
</div>
</form>
) : (

View File

@ -15,6 +15,24 @@ const Hero: React.FC = () => {
const [heroData, setHeroData] = useState<any>();
useEffect(() => {
async function fetchCategories() {
const url = 'https://netidhub.com/api/csrf';
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data; // Menampilkan data yang diterima dari API
} catch (error) {
console.error('Fetch error: ', error);
}
}
fetchCategories();
initFetch();
}, []);
const initFetch = async () => {

View File

@ -11,7 +11,7 @@ import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import { cn, setCookiesEncrypt } from "@/lib/utils";
import { Loader2 } from "lucide-react";
import { getProfile, login } from "@/service/auth";
import { getCsrfToken, getProfile, login } from "@/service/auth";
import { toast } from "sonner";
import { Link, useRouter } from "@/components/navigation";
import { warning } from "@/lib/swal";
@ -49,12 +49,15 @@ const LoginForm = () => {
// Fungsi submit form
const onSubmit: SubmitHandler<LoginFormValues> = async (data) => {
try {
// const response = null;
const response = await login({
...data,
grant_type: "password",
client_id: "mediahub-app",
grantType: "password",
clientId: "mediahub-app",
});
console.log("LOGIN: ", response);
if (response?.error) {
toast.error("Username / Password Tidak Sesuai");
} else {
@ -134,7 +137,6 @@ const LoginForm = () => {
) {
if (profile?.data?.data?.userLevel?.id == 761 || profile?.data?.data?.userLevel?.parentLevelId == 761) {
window.location.href = "/in/welcome";
// router.push('/admin/dashboard');
Cookies.set("status", "login", {
expires: 1,
});

13
package-lock.json generated
View File

@ -101,6 +101,7 @@
"react": "^18",
"react-advanced-news-ticker": "^1.0.1",
"react-apexcharts": "^1.4.1",
"react-audio-player": "^0.17.0",
"react-audio-voice-recorder": "^2.2.0",
"react-chartjs-2": "^5.2.0",
"react-cssfx-loading": "^2.1.0",
@ -13591,6 +13592,18 @@
"react": ">=0.13"
}
},
"node_modules/react-audio-player": {
"version": "0.17.0",
"resolved": "https://registry.npmjs.org/react-audio-player/-/react-audio-player-0.17.0.tgz",
"integrity": "sha512-aCZgusPxA9HK7rLZcTdhTbBH9l6do9vn3NorgoDZRxRxJlOy9uZWzPaKjd7QdcuP2vXpxGA/61JMnnOEY7NXeA==",
"dependencies": {
"prop-types": "^15.7.2"
},
"peerDependencies": {
"react": ">=16",
"react-dom": ">=16"
}
},
"node_modules/react-audio-visualize": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/react-audio-visualize/-/react-audio-visualize-1.2.0.tgz",

View File

@ -102,6 +102,7 @@
"react": "^18",
"react-advanced-news-ticker": "^1.0.1",
"react-apexcharts": "^1.4.1",
"react-audio-player": "^0.17.0",
"react-audio-voice-recorder": "^2.2.0",
"react-chartjs-2": "^5.2.0",
"react-cssfx-loading": "^2.1.0",

View File

@ -1,6 +1,6 @@
import qs from "qs";
import { getAPIDummy } from "./http-config/axiosCustom";
import { httpPost } from "./http-config/http-base-service";
import { httpGet, httpPost } from "./http-config/http-base-service";
import {
httpGetInterceptor,
httpGetInterceptorWithToken,
@ -8,12 +8,66 @@ import {
} from "./http-config/http-interceptor-service";
export async function login(data: any) {
const res = await getCsrfToken();
const csrfToken = res?.data?.token;
console.log("Token CSRF : ", csrfToken);
const pathUrl = "signin";
const headers = {
'content-type': 'application/x-www-form-urlencoded',
'accept': 'application/json',
'content-type': 'application/json',
'X-XSRF-TOKEN': csrfToken
};
return httpPost(pathUrl, headers, qs.stringify(data));
return httpPost(pathUrl, headers, data);
}
// export async function login(data: any, csrfToken: string) {
// const url = 'http://localhost:8080/mediahub/users/signin';
// try {
// const response = await fetch(url, {
// method: 'POST',
// credentials: 'include',
// headers: {
// 'Content-Type': 'application/json',
// 'X-XSRF-TOKEN': csrfToken || ''
// }
// });
// if (!response.ok) {
// throw new Error(`HTTP error! status: ${response.status}`);
// }
// return response; // Menampilkan data yang diterima dari API
// } catch (error) {
// console.error('Fetch error: ', error);
// }
// }
export async function getCsrfToken() {
const pathUrl = "csrf";
const headers = {
'content-type': 'application/json',
};
return httpGet(pathUrl, headers);
// const url = 'https://netidhub.com/api/csrf';
// try {
// const response = await fetch(url, {
// method: 'GET',
// credentials: 'include'
// });
// if (!response.ok) {
// throw new Error(`HTTP error! status: ${response.status}`);
// }
// const data = await response.json();
// console.log("csrf : ", data);
// return data;
// } catch (error) {
// console.error('Fetch error: ', error);
// }
}
export async function getProfile(token: any) {

View File

@ -7,6 +7,7 @@ const axiosBaseInstance = axios.create({
headers: {
"content-type": "application/json",
},
withCredentials: true,
});
export default axiosBaseInstance;

View File

@ -11,6 +11,7 @@ const axiosInterceptorInstance = axios.create({
headers: {
"content-type": "application/json",
},
withCredentials: true,
});
// Request interceptor
@ -39,9 +40,9 @@ axiosInterceptorInstance.interceptors.response.use(
if (error.response.status === 401 && !originalRequest._retry) {
originalRequest._retry = true;
const data = {
grant_type: "refresh_token",
refresh_token: refreshToken,
client_id: "mediahub-app",
grantType: "refresh_token",
refreshToken: refreshToken,
clientId: "mediahub-app",
};
console.log("refresh token ", data);
const res = await login(data);

View File

@ -0,0 +1,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 type { SourceEditing, SourceEditingConfig } from '@ckeditor/ckeditor5-source-editing';
declare module '@ckeditor/ckeditor5-core' {
interface EditorConfig {
/**
* The configuration of the source editing feature.
*
* Read more in {@link module:source-editing/sourceeditingconfig~SourceEditingConfig}.
*/
sourceEditing?: SourceEditingConfig;
}
interface PluginsMap {
[SourceEditing.pluginName]: SourceEditing;
}
}

View File

@ -0,0 +1,10 @@
/**
* @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
*/
/**
* @module source-editing
*/
export { default as SourceEditing } from '@ckeditor/ckeditor5-source-editing/src/sourceediting.js';
export type { SourceEditingConfig } from '@ckeditor/ckeditor5-source-editing/src/sourceeditingconfig.js';
import '@ckeditor/ckeditor5-source-editing/src/augmentation.js';

View File

@ -0,0 +1,104 @@
/**
* @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
*/
/**
* @module source-editing/sourceediting
*/
import { type Editor, Plugin, PendingActions } from '@ckeditor/ckeditor5-source-editing/node_modules/ckeditor5/src/core';
import '@ckeditor/ckeditor5-source-editing/theme/sourceediting.css';
/**
* The source editing feature.
*
* It provides the possibility to view and edit the source of the document.
*
* For a detailed overview, check the {@glink features/source-editing source editing feature documentation} and the
* {@glink api/source-editing package page}.
*/
export default class SourceEditing extends Plugin {
/**
* @inheritDoc
*/
static get pluginName(): "SourceEditing";
/**
* @inheritDoc
*/
static get requires(): readonly [typeof PendingActions];
/**
* Flag indicating whether the document source mode is active.
*
* @observable
*/
isSourceEditingMode: boolean;
/**
* The element replacer instance used to replace the editing roots with the wrapper elements containing the document source.
*/
private _elementReplacer;
/**
* Maps all root names to wrapper elements containing the document source.
*/
private _replacedRoots;
/**
* Maps all root names to their document data.
*/
private _dataFromRoots;
/**
* @inheritDoc
*/
constructor(editor: Editor);
/**
* @inheritDoc
*/
init(): void;
/**
* Updates the source data in all hidden editing roots.
*/
updateEditorData(): void;
private _checkCompatibility;
/**
* Creates source editing wrappers that replace each editing root. Each wrapper contains the document source from the corresponding
* root.
*
* The wrapper element contains a textarea and it solves the problem, that the textarea element cannot auto expand its height based on
* the content it contains. The solution is to make the textarea more like a plain div element, which expands in height as much as it
* needs to, in order to display the whole document source without scrolling. The wrapper element is a parent for the textarea and for
* the pseudo-element `::after`, that replicates the look, content, and position of the textarea. The pseudo-element replica is hidden,
* but it is styled to be an identical visual copy of the textarea with the same content. Then, the wrapper is a grid container and both
* of its children (the textarea and the `::after` pseudo-element) are positioned within a CSS grid to occupy the same grid cell. The
* content in the pseudo-element `::after` is set in CSS and it stretches the grid to the appropriate size based on the textarea value.
* Since both children occupy the same grid cell, both have always the same height.
*/
private _showSourceEditing;
/**
* Restores all hidden editing roots and sets the source data in them.
*/
private _hideSourceEditing;
/**
* Focuses the textarea containing document source from the first editing root.
*/
private _focusSourceEditing;
/**
* Disables all commands.
*/
private _disableCommands;
/**
* Clears forced disable for all commands, that was previously set through {@link #_disableCommands}.
*/
private _enableCommands;
/**
* Adds or removes the `readonly` attribute from the textarea from all roots, if document source mode is active.
*
* @param isReadOnly Indicates whether all textarea elements should be read-only.
*/
private _handleReadOnlyMode;
/**
* Checks, if the plugin is allowed to handle the source editing mode by itself. Currently, the source editing mode is supported only
* for the {@link module:editor-classic/classiceditor~ClassicEditor classic editor}.
*/
private _isAllowedToHandleSourceEditingMode;
/**
* If any {@link module:ui/dialog/dialogview~DialogView editor dialog} is currently visible, hide it.
*/
private _hideVisibleDialog;
private _createButton;
}

View File

@ -0,0 +1,76 @@
/**
* @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 { TableConfig, Table, TableCaption, TableCaptionEditing, TableCaptionUI, TableCellProperties, TableCellPropertiesEditing, TableCellPropertiesUI, TableCellWidthEditing, TableClipboard, TableColumnResize, TableColumnResizeEditing, TableEditing, TableKeyboard, TableMouse, TableProperties, TablePropertiesEditing, TablePropertiesUI, TableSelection, TableToolbar, TableUI, TableUtils, PlainTableOutput, InsertColumnCommand, InsertRowCommand, InsertTableCommand, MergeCellCommand, MergeCellsCommand, RemoveColumnCommand, RemoveRowCommand, SelectColumnCommand, SelectRowCommand, SetHeaderColumnCommand, SetHeaderRowCommand, SplitCellCommand, ToggleTableCaptionCommand, TableCellBackgroundColorCommand, TableCellBorderColorCommand, TableCellBorderStyleCommand, TableCellBorderWidthCommand, TableCellHeightCommand, TableCellHorizontalAlignmentCommand, TableCellPaddingCommand, TableCellVerticalAlignmentCommand, TableCellWidthCommand, TableAlignmentCommand, TableBackgroundColorCommand, TableBorderColorCommand, TableBorderStyleCommand, TableBorderWidthCommand, TableHeightCommand, TableWidthCommand } from '@ckeditor/ckeditor5-table';
declare module '@ckeditor/ckeditor5-core' {
interface EditorConfig {
/**
* The configuration of the {@link module:table/table~Table} feature.
*
* Read more in {@link module:table/tableconfig~TableConfig}.
*/
table?: TableConfig;
}
interface PluginsMap {
[Table.pluginName]: Table;
[TableCaption.pluginName]: TableCaption;
[TableCaptionEditing.pluginName]: TableCaptionEditing;
[TableCaptionUI.pluginName]: TableCaptionUI;
[TableCellProperties.pluginName]: TableCellProperties;
[TableCellPropertiesEditing.pluginName]: TableCellPropertiesEditing;
[TableCellPropertiesUI.pluginName]: TableCellPropertiesUI;
[TableCellWidthEditing.pluginName]: TableCellWidthEditing;
[TableClipboard.pluginName]: TableClipboard;
[TableColumnResize.pluginName]: TableColumnResize;
[TableColumnResizeEditing.pluginName]: TableColumnResizeEditing;
[TableEditing.pluginName]: TableEditing;
[TableKeyboard.pluginName]: TableKeyboard;
[TableMouse.pluginName]: TableMouse;
[TableProperties.pluginName]: TableProperties;
[TablePropertiesEditing.pluginName]: TablePropertiesEditing;
[TablePropertiesUI.pluginName]: TablePropertiesUI;
[TableSelection.pluginName]: TableSelection;
[TableToolbar.pluginName]: TableToolbar;
[TableUI.pluginName]: TableUI;
[TableUtils.pluginName]: TableUtils;
[PlainTableOutput.pluginName]: PlainTableOutput;
}
interface CommandsMap {
insertTableColumnLeft: InsertColumnCommand;
insertTableColumnRight: InsertColumnCommand;
insertTableRowAbove: InsertRowCommand;
insertTableRowBelow: InsertRowCommand;
insertTable: InsertTableCommand;
mergeTableCellRight: MergeCellCommand;
mergeTableCellLeft: MergeCellCommand;
mergeTableCellDown: MergeCellCommand;
mergeTableCellUp: MergeCellCommand;
mergeTableCells: MergeCellsCommand;
removeTableColumn: RemoveColumnCommand;
removeTableRow: RemoveRowCommand;
selectTableColumn: SelectColumnCommand;
selectTableRow: SelectRowCommand;
setTableColumnHeader: SetHeaderColumnCommand;
setTableRowHeader: SetHeaderRowCommand;
splitTableCellVertically: SplitCellCommand;
splitTableCellHorizontally: SplitCellCommand;
toggleTableCaption: ToggleTableCaptionCommand;
tableCellBackgroundColor: TableCellBackgroundColorCommand;
tableCellBorderColor: TableCellBorderColorCommand;
tableCellBorderStyle: TableCellBorderStyleCommand;
tableCellBorderWidth: TableCellBorderWidthCommand;
tableCellHeight: TableCellHeightCommand;
tableCellHorizontalAlignment: TableCellHorizontalAlignmentCommand;
tableCellPadding: TableCellPaddingCommand;
tableCellVerticalAlignment: TableCellVerticalAlignmentCommand;
tableCellWidth: TableCellWidthCommand;
tableAlignment: TableAlignmentCommand;
tableBackgroundColor: TableBackgroundColorCommand;
tableBorderColor: TableBorderColorCommand;
tableBorderStyle: TableBorderStyleCommand;
tableBorderWidth: TableBorderWidthCommand;
tableHeight: TableHeightCommand;
tableWidth: TableWidthCommand;
}
}

View File

@ -0,0 +1,60 @@
/**
* @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
*/
/**
* @module table
*/
export { default as PlainTableOutput } from '@ckeditor/ckeditor5-table/src/plaintableoutput.js';
export { default as Table } from '@ckeditor/ckeditor5-table/src/table.js';
export { default as TableEditing } from '@ckeditor/ckeditor5-table/src/tableediting.js';
export { default as TableUI } from '@ckeditor/ckeditor5-table/src/tableui.js';
export { default as TableToolbar } from '@ckeditor/ckeditor5-table/src/tabletoolbar.js';
export { default as TableCellProperties } from '@ckeditor/ckeditor5-table/src/tablecellproperties.js';
export { default as TableCellPropertiesEditing } from '@ckeditor/ckeditor5-table/src/tablecellproperties/tablecellpropertiesediting.js';
export { default as TableCellPropertiesUI } from '@ckeditor/ckeditor5-table/src/tablecellproperties/tablecellpropertiesui.js';
export { default as TableCellWidthEditing } from '@ckeditor/ckeditor5-table/src/tablecellwidth/tablecellwidthediting.js';
export { default as TableProperties } from '@ckeditor/ckeditor5-table/src/tableproperties.js';
export { default as TablePropertiesEditing } from '@ckeditor/ckeditor5-table/src/tableproperties/tablepropertiesediting.js';
export { default as TablePropertiesUI } from '@ckeditor/ckeditor5-table/src/tableproperties/tablepropertiesui.js';
export { default as TableCaption } from '@ckeditor/ckeditor5-table/src/tablecaption.js';
export { default as TableCaptionEditing } from '@ckeditor/ckeditor5-table/src/tablecaption/tablecaptionediting.js';
export { default as TableCaptionUI } from '@ckeditor/ckeditor5-table/src/tablecaption/tablecaptionui.js';
export { default as TableClipboard } from '@ckeditor/ckeditor5-table/src/tableclipboard.js';
export { default as TableMouse } from '@ckeditor/ckeditor5-table/src/tablemouse.js';
export { default as TableKeyboard } from '@ckeditor/ckeditor5-table/src/tablekeyboard.js';
export { default as TableSelection } from '@ckeditor/ckeditor5-table/src/tableselection.js';
export { default as TableUtils } from '@ckeditor/ckeditor5-table/src/tableutils.js';
export { default as TableColumnResize } from '@ckeditor/ckeditor5-table/src/tablecolumnresize.js';
export { default as TableColumnResizeEditing } from '@ckeditor/ckeditor5-table/src/tablecolumnresize/tablecolumnresizeediting.js';
export type { TableConfig } from '@ckeditor/ckeditor5-table/src/tableconfig.js';
export type { default as InsertColumnCommand } from '@ckeditor/ckeditor5-table/src/commands/insertcolumncommand.js';
export type { default as InsertRowCommand } from '@ckeditor/ckeditor5-table/src/commands/insertrowcommand.js';
export type { default as InsertTableCommand } from '@ckeditor/ckeditor5-table/src/commands/inserttablecommand.js';
export type { default as MergeCellCommand } from '@ckeditor/ckeditor5-table/src/commands/mergecellcommand.js';
export type { default as MergeCellsCommand } from '@ckeditor/ckeditor5-table/src/commands/mergecellscommand.js';
export type { default as RemoveColumnCommand } from '@ckeditor/ckeditor5-table/src/commands/removecolumncommand.js';
export type { default as RemoveRowCommand } from '@ckeditor/ckeditor5-table/src/commands/removerowcommand.js';
export type { default as SelectColumnCommand } from '@ckeditor/ckeditor5-table/src/commands/selectcolumncommand.js';
export type { default as SelectRowCommand } from '@ckeditor/ckeditor5-table/src/commands/selectrowcommand.js';
export type { default as SetHeaderColumnCommand } from '@ckeditor/ckeditor5-table/src/commands/setheadercolumncommand.js';
export type { default as SetHeaderRowCommand } from '@ckeditor/ckeditor5-table/src/commands/setheaderrowcommand.js';
export type { default as SplitCellCommand } from '@ckeditor/ckeditor5-table/src/commands/splitcellcommand.js';
export type { default as ToggleTableCaptionCommand } from '@ckeditor/ckeditor5-table/src/tablecaption/toggletablecaptioncommand.js';
export type { default as TableCellBackgroundColorCommand } from '@ckeditor/ckeditor5-table/src/tablecellproperties/commands/tablecellbackgroundcolorcommand.js';
export type { default as TableCellBorderColorCommand } from '@ckeditor/ckeditor5-table/src/tablecellproperties/commands/tablecellbordercolorcommand.js';
export type { default as TableCellBorderStyleCommand } from '@ckeditor/ckeditor5-table/src/tablecellproperties/commands/tablecellborderstylecommand.js';
export type { default as TableCellBorderWidthCommand } from '@ckeditor/ckeditor5-table/src/tablecellproperties/commands/tablecellborderwidthcommand.js';
export type { default as TableCellHeightCommand } from '@ckeditor/ckeditor5-table/src/tablecellproperties/commands/tablecellheightcommand.js';
export type { default as TableCellHorizontalAlignmentCommand } from '@ckeditor/ckeditor5-table/src/tablecellproperties/commands/tablecellhorizontalalignmentcommand.js';
export type { default as TableCellPaddingCommand } from '@ckeditor/ckeditor5-table/src/tablecellproperties/commands/tablecellpaddingcommand.js';
export type { default as TableCellVerticalAlignmentCommand } from '@ckeditor/ckeditor5-table/src/tablecellproperties/commands/tablecellverticalalignmentcommand.js';
export type { default as TableCellWidthCommand } from '@ckeditor/ckeditor5-table/src/tablecellwidth/commands/tablecellwidthcommand.js';
export type { default as TableAlignmentCommand } from '@ckeditor/ckeditor5-table/src/tableproperties/commands/tablealignmentcommand.js';
export type { default as TableBackgroundColorCommand } from '@ckeditor/ckeditor5-table/src/tableproperties/commands/tablebackgroundcolorcommand.js';
export type { default as TableBorderColorCommand } from '@ckeditor/ckeditor5-table/src/tableproperties/commands/tablebordercolorcommand.js';
export type { default as TableBorderStyleCommand } from '@ckeditor/ckeditor5-table/src/tableproperties/commands/tableborderstylecommand.js';
export type { default as TableBorderWidthCommand } from '@ckeditor/ckeditor5-table/src/tableproperties/commands/tableborderwidthcommand.js';
export type { default as TableHeightCommand } from '@ckeditor/ckeditor5-table/src/tableproperties/commands/tableheightcommand.js';
export type { default as TableWidthCommand } from '@ckeditor/ckeditor5-table/src/tableproperties/commands/tablewidthcommand.js';
import '@ckeditor/ckeditor5-table/src/augmentation.js';

View File

@ -0,0 +1,26 @@
/**
* @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
*/
/**
* @module table/plaintableoutput
*/
import { Plugin } from 'ckeditor5/src/core.js';
import Table from '@ckeditor/ckeditor5-table/src/table.js';
/**
* The plain table output feature.
*/
export default class PlainTableOutput extends Plugin {
/**
* @inheritDoc
*/
static get pluginName(): "PlainTableOutput";
/**
* @inheritDoc
*/
static get requires(): readonly [typeof Table];
/**
* @inheritDoc
*/
init(): void;
}

View File

@ -0,0 +1,40 @@
/**
* @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
*/
/**
* @module table/table
*/
import { Plugin } from 'ckeditor5/src/core.js';
import { Widget } from 'ckeditor5/src/widget.js';
import TableEditing from '@ckeditor/ckeditor5-table/src/tableediting.js';
import TableUI from '@ckeditor/ckeditor5-table/src/tableui.js';
import TableSelection from '@ckeditor/ckeditor5-table/src/tableselection.js';
import TableClipboard from '@ckeditor/ckeditor5-table/src/tableclipboard.js';
import TableKeyboard from '@ckeditor/ckeditor5-table/src/tablekeyboard.js';
import TableMouse from '@ckeditor/ckeditor5-table/src/tablemouse.js';
import '@ckeditor/ckeditor5-table/theme/table.css';
/**
* The table plugin.
*
* For a detailed overview, check the {@glink features/tables/tables Table feature documentation}.
*
* This is a "glue" plugin that loads the following table features:
*
* * {@link module:table/tableediting~TableEditing editing feature},
* * {@link module:table/tableselection~TableSelection selection feature},
* * {@link module:table/tablekeyboard~TableKeyboard keyboard navigation feature},
* * {@link module:table/tablemouse~TableMouse mouse selection feature},
* * {@link module:table/tableclipboard~TableClipboard clipboard feature},
* * {@link module:table/tableui~TableUI UI feature}.
*/
export default class Table extends Plugin {
/**
* @inheritDoc
*/
static get requires(): readonly [typeof TableEditing, typeof TableUI, typeof TableSelection, typeof TableMouse, typeof TableKeyboard, typeof TableClipboard, typeof Widget];
/**
* @inheritDoc
*/
static get pluginName(): "Table";
}

View File

@ -0,0 +1,24 @@
/**
* @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
*/
/**
* @module table/tablecaption
*/
import { Plugin } from 'ckeditor5/src/core.js';
import TableCaptionEditing from '@ckeditor/ckeditor5-table/src/tablecaption/tablecaptionediting.js';
import TableCaptionUI from '@ckeditor/ckeditor5-table/src/tablecaption/tablecaptionui.js';
import '@ckeditor/ckeditor5-table/theme/tablecaption.css';
/**
* The table caption plugin.
*/
export default class TableCaption extends Plugin {
/**
* @inheritDoc
*/
static get pluginName(): "TableCaption";
/**
* @inheritDoc
*/
static get requires(): readonly [typeof TableCaptionEditing, typeof TableCaptionUI];
}

View File

@ -0,0 +1,30 @@
/**
* @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
*/
/**
* @module table/tablecellproperties
*/
import { Plugin } from 'ckeditor5/src/core.js';
import TableCellPropertiesUI from '@ckeditor/ckeditor5-table/src/tablecellproperties/tablecellpropertiesui.js';
import TableCellPropertiesEditing from '@ckeditor/ckeditor5-table/src/tablecellproperties/tablecellpropertiesediting.js';
/**
* The table cell properties feature. Enables support for setting properties of table cells (size, border, background, etc.).
*
* Read more in the {@glink features/tables/tables-styling Table and cell styling tools} section.
* See also the {@link module:table/tableproperties~TableProperties} plugin.
*
* This is a "glue" plugin that loads the
* {@link module:table/tablecellproperties/tablecellpropertiesediting~TableCellPropertiesEditing table cell properties editing feature} and
* the {@link module:table/tablecellproperties/tablecellpropertiesui~TableCellPropertiesUI table cell properties UI feature}.
*/
export default class TableCellProperties extends Plugin {
/**
* @inheritDoc
*/
static get pluginName(): "TableCellProperties";
/**
* @inheritDoc
*/
static get requires(): readonly [typeof TableCellPropertiesEditing, typeof TableCellPropertiesUI];
}

View File

@ -0,0 +1,32 @@
/**
* @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
*/
/**
* @module table/tablecellproperties/commands/tablecellbackgroundcolorcommand
*/
import type { Editor } from 'ckeditor5/src/core.js';
import TableCellPropertyCommand from '@ckeditor/ckeditor5-table/src/tablecellproperties/commands/tablecellpropertycommand.js';
/**
* The table cell background color command.
*
* The command is registered by the {@link module:table/tablecellproperties/tablecellpropertiesediting~TableCellPropertiesEditing} as
* the `'tableCellBackgroundColor'` editor command.
*
* To change the background color of selected cells, execute the command:
*
* ```ts
* editor.execute( 'tableCellBackgroundColor', {
* value: '#f00'
* } );
* ```
*/
export default class TableCellBackgroundColorCommand extends TableCellPropertyCommand {
/**
* Creates a new `TableCellBackgroundColorCommand` instance.
*
* @param editor An editor in which this command will be used.
* @param defaultValue The default value of the attribute.
*/
constructor(editor: Editor, defaultValue: string);
}

View File

@ -0,0 +1,37 @@
/**
* @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
*/
/**
* @module table/tablecellproperties/commands/tablecellbordercolorcommand
*/
import type { Element } from 'ckeditor5/src/engine.js';
import type { Editor } from 'ckeditor5/src/core.js';
import TableCellPropertyCommand from '@ckeditor/ckeditor5-table/src/tablecellproperties/commands/tablecellpropertycommand.js';
/**
* The table cell border color command.
*
* The command is registered by the {@link module:table/tablecellproperties/tablecellpropertiesediting~TableCellPropertiesEditing} as
* the `'tableCellBorderColor'` editor command.
*
* To change the border color of selected cells, execute the command:
*
* ```ts
* editor.execute( 'tableCellBorderColor', {
* value: '#f00'
* } );
* ```
*/
export default class TableCellBorderColorCommand extends TableCellPropertyCommand {
/**
* Creates a new `TableCellBorderColorCommand` instance.
*
* @param editor An editor in which this command will be used.
* @param defaultValue The default value of the attribute.
*/
constructor(editor: Editor, defaultValue: string);
/**
* @inheritDoc
*/
protected _getAttribute(tableCell: Element): unknown;
}

View File

@ -0,0 +1,37 @@
/**
* @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
*/
/**
* @module table/tablecellproperties/commands/tablecellborderstylecommand
*/
import type { Editor } from 'ckeditor5/src/core.js';
import type { Element } from 'ckeditor5/src/engine.js';
import TableCellPropertyCommand from '@ckeditor/ckeditor5-table/src/tablecellproperties/commands/tablecellpropertycommand.js';
/**
* The table cell border style command.
*
* The command is registered by the {@link module:table/tablecellproperties/tablecellpropertiesediting~TableCellPropertiesEditing} as
* the `'tableCellBorderStyle'` editor command.
*
* To change the border style of selected cells, execute the command:
*
* ```ts
* editor.execute( 'tableCellBorderStyle', {
* value: 'dashed'
* } );
* ```
*/
export default class TableCellBorderStyleCommand extends TableCellPropertyCommand {
/**
* Creates a new `TableCellBorderStyleCommand` instance.
*
* @param editor An editor in which this command will be used.
* @param defaultValue The default value of the attribute.
*/
constructor(editor: Editor, defaultValue: string);
/**
* @inheritDoc
*/
protected _getAttribute(tableCell: Element): unknown;
}

View File

@ -0,0 +1,51 @@
/**
* @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
*/
/**
* @module table/tablecellproperties/commands/tablecellborderwidthcommand
*/
import type { Editor } from 'ckeditor5/src/core.js';
import type { Element } from 'ckeditor5/src/engine.js';
import TableCellPropertyCommand from '@ckeditor/ckeditor5-table/src/tablecellproperties/commands/tablecellpropertycommand.js';
/**
* The table cell border width command.
*
* The command is registered by the {@link module:table/tablecellproperties/tablecellpropertiesediting~TableCellPropertiesEditing} as
* the `'tableCellBorderWidth'` editor command.
*
* To change the border width of selected cells, execute the command:
*
* ```ts
* editor.execute( 'tableCellBorderWidth', {
* value: '5px'
* } );
* ```
*
* **Note**: This command adds the default `'px'` unit to numeric values. Executing:
*
* ```ts
* editor.execute( 'tableCellBorderWidth', {
* value: '5'
* } );
* ```
*
* will set the `borderWidth` attribute to `'5px'` in the model.
*/
export default class TableCellBorderWidthCommand extends TableCellPropertyCommand {
/**
* Creates a new `TableCellBorderWidthCommand` instance.
*
* @param editor An editor in which this command will be used.
* @param defaultValue The default value of the attribute.
*/
constructor(editor: Editor, defaultValue: string);
/**
* @inheritDoc
*/
protected _getAttribute(tableCell: Element): unknown;
/**
* @inheritDoc
*/
protected _getValueToSet(value: string | number | undefined): unknown;
}

View File

@ -0,0 +1,46 @@
/**
* @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
*/
/**
* @module table/tablecellproperties/commands/tablecellheightcommand
*/
import type { Editor } from 'ckeditor5/src/core.js';
import TableCellPropertyCommand from '@ckeditor/ckeditor5-table/src/tablecellproperties/commands/tablecellpropertycommand.js';
/**
* The table cell height command.
*
* The command is registered by the {@link module:table/tablecellproperties/tablecellpropertiesediting~TableCellPropertiesEditing} as
* the `'tableCellHeight'` editor command.
*
* To change the height of selected cells, execute the command:
*
* ```ts
* editor.execute( 'tableCellHeight', {
* value: '50px'
* } );
* ```
*
* **Note**: This command adds the default `'px'` unit to numeric values. Executing:
*
* ```ts
* editor.execute( 'tableCellHeight', {
* value: '50'
* } );
* ```
*
* will set the `height` attribute to `'50px'` in the model.
*/
export default class TableCellHeightCommand extends TableCellPropertyCommand {
/**
* Creates a new `TableCellHeightCommand` instance.
*
* @param editor An editor in which this command will be used.
* @param defaultValue The default value of the attribute.
*/
constructor(editor: Editor, defaultValue: string);
/**
* @inheritDoc
*/
protected _getValueToSet(value: string | number | undefined): unknown;
}

View File

@ -0,0 +1,32 @@
/**
* @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
*/
/**
* @module table/tablecellproperties/commands/tablecellhorizontalalignmentcommand
*/
import type { Editor } from 'ckeditor5/src/core.js';
import TableCellPropertyCommand from '@ckeditor/ckeditor5-table/src/tablecellproperties/commands/tablecellpropertycommand.js';
/**
* The table cell horizontal alignment command.
*
* The command is registered by the {@link module:table/tablecellproperties/tablecellpropertiesediting~TableCellPropertiesEditing} as
* the `'tableCellHorizontalAlignment'` editor command.
*
* To change the horizontal text alignment of selected cells, execute the command:
*
* ```ts
* editor.execute( 'tableCellHorizontalAlignment', {
* value: 'right'
* } );
* ```
*/
export default class TableCellHorizontalAlignmentCommand extends TableCellPropertyCommand {
/**
* Creates a new `TableCellHorizontalAlignmentCommand` instance.
*
* @param editor An editor in which this command will be used.
* @param defaultValue The default value for the "alignment" attribute.
*/
constructor(editor: Editor, defaultValue: string);
}

View File

@ -0,0 +1,51 @@
/**
* @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
*/
/**
* @module table/tablecellproperties/commands/tablecellpaddingcommand
*/
import type { Editor } from 'ckeditor5/src/core.js';
import type { Element } from 'ckeditor5/src/engine.js';
import TableCellPropertyCommand from '@ckeditor/ckeditor5-table/src/tablecellproperties/commands/tablecellpropertycommand.js';
/**
* The table cell padding command.
*
* The command is registered by the {@link module:table/tablecellproperties/tablecellpropertiesediting~TableCellPropertiesEditing} as
* the `'tableCellPadding'` editor command.
*
* To change the padding of selected cells, execute the command:
*
* ```ts
* editor.execute( 'tableCellPadding', {
* value: '5px'
* } );
* ```
*
* **Note**: This command adds the default `'px'` unit to numeric values. Executing:
*
* ```ts
* editor.execute( 'tableCellPadding', {
* value: '5'
* } );
* ```
*
* will set the `padding` attribute to `'5px'` in the model.
*/
export default class TableCellPaddingCommand extends TableCellPropertyCommand {
/**
* Creates a new `TableCellPaddingCommand` instance.
*
* @param editor An editor in which this command will be used.
* @param defaultValue The default value of the attribute.
*/
constructor(editor: Editor, defaultValue: string);
/**
* @inheritDoc
*/
protected _getAttribute(tableCell: Element): unknown;
/**
* @inheritDoc
*/
protected _getValueToSet(value: string | number | undefined): unknown;
}

View File

@ -0,0 +1,40 @@
/**
* @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
*/
/**
* @module table/tablecellproperties/commands/tablecellverticalalignmentcommand
*/
import type { Editor } from 'ckeditor5/src/core.js';
import TableCellPropertyCommand from '@ckeditor/ckeditor5-table/src/tablecellproperties/commands/tablecellpropertycommand.js';
/**
* The table cell vertical alignment command.
*
* The command is registered by the {@link module:table/tablecellproperties/tablecellpropertiesediting~TableCellPropertiesEditing} as
* the `'tableCellVerticalAlignment'` editor command.
*
* To change the vertical text alignment of selected cells, execute the command:
*
* ```ts
* editor.execute( 'tableCellVerticalAlignment', {
* value: 'top'
* } );
* ```
*
* The following values, corresponding to the
* [`vertical-align` CSS attribute](https://developer.mozilla.org/en-US/docs/Web/CSS/vertical-align), are allowed:
*
* * `'top'`
* * `'bottom'`
*
* The `'middle'` value is the default one so there is no need to set it.
*/
export default class TableCellVerticalAlignmentCommand extends TableCellPropertyCommand {
/**
* Creates a new `TableCellVerticalAlignmentCommand` instance.
*
* @param editor An editor in which this command will be used.
* @param defaultValue The default value for the "alignment" attribute.
*/
constructor(editor: Editor, defaultValue: string);
}

View File

@ -0,0 +1,43 @@
/**
* @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
*/
/**
* @module table/tablecellproperties/tablecellpropertiesediting
*/
import { Plugin } from 'ckeditor5/src/core.js';
import TableEditing from '@ckeditor/ckeditor5-table/src/tableediting.js';
import TableCellWidthEditing from '@ckeditor/ckeditor5-table/src/tablecellwidth/tablecellwidthediting.js';
/**
* The table cell properties editing feature.
*
* Introduces table cell model attributes and their conversion:
*
* - border: `tableCellBorderStyle`, `tableCellBorderColor` and `tableCellBorderWidth`
* - background color: `tableCellBackgroundColor`
* - cell padding: `tableCellPadding`
* - horizontal and vertical alignment: `tableCellHorizontalAlignment`, `tableCellVerticalAlignment`
* - cell width and height: `tableCellWidth`, `tableCellHeight`
*
* It also registers commands used to manipulate the above attributes:
*
* - border: the `'tableCellBorderStyle'`, `'tableCellBorderColor'` and `'tableCellBorderWidth'` commands
* - background color: the `'tableCellBackgroundColor'` command
* - cell padding: the `'tableCellPadding'` command
* - horizontal and vertical alignment: the `'tableCellHorizontalAlignment'` and `'tableCellVerticalAlignment'` commands
* - width and height: the `'tableCellWidth'` and `'tableCellHeight'` commands
*/
export default class TableCellPropertiesEditing extends Plugin {
/**
* @inheritDoc
*/
static get pluginName(): "TableCellPropertiesEditing";
/**
* @inheritDoc
*/
static get requires(): readonly [typeof TableEditing, typeof TableCellWidthEditing];
/**
* @inheritDoc
*/
init(): void;
}

View File

@ -0,0 +1,112 @@
/**
* @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
*/
/**
* @module table/tablecellproperties/tablecellpropertiesui
*/
import { Plugin, type Editor } from 'ckeditor5/src/core.js';
import { ContextualBalloon } from 'ckeditor5/src/ui.js';
import TableCellPropertiesView from '@ckeditor/ckeditor5-table/src/tablecellproperties/ui/tablecellpropertiesview.js';
/**
* The table cell properties UI plugin. It introduces the `'tableCellProperties'` button
* that opens a form allowing to specify the visual styling of a table cell.
*
* It uses the {@link module:ui/panel/balloon/contextualballoon~ContextualBalloon contextual balloon plugin}.
*/
export default class TableCellPropertiesUI extends Plugin {
/**
* The default table cell properties.
*/
private _defaultTableCellProperties;
/**
* The contextual balloon plugin instance.
*/
private _balloon?;
/**
* The cell properties form view displayed inside the balloon.
*/
view?: TableCellPropertiesView | null;
/**
* The batch used to undo all changes made by the form (which are live, as the user types)
* when "Cancel" was pressed. Each time the view is shown, a new batch is created.
*/
private _undoStepBatch?;
/**
* Flag used to indicate whether view is ready to execute update commands
* (it finished loading initial data).
*/
private _isReady?;
/**
* @inheritDoc
*/
static get requires(): readonly [typeof ContextualBalloon];
/**
* @inheritDoc
*/
static get pluginName(): "TableCellPropertiesUI";
/**
* @inheritDoc
*/
constructor(editor: Editor);
/**
* @inheritDoc
*/
init(): void;
/**
* @inheritDoc
*/
destroy(): void;
/**
* Creates the {@link module:table/tablecellproperties/ui/tablecellpropertiesview~TableCellPropertiesView} instance.
*
* @returns The cell properties form view instance.
*/
private _createPropertiesView;
/**
* In this method the "editor data -> UI" binding is happening.
*
* When executed, this method obtains selected cell property values from various table commands
* and passes them to the {@link #view}.
*
* This way, the UI stays uptodate with the editor data.
*/
private _fillViewFormFromCommandValues;
/**
* Shows the {@link #view} in the {@link #_balloon}.
*
* **Note**: Each time a view is shown, a new {@link #_undoStepBatch} is created. It contains
* all changes made to the document when the view is visible, allowing a single undo step
* for all of them.
*/
protected _showView(): void;
/**
* Removes the {@link #view} from the {@link #_balloon}.
*/
protected _hideView(): void;
/**
* Repositions the {@link #_balloon} or hides the {@link #view} if a table cell is no longer selected.
*/
protected _updateView(): void;
/**
* Returns `true` when the {@link #view} is visible in the {@link #_balloon}.
*/
private get _isViewVisible();
/**
* Returns `true` when the {@link #view} is in the {@link #_balloon}.
*/
private get _isViewInBalloon();
/**
* Creates a callback that when executed upon the {@link #view view's} property change
* executes a related editor command with the new property value.
*
* @param defaultValue The default value of the command.
*/
private _getPropertyChangeCallback;
/**
* Creates a callback that when executed upon the {@link #view view's} property change:
* * Executes a related editor command with the new property value if the value is valid,
* * Or sets the error text next to the invalid field, if the value did not pass the validation.
*/
private _getValidatedPropertyChangeCallback;
}

View File

@ -0,0 +1,228 @@
/**
* @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
*/
/**
* @module table/tablecellproperties/ui/tablecellpropertiesview
*/
import { ButtonView, FocusCycler, LabeledFieldView, ToolbarView, View, ViewCollection, type FocusableView, type NormalizedColorOption, type ColorPickerConfig } from 'ckeditor5/src/ui.js';
import { KeystrokeHandler, FocusTracker, type Locale } from 'ckeditor5/src/utils.js';
import type ColorInputView from '@ckeditor/ckeditor5-table/src/ui/colorinputview.js';
import type { TableCellPropertiesOptions } from '@ckeditor/ckeditor5-table/src/tableconfig.js';
import '@ckeditor/ckeditor5-table/theme/form.css';
import '@ckeditor/ckeditor5-table/theme/tableform.css';
import '@ckeditor/ckeditor5-table/theme/tablecellproperties.css';
export interface TableCellPropertiesViewOptions {
borderColors: Array<NormalizedColorOption>;
backgroundColors: Array<NormalizedColorOption>;
defaultTableCellProperties: TableCellPropertiesOptions;
colorPickerConfig: false | ColorPickerConfig;
}
/**
* The class representing a table cell properties form, allowing users to customize
* certain style aspects of a table cell, for instance, border, padding, text alignment, etc..
*/
export default class TableCellPropertiesView extends View {
/**
* The value of the cell border style.
*
* @observable
* @default ''
*/
borderStyle: string;
/**
* The value of the cell border width style.
*
* @observable
* @default ''
*/
borderWidth: string;
/**
* The value of the cell border color style.
*
* @observable
* @default ''
*/
borderColor: string;
/**
* The value of the cell padding style.
*
* @observable
* @default ''
*/
padding: string;
/**
* The value of the cell background color style.
*
* @observable
* @default ''
*/
backgroundColor: string;
/**
* The value of the table cell width style.
*
* @observable
* @default ''
*/
width: string;
/**
* The value of the table cell height style.
*
* @observable
* @default ''
*/
height: string;
/**
* The value of the horizontal text alignment style.
*
* @observable
* @default ''
*/
horizontalAlignment: string;
/**
* The value of the vertical text alignment style.
*
* @observable
* @default ''
*/
verticalAlignment: string;
/**
* Options passed to the view. See {@link #constructor} to learn more.
*/
readonly options: TableCellPropertiesViewOptions;
/**
* Tracks information about the DOM focus in the form.
*/
readonly focusTracker: FocusTracker;
/**
* An instance of the {@link module:utils/keystrokehandler~KeystrokeHandler}.
*/
readonly keystrokes: KeystrokeHandler;
/**
* A collection of child views in the form.
*/
readonly children: ViewCollection;
/**
* A dropdown that allows selecting the style of the table cell border.
*/
readonly borderStyleDropdown: LabeledFieldView<FocusableView>;
/**
* An input that allows specifying the width of the table cell border.
*/
readonly borderWidthInput: LabeledFieldView<FocusableView>;
/**
* An input that allows specifying the color of the table cell border.
*/
readonly borderColorInput: LabeledFieldView<ColorInputView>;
/**
* An input that allows specifying the table cell background color.
*/
readonly backgroundInput: LabeledFieldView<ColorInputView>;
/**
* An input that allows specifying the table cell padding.
*/
readonly paddingInput: LabeledFieldView;
/**
* An input that allows specifying the table cell width.
*/
readonly widthInput: LabeledFieldView<FocusableView>;
/**
* An input that allows specifying the table cell height.
*/
readonly heightInput: LabeledFieldView<FocusableView>;
/**
* A toolbar with buttons that allow changing the horizontal text alignment in a table cell.
*/
readonly horizontalAlignmentToolbar: ToolbarView;
/**
* A toolbar with buttons that allow changing the vertical text alignment in a table cell.
*/
readonly verticalAlignmentToolbar: ToolbarView;
/**
* The "Save" button view.
*/
saveButtonView: ButtonView;
/**
* The "Cancel" button view.
*/
cancelButtonView: ButtonView;
/**
* A collection of views that can be focused in the form.
*/
protected readonly _focusables: ViewCollection<FocusableView>;
/**
* Helps cycling over {@link #_focusables} in the form.
*/
protected readonly _focusCycler: FocusCycler;
/**
* @param locale The {@link module:core/editor/editor~Editor#locale} instance.
* @param options Additional configuration of the view.
* @param options.borderColors A configuration of the border color palette used by the
* {@link module:table/tablecellproperties/ui/tablecellpropertiesview~TableCellPropertiesView#borderColorInput}.
* @param options.backgroundColors A configuration of the background color palette used by the
* {@link module:table/tablecellproperties/ui/tablecellpropertiesview~TableCellPropertiesView#backgroundInput}.
* @param options.defaultTableCellProperties The default table cell properties.
*/
constructor(locale: Locale, options: TableCellPropertiesViewOptions);
/**
* @inheritDoc
*/
render(): void;
/**
* @inheritDoc
*/
destroy(): void;
/**
* Focuses the fist focusable field in the form.
*/
focus(): void;
/**
* Creates the following form fields:
*
* * {@link #borderStyleDropdown},
* * {@link #borderWidthInput},
* * {@link #borderColorInput}.
*/
private _createBorderFields;
/**
* Creates the following form fields:
*
* * {@link #backgroundInput}.
*/
private _createBackgroundFields;
/**
* Creates the following form fields:
*
* * {@link #widthInput}.
* * {@link #heightInput}.
*/
private _createDimensionFields;
/**
* Creates the following form fields:
*
* * {@link #paddingInput}.
*/
private _createPaddingField;
/**
* Creates the following form fields:
*
* * {@link #horizontalAlignmentToolbar},
* * {@link #verticalAlignmentToolbar}.
*/
private _createAlignmentFields;
/**
* Creates the following form controls:
*
* * {@link #saveButtonView},
* * {@link #cancelButtonView}.
*/
private _createActionButtons;
/**
* Provides localized labels for {@link #horizontalAlignmentToolbar} buttons.
*/
private get _horizontalAlignmentLabels();
/**
* Provides localized labels for {@link #verticalAlignmentToolbar} buttons.
*/
private get _verticalAlignmentLabels();
}

View File

@ -0,0 +1,46 @@
/**
* @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
*/
/**
* @module table/tablecellwidth/commands/tablecellwidthcommand
*/
import type { Editor } from 'ckeditor5/src/core.js';
import TableCellPropertyCommand from '@ckeditor/ckeditor5-table/src/tablecellproperties/commands/tablecellpropertycommand.js';
/**
* The table cell width command.
*
* The command is registered by the {@link module:table/tablecellwidth/tablecellwidthediting~TableCellWidthEditing} as
* the `'tableCellWidth'` editor command.
*
* To change the width of selected cells, execute the command:
*
* ```ts
* editor.execute( 'tableCellWidth', {
* value: '50px'
* } );
* ```
*
* **Note**: This command adds a default `'px'` unit to numeric values. Executing:
*
* ```ts
* editor.execute( 'tableCellWidth', {
* value: '50'
* } );
* ```
*
* will set the `width` attribute to `'50px'` in the model.
*/
export default class TableCellWidthCommand extends TableCellPropertyCommand {
/**
* Creates a new `TableCellWidthCommand` instance.
*
* @param editor An editor in which this command will be used.
* @param defaultValue The default value of the attribute.
*/
constructor(editor: Editor, defaultValue: string);
/**
* @inheritDoc
*/
_getValueToSet(value: string | number | undefined): unknown;
}

View File

@ -0,0 +1,29 @@
/**
* @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
*/
/**
* @module table/tablecellwidth/tablecellwidthediting
*/
import { Plugin } from 'ckeditor5/src/core.js';
import TableEditing from '@ckeditor/ckeditor5-table/src/tableediting.js';
/**
* The table cell width editing feature.
*
* Introduces `tableCellWidth` table cell model attribute alongside with its converters
* and a command.
*/
export default class TableCellWidthEditing extends Plugin {
/**
* @inheritDoc
*/
static get pluginName(): "TableCellWidthEditing";
/**
* @inheritDoc
*/
static get requires(): readonly [typeof TableEditing];
/**
* @inheritDoc
*/
init(): void;
}

View File

@ -0,0 +1,70 @@
/**
* @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 { ClipboardPipeline, ClipboardMarkersUtils } from 'ckeditor5/src/clipboard.js';
import { Plugin } from 'ckeditor5/src/core.js';
import type { DocumentFragment, Element, Item, Model, Position, Writer } from 'ckeditor5/src/engine.js';
import TableSelection from '@ckeditor/ckeditor5-table/src/tableselection.js';
import { type TableSlot } from '@ckeditor/ckeditor5-table/src/tablewalker.js';
import TableUtils from '@ckeditor/ckeditor5-table/src/tableutils.js';
/**
* This plugin adds support for copying/cutting/pasting fragments of tables.
* It is loaded automatically by the {@link module:table/table~Table} plugin.
*/
export default class TableClipboard extends Plugin {
/**
* @inheritDoc
*/
static get pluginName(): "TableClipboard";
/**
* @inheritDoc
*/
static get requires(): readonly [typeof ClipboardMarkersUtils, typeof ClipboardPipeline, typeof TableSelection, typeof TableUtils];
/**
* @inheritDoc
*/
init(): void;
/**
* Copies table content to a clipboard on "copy" & "cut" events.
*
* @param evt An object containing information about the handled event.
* @param data Clipboard event data.
*/
private _onCopyCut;
/**
* Overrides default {@link module:engine/model/model~Model#insertContent `model.insertContent()`} method to handle pasting table inside
* selected table fragment.
*
* Depending on selected table fragment:
* - If a selected table fragment is smaller than paste table it will crop pasted table to match dimensions.
* - If dimensions are equal it will replace selected table fragment with a pasted table contents.
*
* @param content The content to insert.
* @param selectable The selection into which the content should be inserted.
* If not provided the current model document selection will be used.
*/
private _onInsertContent;
/**
* Inserts provided `selectedTableCells` into `pastedTable`.
*/
private _replaceSelectedCells;
/**
* Replaces the part of selectedTable with pastedTable.
*/
private _replaceSelectedCellsWithPasted;
/**
* Replaces a single table slot.
*
* @returns Inserted table cell or null if slot should remain empty.
* @private
*/
_replaceTableSlotCell(tableSlot: TableSlot, cellToInsert: Element | null, insertPosition: Position, writer: Writer): Element | null;
/**
* Extracts the table for pasting into a table.
*
* @param content The content to insert.
* @param model The editor model.
*/
getTableIfOnlyTableInContent(content: DocumentFragment | Item, model: Model): Element | null;
}

View File

@ -0,0 +1,26 @@
/**
* @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
*/
/**
* @module table/tablecolumnresize
*/
import { Plugin } from 'ckeditor5/src/core.js';
import TableColumnResizeEditing from '@ckeditor/ckeditor5-table/src/tablecolumnresize/tablecolumnresizeediting.js';
import TableCellWidthEditing from '@ckeditor/ckeditor5-table/src/tablecellwidth/tablecellwidthediting.js';
import '@ckeditor/ckeditor5-table/theme/tablecolumnresize.css';
/**
* The table column resize feature.
*
* It provides the possibility to set the width of each column in a table using a resize handler.
*/
export default class TableColumnResize extends Plugin {
/**
* @inheritDoc
*/
static get requires(): readonly [typeof TableColumnResizeEditing, typeof TableCellWidthEditing];
/**
* @inheritDoc
*/
static get pluginName(): "TableColumnResize";
}

View File

@ -0,0 +1,139 @@
/**
* @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 { Plugin, type Editor } from 'ckeditor5/src/core.js';
import type { Element } from 'ckeditor5/src/engine.js';
import TableEditing from '@ckeditor/ckeditor5-table/src/tableediting.js';
import TableUtils from '@ckeditor/ckeditor5-table/src/tableutils.js';
/**
* The table column resize editing plugin.
*/
export default class TableColumnResizeEditing extends Plugin {
/**
* A flag indicating if the column resizing is in progress.
*/
private _isResizingActive;
/**
* A flag indicating if the column resizing is allowed. It is not allowed if the editor is in read-only
* or comments-only mode or the `TableColumnResize` plugin is disabled.
*
* @observable
* @internal
*/
_isResizingAllowed: boolean;
/**
* A temporary storage for the required data needed to correctly calculate the widths of the resized columns. This storage is
* initialized when column resizing begins, and is purged upon completion.
*/
private _resizingData;
/**
* DOM emitter.
*/
private _domEmitter;
/**
* A local reference to the {@link module:table/tableutils~TableUtils} plugin.
*/
private _tableUtilsPlugin;
/**
* @inheritDoc
*/
static get requires(): readonly [typeof TableEditing, typeof TableUtils];
/**
* @inheritDoc
*/
static get pluginName(): "TableColumnResizeEditing";
/**
* @inheritDoc
*/
constructor(editor: Editor);
/**
* @inheritDoc
*/
init(): void;
/**
* @inheritDoc
*/
destroy(): void;
/**
* Returns a 'tableColumnGroup' element from the 'table'.
*
* @param element A 'table' or 'tableColumnGroup' element.
* @returns A 'tableColumnGroup' element.
*/
getColumnGroupElement(element: Element): Element | undefined;
/**
* Returns an array of 'tableColumn' elements.
*
* @param element A 'table' or 'tableColumnGroup' element.
* @returns An array of 'tableColumn' elements.
*/
getTableColumnElements(element: Element): Array<Element>;
/**
* Returns an array of table column widths.
*
* @param element A 'table' or 'tableColumnGroup' element.
* @returns An array of table column widths.
*/
getTableColumnsWidths(element: Element): Array<string>;
/**
* Registers new attributes for a table model element.
*/
private _extendSchema;
/**
* Registers table column resize post-fixer.
*
* It checks if the change from the differ concerns a table-related element or attribute. For detected changes it:
* * Adjusts the `columnWidths` attribute to guarantee that the sum of the widths from all columns is 100%.
* * Checks if the `columnWidths` attribute gets updated accordingly after columns have been added or removed.
*/
private _registerPostFixer;
/**
* Registers table column resize converters.
*/
private _registerConverters;
/**
* Registers listeners to handle resizing process.
*/
private _registerResizingListeners;
/**
* Handles the `mousedown` event on column resizer element:
* * calculates the initial column pixel widths,
* * inserts the `<colgroup>` element if it is not present in the `<table>`,
* * puts the necessary data in the temporary storage,
* * applies the attributes to the `<table>` view element.
*
* @param eventInfo An object containing information about the fired event.
* @param domEventData The data related to the DOM event.
*/
private _onMouseDownHandler;
/**
* Handles the `mousemove` event.
* * If resizing process is not in progress, it does nothing.
* * If resizing is active but not allowed, it stops the resizing process instantly calling the `mousedown` event handler.
* * Otherwise it dynamically updates the widths of the resized columns.
*
* @param eventInfo An object containing information about the fired event.
* @param mouseEventData The native DOM event.
*/
private _onMouseMoveHandler;
/**
* Handles the `mouseup` event.
* * If resizing process is not in progress, it does nothing.
* * If resizing is active but not allowed, it cancels the resizing process restoring the original widths.
* * Otherwise it propagates the changes from view to the model by executing the adequate commands.
*/
private _onMouseUpHandler;
/**
* Retrieves and returns required data needed for the resizing process.
*
* @param domEventData The data of the `mousedown` event.
* @param columnWidths The current widths of the columns.
* @returns The data needed for the resizing process.
*/
private _getResizingData;
/**
* Registers a listener ensuring that each resizable cell have a resizer handle.
*/
private _registerResizerInserter;
}

View File

@ -0,0 +1,98 @@
/**
* @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
*/
/**
* @module table/tableediting
*/
import { Plugin, type Editor } from 'ckeditor5/src/core.js';
import type { PositionOffset, SlotFilter } from 'ckeditor5/src/engine.js';
import TableUtils from '@ckeditor/ckeditor5-table/src/tableutils.js';
import '@ckeditor/ckeditor5-table/theme/tableediting.css';
/**
* The table editing feature.
*/
export default class TableEditing extends Plugin {
/**
* Handlers for creating additional slots in the table.
*/
private _additionalSlots;
/**
* @inheritDoc
*/
static get pluginName(): "TableEditing";
/**
* @inheritDoc
*/
static get requires(): readonly [typeof TableUtils];
/**
* @inheritDoc
*/
constructor(editor: Editor);
/**
* @inheritDoc
*/
init(): void;
/**
* Registers downcast handler for the additional table slot.
*/
registerAdditionalSlot(slotHandler: AdditionalSlot): void;
}
/**
* By default, only the `tableRow` elements from the `table` model are downcast inside the `<table>` and
* all other elements are pushed outside the table. This handler allows creating additional slots inside
* the table for other elements.
*
* Take this model as an example:
*
* ```xml
* <table>
* <tableRow>...</tableRow>
* <tableRow>...</tableRow>
* <tableColumnGroup>...</tableColumnGroup>
* </table>
* ```
*
* By default, downcasting result will be:
*
* ```xml
* <table>
* <tbody>
* <tr>...</tr>
* <tr>...</tr>
* </tbody>
* </table>
* <colgroup>...</colgroup>
* ```
*
* To allow the `tableColumnGroup` element at the end of the table, use the following configuration:
*
* ```ts
* const additionalSlot = {
* filter: element => element.is( 'element', 'tableColumnGroup' ),
* positionOffset: 'end'
* }
* ```
*
* Now, the downcast result will be:
*
* ```xml
* <table>
* <tbody>
* <tr>...</tr>
* <tr>...</tr>
* </tbody>
* <colgroup>...</colgroup>
* </table>
* ```
*/
export interface AdditionalSlot {
/**
* Filter for elements that should be placed inside given slot.
*/
filter: SlotFilter;
/**
* Position of the slot within the table.
*/
positionOffset: PositionOffset;
}

View File

@ -0,0 +1,68 @@
/**
* @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
*/
/**
* @module table/tablekeyboard
*/
import TableSelection from '@ckeditor/ckeditor5-table/src/tableselection.js';
import TableUtils from '@ckeditor/ckeditor5-table/src/tableutils.js';
import { Plugin } from 'ckeditor5/src/core.js';
import { type ArrowKeyCodeDirection } from 'ckeditor5/src/utils.js';
import type { Element } from 'ckeditor5/src/engine.js';
/**
* This plugin enables keyboard navigation for tables.
* It is loaded automatically by the {@link module:table/table~Table} plugin.
*/
export default class TableKeyboard extends Plugin {
/**
* @inheritDoc
*/
static get pluginName(): "TableKeyboard";
/**
* @inheritDoc
*/
static get requires(): readonly [typeof TableSelection, typeof TableUtils];
/**
* @inheritDoc
*/
init(): void;
/**
* Handles {@link module:engine/view/document~Document#event:tab tab} events for the <kbd>Tab</kbd> key executed
* when the table widget is selected.
*/
private _handleTabOnSelectedTable;
/**
* Handles {@link module:engine/view/document~Document#event:tab tab} events for the <kbd>Tab</kbd> key executed
* inside table cells.
*/
private _handleTab;
/**
* Handles {@link module:engine/view/document~Document#event:keydown keydown} events.
*/
private _onArrowKey;
/**
* Handles arrow keys to move the selection around the table.
*
* @param direction The direction of the arrow key.
* @param expandSelection If the current selection should be expanded.
* @returns Returns `true` if key was handled.
*/
private _handleArrowKeys;
/**
* Returns `true` if the selection is at the boundary of a table cell according to the navigation direction.
*
* @param selection The current selection.
* @param tableCell The current table cell element.
* @param isForward The expected navigation direction.
*/
private _isSelectionAtCellEdge;
/**
* Moves the selection from the given table cell in the specified direction.
*
* @param focusCell The table cell that is current multi-cell selection focus.
* @param direction Direction in which selection should move.
* @param expandSelection If the current selection should be expanded. Default value is false.
*/
protected _navigateFromCellInDirection(focusCell: Element, direction: ArrowKeyCodeDirection, expandSelection?: boolean): void;
}

View File

@ -0,0 +1,48 @@
/**
* @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
*/
/**
* @module table/tablemouse
*/
import { Plugin } from 'ckeditor5/src/core.js';
import TableSelection from '@ckeditor/ckeditor5-table/src/tableselection.js';
import TableUtils from '@ckeditor/ckeditor5-table/src/tableutils.js';
/**
* This plugin enables a table cells' selection with the mouse.
* It is loaded automatically by the {@link module:table/table~Table} plugin.
*/
export default class TableMouse extends Plugin {
/**
* @inheritDoc
*/
static get pluginName(): "TableMouse";
/**
* @inheritDoc
*/
static get requires(): readonly [typeof TableSelection, typeof TableUtils];
/**
* @inheritDoc
*/
init(): void;
/**
* Enables making cells selection by <kbd>Shift</kbd>+click. Creates a selection from the cell which previously held
* the selection to the cell which was clicked. It can be the same cell, in which case it selects a single cell.
*/
private _enableShiftClickSelection;
/**
* Enables making cells selection by dragging.
*
* The selection is made only on mousemove. Mouse tracking is started on mousedown.
* However, the cells selection is enabled only after the mouse cursor left the anchor cell.
* Thanks to that normal text selection within one cell works just fine. However, you can still select
* just one cell by leaving the anchor cell and moving back to it.
*/
private _enableMouseDragSelection;
/**
* Returns the model table cell element based on the target element of the passed DOM event.
*
* @returns Returns the table cell or `undefined`.
*/
private _getModelTableCellFromDomEvent;
}

View File

@ -0,0 +1,30 @@
/**
* @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
*/
/**
* @module table/tableproperties
*/
import { Plugin } from 'ckeditor5/src/core.js';
import TablePropertiesEditing from '@ckeditor/ckeditor5-table/src/tableproperties/tablepropertiesediting.js';
import TablePropertiesUI from '@ckeditor/ckeditor5-table/src/tableproperties/tablepropertiesui.js';
/**
* The table properties feature. Enables support for setting properties of tables (size, border, background, etc.).
*
* Read more in the {@glink features/tables/tables-styling Table and cell styling tools} section.
* See also the {@link module:table/tablecellproperties~TableCellProperties} plugin.
*
* This is a "glue" plugin that loads the
* {@link module:table/tableproperties/tablepropertiesediting~TablePropertiesEditing table properties editing feature} and
* the {@link module:table/tableproperties/tablepropertiesui~TablePropertiesUI table properties UI feature}.
*/
export default class TableProperties extends Plugin {
/**
* @inheritDoc
*/
static get pluginName(): "TableProperties";
/**
* @inheritDoc
*/
static get requires(): readonly [typeof TablePropertiesEditing, typeof TablePropertiesUI];
}

View File

@ -0,0 +1,32 @@
/**
* @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
*/
/**
* @module table/tableproperties/commands/tablealignmentcommand
*/
import type { Editor } from 'ckeditor5/src/core.js';
import TablePropertyCommand from '@ckeditor/ckeditor5-table/src/tableproperties/commands/tablepropertycommand.js';
/**
* The table alignment command.
*
* The command is registered by the {@link module:table/tableproperties/tablepropertiesediting~TablePropertiesEditing} as
* the `'tableAlignment'` editor command.
*
* To change the alignment of the selected table, execute the command:
*
* ```ts
* editor.execute( 'tableAlignment', {
* value: 'right'
* } );
* ```
*/
export default class TableAlignmentCommand extends TablePropertyCommand {
/**
* Creates a new `TableAlignmentCommand` instance.
*
* @param editor An editor in which this command will be used.
* @param defaultValue The default value for the "alignment" attribute.
*/
constructor(editor: Editor, defaultValue: string);
}

View File

@ -0,0 +1,32 @@
/**
* @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
*/
/**
* @module table/tableproperties/commands/tablebackgroundcolorcommand
*/
import type { Editor } from 'ckeditor5/src/core.js';
import TablePropertyCommand from '@ckeditor/ckeditor5-table/src/tableproperties/commands/tablepropertycommand.js';
/**
* The table background color command.
*
* The command is registered by the {@link module:table/tableproperties/tablepropertiesediting~TablePropertiesEditing} as
* the `'tableBackgroundColor'` editor command.
*
* To change the background color of the selected table, execute the command:
*
* ```ts
* editor.execute( 'tableBackgroundColor', {
* value: '#f00'
* } );
* ```
*/
export default class TableBackgroundColorCommand extends TablePropertyCommand {
/**
* Creates a new `TableBackgroundColorCommand` instance.
*
* @param editor An editor in which this command will be used.
* @param defaultValue The default value of the attribute.
*/
constructor(editor: Editor, defaultValue: string);
}

View File

@ -0,0 +1,37 @@
/**
* @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
*/
/**
* @module table/tableproperties/commands/tablebordercolorcommand
*/
import type { Element } from 'ckeditor5/src/engine.js';
import type { Editor } from 'ckeditor5/src/core.js';
import TablePropertyCommand from '@ckeditor/ckeditor5-table/src/tableproperties/commands/tablepropertycommand.js';
/**
* The table border color command.
*
* The command is registered by the {@link module:table/tableproperties/tablepropertiesediting~TablePropertiesEditing} as
* the `'tableBorderColor'` editor command.
*
* To change the border color of the selected table, execute the command:
*
* ```ts
* editor.execute( 'tableBorderColor', {
* value: '#f00'
* } );
* ```
*/
export default class TableBorderColorCommand extends TablePropertyCommand {
/**
* Creates a new `TableBorderColorCommand` instance.
*
* @param editor An editor in which this command will be used.
* @param defaultValue The default value of the attribute.
*/
constructor(editor: Editor, defaultValue: string);
/**
* @inheritDoc
*/
protected _getValue(table: Element): unknown;
}

View File

@ -0,0 +1,37 @@
/**
* @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
*/
/**
* @module table/tableproperties/commands/tableborderstylecommand
*/
import type { Element } from 'ckeditor5/src/engine.js';
import TablePropertyCommand from '@ckeditor/ckeditor5-table/src/tableproperties/commands/tablepropertycommand.js';
import type { Editor } from 'ckeditor5/src/core.js';
/**
* The table style border command.
*
* The command is registered by the {@link module:table/tableproperties/tablepropertiesediting~TablePropertiesEditing} as
* the `'tableBorderStyle'` editor command.
*
* To change the border style of the selected table, execute the command:
*
* ```ts
* editor.execute( 'tableBorderStyle', {
* value: 'dashed'
* } );
* ```
*/
export default class TableBorderStyleCommand extends TablePropertyCommand {
/**
* Creates a new `TableBorderStyleCommand` instance.
*
* @param editor An editor in which this command will be used.
* @param defaultValue The default value of the attribute.
*/
constructor(editor: Editor, defaultValue: string);
/**
* @inheritDoc
*/
protected _getValue(table: Element): unknown;
}

View File

@ -0,0 +1,51 @@
/**
* @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
*/
/**
* @module table/tableproperties/commands/tableborderwidthcommand
*/
import type { Element } from 'ckeditor5/src/engine.js';
import type { Editor } from 'ckeditor5/src/core.js';
import TablePropertyCommand from '@ckeditor/ckeditor5-table/src/tableproperties/commands/tablepropertycommand.js';
/**
* The table width border command.
*
* The command is registered by the {@link module:table/tableproperties/tablepropertiesediting~TablePropertiesEditing} as
* the `'tableBorderWidth'` editor command.
*
* To change the border width of the selected table, execute the command:
*
* ```ts
* editor.execute( 'tableBorderWidth', {
* value: '5px'
* } );
* ```
*
* **Note**: This command adds the default `'px'` unit to numeric values. Executing:
*
* ```ts
* editor.execute( 'tableBorderWidth', {
* value: '5'
* } );
* ```
*
* will set the `borderWidth` attribute to `'5px'` in the model.
*/
export default class TableBorderWidthCommand extends TablePropertyCommand {
/**
* Creates a new `TableBorderWidthCommand` instance.
*
* @param editor An editor in which this command will be used.
* @param defaultValue The default value of the attribute.
*/
constructor(editor: Editor, defaultValue: string);
/**
* @inheritDoc
*/
protected _getValue(table: Element): string | undefined;
/**
* @inheritDoc
*/
protected _getValueToSet(value: string | number | undefined): unknown;
}

View File

@ -0,0 +1,46 @@
/**
* @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
*/
/**
* @module table/tableproperties/commands/tableheightcommand
*/
import TablePropertyCommand from '@ckeditor/ckeditor5-table/src/tableproperties/commands/tablepropertycommand.js';
import type { Editor } from 'ckeditor5/src/core.js';
/**
* The table height command.
*
* The command is registered by the {@link module:table/tableproperties/tablepropertiesediting~TablePropertiesEditing} as
* the `'tableHeight'` editor command.
*
* To change the height of the selected table, execute the command:
*
* ```ts
* editor.execute( 'tableHeight', {
* value: '500px'
* } );
* ```
*
* **Note**: This command adds the default `'px'` unit to numeric values. Executing:
*
* ```ts
* editor.execute( 'tableHeight', {
* value: '50'
* } );
* ```
*
* will set the `height` attribute to `'50px'` in the model.
*/
export default class TableHeightCommand extends TablePropertyCommand {
/**
* Creates a new `TableHeightCommand` instance.
*
* @param editor An editor in which this command will be used.
* @param defaultValue The default value of the attribute.
*/
constructor(editor: Editor, defaultValue: string);
/**
* @inheritDoc
*/
protected _getValueToSet(value: string | number | undefined): unknown;
}

View File

@ -0,0 +1,46 @@
/**
* @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
*/
/**
* @module table/tableproperties/commands/tablewidthcommand
*/
import TablePropertyCommand from '@ckeditor/ckeditor5-table/src/tableproperties/commands/tablepropertycommand.js';
import type { Editor } from 'ckeditor5/src/core.js';
/**
* The table width command.
*
* The command is registered by the {@link module:table/tableproperties/tablepropertiesediting~TablePropertiesEditing} as
* the `'tableWidth'` editor command.
*
* To change the width of the selected table, execute the command:
*
* ```ts
* editor.execute( 'tableWidth', {
* value: '400px'
* } );
* ```
*
* **Note**: This command adds the default `'px'` unit to numeric values. Executing:
*
* ```ts
* editor.execute( 'tableWidth', {
* value: '50'
* } );
* ```
*
* will set the `width` attribute to `'50px'` in the model.
*/
export default class TableWidthCommand extends TablePropertyCommand {
/**
* Creates a new `TableWidthCommand` instance.
*
* @param editor An editor in which this command will be used.
* @param defaultValue The default value of the attribute.
*/
constructor(editor: Editor, defaultValue: string);
/**
* @inheritDoc
*/
_getValueToSet(value: string | number | undefined): unknown;
}

View File

@ -0,0 +1,40 @@
/**
* @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
*/
/**
* @module table/tableproperties/tablepropertiesediting
*/
import { Plugin } from 'ckeditor5/src/core.js';
import TableEditing from '@ckeditor/ckeditor5-table/src/tableediting.js';
/**
* The table properties editing feature.
*
* Introduces table's model attributes and their conversion:
*
* - border: `tableBorderStyle`, `tableBorderColor` and `tableBorderWidth`
* - background color: `tableBackgroundColor`
* - horizontal alignment: `tableAlignment`
* - width & height: `tableWidth` & `tableHeight`
*
* It also registers commands used to manipulate the above attributes:
*
* - border: `'tableBorderStyle'`, `'tableBorderColor'` and `'tableBorderWidth'` commands
* - background color: `'tableBackgroundColor'`
* - horizontal alignment: `'tableAlignment'`
* - width & height: `'tableWidth'` & `'tableHeight'`
*/
export default class TablePropertiesEditing extends Plugin {
/**
* @inheritDoc
*/
static get pluginName(): "TablePropertiesEditing";
/**
* @inheritDoc
*/
static get requires(): readonly [typeof TableEditing];
/**
* @inheritDoc
*/
init(): void;
}

View File

@ -0,0 +1,114 @@
/**
* @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
*/
/**
* @module table/tableproperties/tablepropertiesui
*/
import { type Editor, Plugin } from 'ckeditor5/src/core.js';
import { ContextualBalloon } from 'ckeditor5/src/ui.js';
import TablePropertiesView from '@ckeditor/ckeditor5-table/src/tableproperties/ui/tablepropertiesview.js';
/**
* The table properties UI plugin. It introduces the `'tableProperties'` button
* that opens a form allowing to specify visual styling of an entire table.
*
* It uses the {@link module:ui/panel/balloon/contextualballoon~ContextualBalloon contextual balloon plugin}.
*/
export default class TablePropertiesUI extends Plugin {
/**
* The default table properties.
*/
private _defaultTableProperties;
/**
* The contextual balloon plugin instance.
*/
private _balloon;
/**
* The properties form view displayed inside the balloon.
*/
view: TablePropertiesView | null;
/**
* The batch used to undo all changes made by the form (which are live, as the user types)
* when "Cancel" was pressed. Each time the view is shown, a new batch is created.
*/
private _undoStepBatch?;
/**
* Flag used to indicate whether view is ready to execute update commands
* (it finished loading initial data).
*/
private _isReady?;
/**
* @inheritDoc
*/
static get requires(): readonly [typeof ContextualBalloon];
/**
* @inheritDoc
*/
static get pluginName(): "TablePropertiesUI";
/**
* @inheritDoc
*/
constructor(editor: Editor);
/**
* @inheritDoc
*/
init(): void;
/**
* @inheritDoc
*/
destroy(): void;
/**
* Creates the {@link module:table/tableproperties/ui/tablepropertiesview~TablePropertiesView} instance.
*
* @returns The table properties form view instance.
*/
private _createPropertiesView;
/**
* In this method the "editor data -> UI" binding is happening.
*
* When executed, this method obtains selected table property values from various table commands
* and passes them to the {@link #view}.
*
* This way, the UI stays uptodate with the editor data.
*/
private _fillViewFormFromCommandValues;
/**
* Shows the {@link #view} in the {@link #_balloon}.
*
* **Note**: Each time a view is shown, the new {@link #_undoStepBatch} is created that contains
* all changes made to the document when the view is visible, allowing a single undo step
* for all of them.
*/
protected _showView(): void;
/**
* Removes the {@link #view} from the {@link #_balloon}.
*/
protected _hideView(): void;
/**
* Repositions the {@link #_balloon} or hides the {@link #view} if a table is no longer selected.
*/
protected _updateView(): void;
/**
* Returns `true` when the {@link #view} is the visible in the {@link #_balloon}.
*/
private get _isViewVisible();
/**
* Returns `true` when the {@link #view} is in the {@link #_balloon}.
*/
private get _isViewInBalloon();
/**
* Creates a callback that when executed upon {@link #view view's} property change
* executes a related editor command with the new property value.
*
* If new value will be set to the default value, the command will not be executed.
*
* @param commandName The command that will be executed.
*/
private _getPropertyChangeCallback;
/**
* Creates a callback that when executed upon {@link #view view's} property change:
* * executes a related editor command with the new property value if the value is valid,
* * or sets the error text next to the invalid field, if the value did not pass the validation.
*/
private _getValidatedPropertyChangeCallback;
}

View File

@ -0,0 +1,207 @@
/**
* @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
*/
/**
* @module table/tableproperties/ui/tablepropertiesview
*/
import { ButtonView, FocusCycler, LabeledFieldView, ToolbarView, View, ViewCollection, type DropdownView, type InputTextView, type NormalizedColorOption, type ColorPickerConfig, type FocusableView } from 'ckeditor5/src/ui.js';
import { FocusTracker, KeystrokeHandler, type Locale } from 'ckeditor5/src/utils.js';
import '@ckeditor/ckeditor5-table/theme/form.css';
import '@ckeditor/ckeditor5-table/theme/tableform.css';
import '@ckeditor/ckeditor5-table/theme/tableproperties.css';
import type ColorInputView from '@ckeditor/ckeditor5-table/src/ui/colorinputview';
import type { TablePropertiesOptions } from '@ckeditor/ckeditor5-table/src/tableconfig';
/**
* Additional configuration of the view.
*/
export interface TablePropertiesViewOptions {
/**
* A configuration of the border color palette used by the
* {@link module:table/tableproperties/ui/tablepropertiesview~TablePropertiesView#borderColorInput}.
*/
borderColors: Array<NormalizedColorOption>;
/**
* A configuration of the background color palette used by the
* {@link module:table/tableproperties/ui/tablepropertiesview~TablePropertiesView#backgroundInput}.
*/
backgroundColors: Array<NormalizedColorOption>;
/**
* The default table properties.
*/
defaultTableProperties: TablePropertiesOptions;
/**
* The default color picker config.
*/
colorPickerConfig: false | ColorPickerConfig;
}
/**
* The class representing a table properties form, allowing users to customize
* certain style aspects of a table, for instance, border, background color, alignment, etc..
*/
export default class TablePropertiesView extends View {
/**
* The value of the border style.
*
* @observable
* @default ''
*/
borderStyle: string;
/**
* The value of the border width style.
*
* @observable
* @default ''
*/
borderWidth: string;
/**
* The value of the border color style.
*
* @observable
* @default ''
*/
borderColor: string;
/**
* The value of the background color style.
*
* @observable
* @default ''
*/
backgroundColor: string;
/**
* The value of the table width style.
*
* @observable
* @default ''
*/
width: string;
/**
* The value of the table height style.
*
* @observable
* @default ''
*/
height: string;
/**
* The value of the table alignment style.
*
* @observable
* @default ''
*/
alignment: string;
/**
* Options passed to the view. See {@link #constructor} to learn more.
*/
readonly options: TablePropertiesViewOptions;
/**
* Tracks information about the DOM focus in the form.
*/
readonly focusTracker: FocusTracker;
/**
* An instance of the {@link module:utils/keystrokehandler~KeystrokeHandler}.
*/
readonly keystrokes: KeystrokeHandler;
/**
* A collection of child views in the form.
*/
readonly children: ViewCollection;
/**
* A dropdown that allows selecting the style of the table border.
*/
readonly borderStyleDropdown: LabeledFieldView<DropdownView>;
/**
* An input that allows specifying the width of the table border.
*/
readonly borderWidthInput: LabeledFieldView<InputTextView>;
/**
* An input that allows specifying the color of the table border.
*/
readonly borderColorInput: LabeledFieldView<ColorInputView>;
/**
* An input that allows specifying the table background color.
*/
readonly backgroundInput: LabeledFieldView<ColorInputView>;
/**
* An input that allows specifying the table width.
*/
readonly widthInput: LabeledFieldView<InputTextView>;
/**
* An input that allows specifying the table height.
*/
readonly heightInput: LabeledFieldView<InputTextView>;
/**
* A toolbar with buttons that allow changing the alignment of an entire table.
*/
readonly alignmentToolbar: ToolbarView;
/**
* The "Save" button view.
*/
saveButtonView: ButtonView;
/**
* The "Cancel" button view.
*/
cancelButtonView: ButtonView;
/**
* A collection of views that can be focused in the form.
*/
protected readonly _focusables: ViewCollection<FocusableView>;
/**
* Helps cycling over {@link #_focusables} in the form.
*/
protected readonly _focusCycler: FocusCycler;
/**
* @param locale The {@link module:core/editor/editor~Editor#locale} instance.
* @param options Additional configuration of the view.
*/
constructor(locale: Locale, options: TablePropertiesViewOptions);
/**
* @inheritDoc
*/
render(): void;
/**
* @inheritDoc
*/
destroy(): void;
/**
* Focuses the fist focusable field in the form.
*/
focus(): void;
/**
* Creates the following form fields:
*
* * {@link #borderStyleDropdown},
* * {@link #borderWidthInput},
* * {@link #borderColorInput}.
*/
private _createBorderFields;
/**
* Creates the following form fields:
*
* * {@link #backgroundInput}.
*/
private _createBackgroundFields;
/**
* Creates the following form fields:
*
* * {@link #widthInput},
* * {@link #heightInput}.
*/
private _createDimensionFields;
/**
* Creates the following form fields:
*
* * {@link #alignmentToolbar}.
*/
private _createAlignmentFields;
/**
* Creates the following form controls:
*
* * {@link #saveButtonView},
* * {@link #cancelButtonView}.
*/
private _createActionButtons;
/**
* Provides localized labels for {@link #alignmentToolbar} buttons.
*/
private get _alignmentLabels();
}

View File

@ -0,0 +1,107 @@
/**
* @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
*/
/**
* @module table/tableselection
*/
import { Plugin } from 'ckeditor5/src/core.js';
import type { Element, DocumentFragment } from 'ckeditor5/src/engine.js';
import TableUtils from '@ckeditor/ckeditor5-table/src/tableutils.js';
import '@ckeditor/ckeditor5-table/theme/tableselection.css';
/**
* This plugin enables the advanced table cells, rows and columns selection.
* It is loaded automatically by the {@link module:table/table~Table} plugin.
*/
export default class TableSelection extends Plugin {
/**
* @inheritDoc
*/
static get pluginName(): "TableSelection";
/**
* @inheritDoc
*/
static get requires(): readonly [typeof TableUtils, typeof TableUtils];
/**
* @inheritDoc
*/
init(): void;
/**
* Returns the currently selected table cells or `null` if it is not a table cells selection.
*/
getSelectedTableCells(): Array<Element> | null;
/**
* Returns the selected table fragment as a document fragment.
*/
getSelectionAsFragment(): DocumentFragment | null;
/**
* Sets the model selection based on given anchor and target cells (can be the same cell).
* Takes care of setting the backward flag.
*
* ```ts
* const modelRoot = editor.model.document.getRoot();
* const firstCell = modelRoot.getNodeByPath( [ 0, 0, 0 ] );
* const lastCell = modelRoot.getNodeByPath( [ 0, 0, 1 ] );
*
* const tableSelection = editor.plugins.get( 'TableSelection' );
* tableSelection.setCellSelection( firstCell, lastCell );
* ```
*/
setCellSelection(anchorCell: Element, targetCell: Element): void;
/**
* Returns the focus cell from the current selection.
*/
getFocusCell(): Element | null;
/**
* Returns the anchor cell from the current selection.
*/
getAnchorCell(): Element | null;
/**
* Defines a selection converter which marks the selected cells with a specific class.
*
* The real DOM selection is put in the last cell. Since the order of ranges is dependent on whether the
* selection is backward or not, the last cell will usually be close to the "focus" end of the selection
* (a selection has anchor and focus).
*
* The real DOM selection is then hidden with CSS.
*/
private _defineSelectionConverter;
/**
* Creates a listener that reacts to changes in {@link #isEnabled} and, if the plugin was disabled,
* it collapses the multi-cell selection to a regular selection placed inside a table cell.
*
* This listener helps features that disable the table selection plugin bring the selection
* to a clear state they can work with (for instance, because they don't support multiple cell selection).
*/
private _enablePluginDisabling;
/**
* Overrides the default `model.deleteContent()` behavior over a selected table fragment.
*
* @param args Delete content method arguments.
*/
private _handleDeleteContent;
/**
* This handler makes it possible to remove the content of all selected cells by starting to type.
* If you take a look at {@link #_defineSelectionConverter} you will find out that despite the multi-cell selection being set
* in the model, the view selection is collapsed in the last cell (because most browsers are unable to render multi-cell selections;
* yes, it's a hack).
*
* When multiple cells are selected in the model and the user starts to type, the
* {@link module:engine/view/document~Document#event:insertText} event carries information provided by the
* beforeinput DOM event, that in turn only knows about this collapsed DOM selection in the last cell.
*
* As a result, the selected cells have no chance to be cleaned up. To fix this, this listener intercepts
* the event and injects the custom view selection in the data that translates correctly to the actual state
* of the multi-cell selection in the model.
*
* @param data Insert text event data.
*/
private _handleInsertTextEvent;
/**
* Returns an array of table cells that should be selected based on the
* given anchor cell and target (focus) cell.
*
* The cells are returned in a reverse direction if the selection is backward.
*/
private _getCellsToSelect;
}

View File

@ -0,0 +1,448 @@
/**
* @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 { Plugin } from 'ckeditor5/src/core.js';
import type { DocumentSelection, Element, Range, Selection, Writer } from 'ckeditor5/src/engine.js';
import TableWalker, { type TableWalkerOptions } from '@ckeditor/ckeditor5-table/src/tablewalker.js';
type IndexesObject = {
first: number;
last: number;
};
/**
* The table utilities plugin.
*/
export default class TableUtils extends Plugin {
/**
* @inheritDoc
*/
static get pluginName(): "TableUtils";
/**
* @inheritDoc
*/
init(): void;
/**
* Returns the table cell location as an object with table row and table column indexes.
*
* For instance, in the table below:
*
* 0 1 2 3
* +---+---+---+---+
* 0 | a | b | c |
* + + +---+
* 1 | | | d |
* +---+---+ +---+
* 2 | e | | f |
* +---+---+---+---+
*
* the method will return:
*
* ```ts
* const cellA = table.getNodeByPath( [ 0, 0 ] );
* editor.plugins.get( 'TableUtils' ).getCellLocation( cellA );
* // will return { row: 0, column: 0 }
*
* const cellD = table.getNodeByPath( [ 1, 0 ] );
* editor.plugins.get( 'TableUtils' ).getCellLocation( cellD );
* // will return { row: 1, column: 3 }
* ```
*
* @returns Returns a `{row, column}` object.
*/
getCellLocation(tableCell: Element): {
row: number;
column: number;
};
/**
* Creates an empty table with a proper structure. The table needs to be inserted into the model,
* for example, by using the {@link module:engine/model/model~Model#insertContent} function.
*
* ```ts
* model.change( ( writer ) => {
* // Create a table of 2 rows and 7 columns:
* const table = tableUtils.createTable( writer, { rows: 2, columns: 7 } );
*
* // Insert a table to the model at the best position taking the current selection:
* model.insertContent( table );
* }
* ```
*
* @param writer The model writer.
* @param options.rows The number of rows to create. Default value is 2.
* @param options.columns The number of columns to create. Default value is 2.
* @param options.headingRows The number of heading rows. Default value is 0.
* @param options.headingColumns The number of heading columns. Default value is 0.
* @returns The created table element.
*/
createTable(writer: Writer, options: {
rows?: number;
columns?: number;
headingRows?: number;
headingColumns?: number;
}): Element;
/**
* Inserts rows into a table.
*
* ```ts
* editor.plugins.get( 'TableUtils' ).insertRows( table, { at: 1, rows: 2 } );
* ```
*
* Assuming the table on the left, the above code will transform it to the table on the right:
*
* row index
* 0 +---+---+---+ `at` = 1, +---+---+---+ 0
* | a | b | c | `rows` = 2, | a | b | c |
* 1 + +---+---+ <-- insert here + +---+---+ 1
* | | d | e | | | | |
* 2 + +---+---+ will give: + +---+---+ 2
* | | f | g | | | | |
* 3 +---+---+---+ + +---+---+ 3
* | | d | e |
* + +---+---+ 4
* + + f | g |
* +---+---+---+ 5
*
* @param table The table model element where the rows will be inserted.
* @param options.at The row index at which the rows will be inserted. Default value is 0.
* @param options.rows The number of rows to insert. Default value is 1.
* @param options.copyStructureFromAbove The flag for copying row structure. Note that
* the row structure will not be copied if this option is not provided.
*/
insertRows(table: Element, options?: {
at?: number;
rows?: number;
copyStructureFromAbove?: boolean;
}): void;
/**
* Inserts columns into a table.
*
* ```ts
* editor.plugins.get( 'TableUtils' ).insertColumns( table, { at: 1, columns: 2 } );
* ```
*
* Assuming the table on the left, the above code will transform it to the table on the right:
*
* 0 1 2 3 0 1 2 3 4 5
* +---+---+---+ +---+---+---+---+---+
* | a | b | | a | b |
* + +---+ + +---+
* | | c | | | c |
* +---+---+---+ will give: +---+---+---+---+---+
* | d | e | f | | d | | | e | f |
* +---+ +---+ +---+---+---+ +---+
* | g | | h | | g | | | | h |
* +---+---+---+ +---+---+---+---+---+
* | i | | i |
* +---+---+---+ +---+---+---+---+---+
* ^---- insert here, `at` = 1, `columns` = 2
*
* @param table The table model element where the columns will be inserted.
* @param options.at The column index at which the columns will be inserted. Default value is 0.
* @param options.columns The number of columns to insert. Default value is 1.
*/
insertColumns(table: Element, options?: {
at?: number;
columns?: number;
}): void;
/**
* Removes rows from the given `table`.
*
* This method re-calculates the table geometry including `rowspan` attribute of table cells overlapping removed rows
* and table headings values.
*
* ```ts
* editor.plugins.get( 'TableUtils' ).removeRows( table, { at: 1, rows: 2 } );
* ```
*
* Executing the above code in the context of the table on the left will transform its structure as presented on the right:
*
* row index
* `at` = 1
* 0 a b c `rows` = 2 a b c 0
*
* 1 d e <-- remove from here d g 1
* will give:
* 2 f h i j 2
*
* 3 g
*
* 4 h i j
*
*
* @param options.at The row index at which the removing rows will start.
* @param options.rows The number of rows to remove. Default value is 1.
*/
removeRows(table: Element, options: {
at: number;
rows?: number;
}): void;
/**
* Removes columns from the given `table`.
*
* This method re-calculates the table geometry including the `colspan` attribute of table cells overlapping removed columns
* and table headings values.
*
* ```ts
* editor.plugins.get( 'TableUtils' ).removeColumns( table, { at: 1, columns: 2 } );
* ```
*
* Executing the above code in the context of the table on the left will transform its structure as presented on the right:
*
* 0 1 2 3 4 0 1 2
*
* a b a b
*
* c c
* will give:
* d e f g h d g h
*
* i j k l i l
*
* m m
*
* ^---- remove from here, `at` = 1, `columns` = 2
*
* @param options.at The row index at which the removing columns will start.
* @param options.columns The number of columns to remove.
*/
removeColumns(table: Element, options: {
at: number;
columns?: number;
}): void;
/**
* Divides a table cell vertically into several ones.
*
* The cell will be visually split into more cells by updating colspans of other cells in a column
* and inserting cells (columns) after that cell.
*
* In the table below, if cell "a" is split into 3 cells:
*
* +---+---+---+
* | a | b | c |
* +---+---+---+
* | d | e | f |
* +---+---+---+
*
* it will result in the table below:
*
* +---+---+---+---+---+
* | a | | | b | c |
* +---+---+---+---+---+
* | d | e | f |
* +---+---+---+---+---+
*
* So cell "d" will get its `colspan` updated to `3` and 2 cells will be added (2 columns will be created).
*
* Splitting a cell that already has a `colspan` attribute set will distribute the cell `colspan` evenly and the remainder
* will be left to the original cell:
*
* +---+---+---+
* | a |
* +---+---+---+
* | b | c | d |
* +---+---+---+
*
* Splitting cell "a" with `colspan=3` into 2 cells will create 1 cell with a `colspan=a` and cell "a" that will have `colspan=2`:
*
* +---+---+---+
* | a | |
* +---+---+---+
* | b | c | d |
* +---+---+---+
*/
splitCellVertically(tableCell: Element, numberOfCells?: number): void;
/**
* Divides a table cell horizontally into several ones.
*
* The cell will be visually split into more cells by updating rowspans of other cells in the row and inserting rows with a single cell
* below.
*
* If in the table below cell "b" is split into 3 cells:
*
* +---+---+---+
* | a | b | c |
* +---+---+---+
* | d | e | f |
* +---+---+---+
*
* It will result in the table below:
*
* +---+---+---+
* | a | b | c |
* + +---+ +
* | | | |
* + +---+ +
* | | | |
* +---+---+---+
* | d | e | f |
* +---+---+---+
*
* So cells "a" and "b" will get their `rowspan` updated to `3` and 2 rows with a single cell will be added.
*
* Splitting a cell that already has a `rowspan` attribute set will distribute the cell `rowspan` evenly and the remainder
* will be left to the original cell:
*
* +---+---+---+
* | a | b | c |
* + +---+---+
* | | d | e |
* + +---+---+
* | | f | g |
* + +---+---+
* | | h | i |
* +---+---+---+
*
* Splitting cell "a" with `rowspan=4` into 3 cells will create 2 cells with a `rowspan=1` and cell "a" will have `rowspan=2`:
*
* +---+---+---+
* | a | b | c |
* + +---+---+
* | | d | e |
* +---+---+---+
* | | f | g |
* +---+---+---+
* | | h | i |
* +---+---+---+
*/
splitCellHorizontally(tableCell: Element, numberOfCells?: number): void;
/**
* Returns the number of columns for a given table.
*
* ```ts
* editor.plugins.get( 'TableUtils' ).getColumns( table );
* ```
*
* @param table The table to analyze.
*/
getColumns(table: Element): number;
/**
* Returns the number of rows for a given table. Any other element present in the table model is omitted.
*
* ```ts
* editor.plugins.get( 'TableUtils' ).getRows( table );
* ```
*
* @param table The table to analyze.
*/
getRows(table: Element): number;
/**
* Creates an instance of the table walker.
*
* The table walker iterates internally by traversing the table from row index = 0 and column index = 0.
* It walks row by row and column by column in order to output values defined in the options.
* By default it will output only the locations that are occupied by a cell. To include also spanned rows and columns,
* pass the `includeAllSlots` option.
*
* @internal
* @param table A table over which the walker iterates.
* @param options An object with configuration.
*/
createTableWalker(table: Element, options?: TableWalkerOptions): TableWalker;
/**
* Returns all model table cells that are fully selected (from the outside)
* within the provided model selection's ranges.
*
* To obtain the cells selected from the inside, use
* {@link #getTableCellsContainingSelection}.
*/
getSelectedTableCells(selection: Selection | DocumentSelection): Array<Element>;
/**
* Returns all model table cells that the provided model selection's ranges
* {@link module:engine/model/range~Range#start} inside.
*
* To obtain the cells selected from the outside, use
* {@link #getSelectedTableCells}.
*/
getTableCellsContainingSelection(selection: Selection | DocumentSelection): Array<Element>;
/**
* Returns all model table cells that are either completely selected
* by selection ranges or host selection range
* {@link module:engine/model/range~Range#start start positions} inside them.
*
* Combines {@link #getTableCellsContainingSelection} and
* {@link #getSelectedTableCells}.
*/
getSelectionAffectedTableCells(selection: Selection | DocumentSelection): Array<Element>;
/**
* Returns an object with the `first` and `last` row index contained in the given `tableCells`.
*
* ```ts
* const selectedTableCells = getSelectedTableCells( editor.model.document.selection );
*
* const { first, last } = getRowIndexes( selectedTableCells );
*
* console.log( `Selected rows: ${ first } to ${ last }` );
* ```
*
* @returns Returns an object with the `first` and `last` table row indexes.
*/
getRowIndexes(tableCells: Array<Element>): IndexesObject;
/**
* Returns an object with the `first` and `last` column index contained in the given `tableCells`.
*
* ```ts
* const selectedTableCells = getSelectedTableCells( editor.model.document.selection );
*
* const { first, last } = getColumnIndexes( selectedTableCells );
*
* console.log( `Selected columns: ${ first } to ${ last }` );
* ```
*
* @returns Returns an object with the `first` and `last` table column indexes.
*/
getColumnIndexes(tableCells: Array<Element>): IndexesObject;
/**
* Checks if the selection contains cells that do not exceed rectangular selection.
*
* In a table below:
*
*
* a b c d
*
* e f
*
* g h
*
*
* Valid selections are these which create a solid rectangle (without gaps), such as:
* - a, b (two horizontal cells)
* - c, f (two vertical cells)
* - a, b, e (cell "e" spans over four cells)
* - c, d, f (cell d spans over a cell in the row below)
*
* While an invalid selection would be:
* - a, c (the unselected cell "b" creates a gap)
* - f, g, h (cell "d" spans over a cell from the row of "f" cell - thus creates a gap)
*/
isSelectionRectangular(selectedTableCells: Array<Element>): boolean;
/**
* Returns array of sorted ranges.
*/
sortRanges(ranges: Iterable<Range>): Array<Range>;
/**
* Helper method to get an object with `first` and `last` indexes from an unsorted array of indexes.
*/
private _getFirstLastIndexesObject;
/**
* Checks if the selection does not mix a header (column or row) with other cells.
*
* For instance, in the table below valid selections consist of cells with the same letter only.
* So, a-a (same heading row and column) or d-d (body cells) are valid while c-d or a-b are not.
*
* header columns
*
*
* a a b b header row
*
* c c d d
*
* c c d d
*
*/
private _areCellInTheSameTableSection;
/**
* Unified check if table rows/columns indexes are in the same heading/body section.
*/
private _areIndexesInSameSection;
}
export {};

View File

@ -0,0 +1,140 @@
/**
* @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
*/
/**
* @module table/ui/colorinputview
*/
import { View, InputTextView, FocusCycler, ViewCollection, type ColorDefinition, type DropdownView, type ColorPickerConfig, type FocusableView } from 'ckeditor5/src/ui.js';
import { FocusTracker, KeystrokeHandler, type Locale } from 'ckeditor5/src/utils.js';
import '@ckeditor/ckeditor5-table/theme/colorinput.css';
export type ColorInputViewOptions = {
colorDefinitions: Array<ColorDefinition>;
columns: number;
defaultColorValue?: string;
colorPickerConfig: false | ColorPickerConfig;
};
/**
* The color input view class. It allows the user to type in a color (hex, rgb, etc.)
* or choose it from the configurable color palette with a preview.
*
* @internal
*/
export default class ColorInputView extends View implements FocusableView {
/**
* The value of the input.
*
* @observable
* @default ''
*/
value: string;
/**
* Controls whether the input view is in read-only mode.
*
* @observable
* @default false
*/
isReadOnly: boolean;
/**
* An observable flag set to `true` when the input is focused by the user.
* `false` otherwise.
*
* @observable
* @default false
*/
readonly isFocused: boolean;
/**
* An observable flag set to `true` when the input contains no text.
*
* @observable
* @default true
*/
readonly isEmpty: boolean;
/**
* @observable
*/
hasError: boolean;
/**
* A cached reference to the options passed to the constructor.
*/
options: ColorInputViewOptions;
/**
* Tracks information about the DOM focus in the view.
*/
readonly focusTracker: FocusTracker;
/**
* Helps cycling over focusable children in the input view.
*/
readonly focusCycler: FocusCycler;
/**
* A collection of views that can be focused in the view.
*/
protected readonly _focusables: ViewCollection<FocusableView>;
/**
* An instance of the dropdown allowing to select a color from a grid.
*/
dropdownView: DropdownView;
/**
* An instance of the input allowing the user to type a color value.
*/
inputView: InputTextView;
/**
* An instance of the {@link module:utils/keystrokehandler~KeystrokeHandler}.
*/
readonly keystrokes: KeystrokeHandler;
/**
* The flag that indicates whether the user is still typing.
* If set to true, it means that the text input field ({@link #inputView}) still has the focus.
* So, we should interrupt the user by replacing the input's value.
*/
protected _stillTyping: boolean;
/**
* Creates an instance of the color input view.
*
* @param locale The locale instance.
* @param options The input options.
* @param options.colorDefinitions The colors to be displayed in the palette inside the input's dropdown.
* @param options.columns The number of columns in which the colors will be displayed.
* @param options.defaultColorValue If specified, the color input view will replace the "Remove color" button with
* the "Restore default" button. Instead of clearing the input field, the default color value will be set.
*/
constructor(locale: Locale, options: ColorInputViewOptions);
/**
* @inheritDoc
*/
render(): void;
/**
* Focuses the view.
*/
focus(direction: 1 | -1): void;
/**
* @inheritDoc
*/
destroy(): void;
/**
* Creates and configures the {@link #dropdownView}.
*/
private _createDropdownView;
/**
* Creates and configures an instance of {@link module:ui/inputtext/inputtextview~InputTextView}.
*
* @returns A configured instance to be set as {@link #inputView}.
*/
private _createInputTextView;
/**
* Creates and configures the panel with "color grid" and "color picker" inside the {@link #dropdownView}.
*/
private _createColorSelector;
/**
* Sets {@link #inputView}'s value property to the color value or color label,
* if there is one and the user is not typing.
*
* Handles cases like:
*
* * Someone picks the color in the grid.
* * The color is set from the plugin level.
*
* @param inputValue Color value to be set.
*/
private _setInputValue;
}

View File

@ -0,0 +1,27 @@
/**
* @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 { Delete, DeleteCommand, Input, InsertTextCommand, TextTransformation, TwoStepCaretMovement, Typing, TypingConfig } from '@ckeditor/ckeditor5-typing';
declare module '@ckeditor/ckeditor5-core' {
interface EditorConfig {
/**
* The configuration of the typing features. Used by the features from the `@ckeditor/ckeditor5-typing` package.
*
* Read more in {@link module:typing/typingconfig~TypingConfig}.
*/
typing?: TypingConfig;
}
interface CommandsMap {
deleteForward: DeleteCommand;
delete: DeleteCommand;
insertText: InsertTextCommand;
}
interface PluginsMap {
[Delete.pluginName]: Delete;
[Input.pluginName]: Input;
[TextTransformation.pluginName]: TextTransformation;
[TwoStepCaretMovement.pluginName]: TwoStepCaretMovement;
[Typing.pluginName]: Typing;
}
}

View File

@ -0,0 +1,83 @@
/**
* @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
*/
/**
* @module typing/deletecommand
*/
import { Command, type Editor } from '@ckeditor/ckeditor5-core';
import type { DocumentSelection, Selection } from '@ckeditor/ckeditor5-engine';
import ChangeBuffer from '@ckeditor/ckeditor5-typing/src/utils/changebuffer.js';
/**
* The delete command. Used by the {@link module:typing/delete~Delete delete feature} to handle the <kbd>Delete</kbd> and
* <kbd>Backspace</kbd> keys.
*/
export default class DeleteCommand extends Command {
/**
* The directionality of the delete describing in what direction it should
* consume the content when the selection is collapsed.
*/
readonly direction: 'forward' | 'backward';
/**
* Delete's change buffer used to group subsequent changes into batches.
*/
private readonly _buffer;
/**
* Creates an instance of the command.
*
* @param direction The directionality of the delete describing in what direction it
* should consume the content when the selection is collapsed.
*/
constructor(editor: Editor, direction: 'forward' | 'backward');
/**
* The current change buffer.
*/
get buffer(): ChangeBuffer;
/**
* Executes the delete command. Depending on whether the selection is collapsed or not, deletes its content
* or a piece of content in the {@link #direction defined direction}.
*
* @fires execute
* @param options The command options.
* @param options.unit See {@link module:engine/model/utils/modifyselection~modifySelection}'s options.
* @param options.sequence A number describing which subsequent delete event it is without the key being released.
* See the {@link module:engine/view/document~Document#event:delete} event data.
* @param options.selection Selection to remove. If not set, current model selection will be used.
*/
execute(options?: {
unit?: 'character' | 'codePoint' | 'word';
sequence?: number;
selection?: Selection | DocumentSelection;
}): void;
/**
* If the user keeps <kbd>Backspace</kbd> or <kbd>Delete</kbd> key pressed, the content of the current
* editable will be cleared. However, this will not yet lead to resetting the remaining block to a paragraph
* (which happens e.g. when the user does <kbd>Ctrl</kbd> + <kbd>A</kbd>, <kbd>Backspace</kbd>).
*
* But, if the user pressed the key in an empty editable for the first time,
* we want to replace the entire content with a paragraph if:
*
* * the current limit element is empty,
* * the paragraph is allowed in the limit element,
* * the limit doesn't already have a paragraph inside.
*
* See https://github.com/ckeditor/ckeditor5-typing/issues/61.
*
* @param sequence A number describing which subsequent delete event it is without the key being released.
*/
private _shouldEntireContentBeReplacedWithParagraph;
/**
* The entire content is replaced with the paragraph. Selection is moved inside the paragraph.
*
* @param writer The model writer.
*/
private _replaceEntireContentWithParagraph;
/**
* Checks if the selection is inside an empty element that is the first child of the limit element
* and should be replaced with a paragraph.
*
* @param selection The selection.
* @param sequence A number describing which subsequent delete event it is without the key being released.
*/
private _shouldReplaceFirstBlockWithParagraph;
}

View File

@ -0,0 +1,24 @@
/**
* @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
*/
/**
* @module typing
*/
export { default as Typing } from '@ckeditor/ckeditor5-typing/src/typing.js';
export { default as Input } from '@ckeditor/ckeditor5-typing/src/input.js';
export { default as Delete } from '@ckeditor/ckeditor5-typing/src/delete.js';
export { default as TextWatcher } from '@ckeditor/ckeditor5-typing/src/textwatcher.js';
export { default as TwoStepCaretMovement } from '@ckeditor/ckeditor5-typing/src/twostepcaretmovement.js';
export { default as TextTransformation } from '@ckeditor/ckeditor5-typing/src/texttransformation.js';
export { default as inlineHighlight } from '@ckeditor/ckeditor5-typing/src/utils/inlinehighlight.js';
export { default as findAttributeRange, findAttributeRangeBound } from '@ckeditor/ckeditor5-typing/src/utils/findattributerange.js';
export { default as getLastTextLine, type LastTextLineData } from '@ckeditor/ckeditor5-typing/src/utils/getlasttextline.js';
export { default as InsertTextCommand, type InsertTextCommandExecuteEvent } from '@ckeditor/ckeditor5-typing/src/inserttextcommand.js';
export type { default as DeleteCommand } from '@ckeditor/ckeditor5-typing/src/deletecommand.js';
export type { TypingConfig } from '@ckeditor/ckeditor5-typing/src/typingconfig.js';
export type { ViewDocumentDeleteEvent } from '@ckeditor/ckeditor5-typing/src/deleteobserver.js';
export type { ViewDocumentInsertTextEvent, InsertTextEventData } from '@ckeditor/ckeditor5-typing/src/inserttextobserver.js';
export type { TextWatcherMatchedEvent } from '@ckeditor/ckeditor5-typing/src/textwatcher.js';
export type { TextWatcherMatchedDataEvent } from '@ckeditor/ckeditor5-typing/src/textwatcher.js';
import '@ckeditor/ckeditor5-typing/src/augmentation.js';

View File

@ -0,0 +1,76 @@
/**
* @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
*/
/**
* @module typing/inserttextcommand
*/
import { Command, type Editor } from '@ckeditor/ckeditor5-core';
import ChangeBuffer from '@ckeditor/ckeditor5-typing/src/utils/changebuffer.js';
import type { DocumentSelection, Range, Selection } from '@ckeditor/ckeditor5-engine';
/**
* The insert text command. Used by the {@link module:typing/input~Input input feature} to handle typing.
*/
export default class InsertTextCommand extends Command {
/**
* Typing's change buffer used to group subsequent changes into batches.
*/
private readonly _buffer;
/**
* Creates an instance of the command.
*
* @param undoStepSize The maximum number of atomic changes
* which can be contained in one batch in the command buffer.
*/
constructor(editor: Editor, undoStepSize: number);
/**
* The current change buffer.
*/
get buffer(): ChangeBuffer;
/**
* @inheritDoc
*/
destroy(): void;
/**
* Executes the input command. It replaces the content within the given range with the given text.
* Replacing is a two step process, first the content within the range is removed and then the new text is inserted
* at the beginning of the range (which after the removal is a collapsed range).
*
* @fires execute
* @param options The command options.
*/
execute(options?: InsertTextCommandOptions): void;
}
/**
* Interface with parameters for executing InsertTextCommand.
*
* Both `range` and `selection` parameters are used for defining selection but should not be used together.
* If both are defined, only `selection` will be considered.
*/
export interface InsertTextCommandOptions {
/**
* The text to be inserted.
*/
text?: string;
/**
* The selection in which the text is inserted.
* Inserting a text into a selection deletes the current content within selection ranges. If the selection is not specified,
* the current selection in the model will be used instead.
*/
selection?: Selection | DocumentSelection;
/**
* The range in which the text is inserted. Defaults to the first range in the current selection.
*/
range?: Range;
/**
* The range where the selection should be placed after the insertion.
* If not specified, the selection will be placed right after the inserted text.
*/
resultRange?: Range;
}
export interface InsertTextCommandExecuteEvent {
name: 'execute';
args: [
data: [options: InsertTextCommandOptions]
];
}

View File

@ -0,0 +1,23 @@
/**
* @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
*/
/**
* @module typing/typing
*/
import { Plugin } from '@ckeditor/ckeditor5-core';
import Input from '@ckeditor/ckeditor5-typing/src/input.js';
import Delete from '@ckeditor/ckeditor5-typing/src/delete.js';
/**
* The typing feature. It handles typing.
*
* This is a "glue" plugin which loads the {@link module:typing/input~Input} and {@link module:typing/delete~Delete}
* plugins.
*/
export default class Typing extends Plugin {
static get requires(): readonly [typeof Input, typeof Delete];
/**
* @inheritDoc
*/
static get pluginName(): "Typing";
}

View File

@ -0,0 +1,98 @@
/**
* @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
*/
/**
* @module ui/arialiveannouncer
*/
import type { Editor } from '@ckeditor/ckeditor5-core';
import type { Locale } from '@ckeditor/ckeditor5-utils';
import type ViewCollection from '@ckeditor/ckeditor5-ui/src/viewcollection.js';
import View from '@ckeditor/ckeditor5-ui/src/view.js';
import '@ckeditor/ckeditor5-ui/theme/components/arialiveannouncer/arialiveannouncer.css';
/**
* The politeness level of an `aria-live` announcement.
*
* Available keys are:
* * `AriaLiveAnnouncerPoliteness.POLITE`,
* * `AriaLiveAnnouncerPoliteness.ASSERTIVE`
*
* [Learn more](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Live_Regions#Politeness_levels).
*/
export declare const AriaLiveAnnouncerPoliteness: {
readonly POLITE: "polite";
readonly ASSERTIVE: "assertive";
};
/**
* An accessibility helper that manages all ARIA live regions associated with an editor instance. ARIA live regions announce changes
* to the state of the editor features.
*
* These announcements are consumed and propagated by screen readers and give users a better understanding of the current
* state of the editor.
*
* To announce a state change to an editor feature named `'Some feature'`, use the {@link #announce} method:
* ```ts
* editor.ui.ariaLiveAnnouncer.announce( 'Some feature', 'Text of an announcement.' );
* ```
*/
export default class AriaLiveAnnouncer {
/**
* The editor instance.
*/
readonly editor: Editor;
/**
* The view that aggregates all `aria-live` regions.
*/
view?: AriaLiveAnnouncerView;
/**
* @inheritDoc
*/
constructor(editor: Editor);
/**
* Sets an announcement text to an aria region associated with a specific editor feature. The text is then
* announced by a screen reader to the user.
*
* If the aria region of a given name does not exist, it will be created and can be re-used later. The name of the region
* groups announcements originating from a specific editor feature and does not get announced by a screen reader.
*
* Using multiple regions allows for many announcements to be emitted in a short period of time. Changes to ARIA-live announcements
* are captured by a screen reader and read out in the order they were emitted.
*
* The default announcement politeness level is `'polite'`.
*
* ```ts
* // Most screen readers will queue announcements from multiple aria-live regions and read them out in the order they were emitted.
* editor.ui.ariaLiveAnnouncer.announce( 'image', 'Image uploaded.' );
* editor.ui.ariaLiveAnnouncer.announce( 'network', 'Connection lost. Reconnecting.' );
* ```
*/
announce(regionName: string, announcementText: string, politeness?: typeof AriaLiveAnnouncerPoliteness[keyof typeof AriaLiveAnnouncerPoliteness]): void;
}
/**
* The view that aggregates all `aria-live` regions.
*/
export declare class AriaLiveAnnouncerView extends View {
/**
* A collection of all views that represent individual `aria-live` regions.
*/
readonly regionViews: ViewCollection<AriaLiveAnnouncerRegionView>;
constructor(locale: Locale);
}
/**
* The view that represents a single `aria-live` region (e.g. for a specific editor feature) and its text.
*/
export declare class AriaLiveAnnouncerRegionView extends View {
/**
* Current text of the region.
*/
text: string;
/**
* Current politeness level of the region.
*/
politeness: typeof AriaLiveAnnouncerPoliteness[keyof typeof AriaLiveAnnouncerPoliteness];
/**
* A unique name of the region, usually associated with a specific editor feature or system.
*/
regionName: string;
constructor(locale: Locale);
}

View File

@ -0,0 +1,88 @@
/**
* @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 { BalloonToolbar, BlockToolbar, ContextualBalloon, Notification, Dialog, AccessibilityHelp } from '@ckeditor/ckeditor5-ui';
import type { ToolbarConfig } from '@ckeditor/ckeditor5-core';
declare module '@ckeditor/ckeditor5-core' {
interface EditorConfig {
/**
* Contextual toolbar configuration. Used by the {@link module:ui/toolbar/balloon/balloontoolbar~BalloonToolbar}
* feature.
*
* ## Configuring toolbar items
*
* ```ts
* const config = {
* balloonToolbar: [ 'bold', 'italic', 'undo', 'redo' ]
* };
* ```
*
* You can also use `'|'` to create a separator between groups of items:
*
* ```ts
* const config = {
* balloonToolbar: [ 'bold', 'italic', '|', 'undo', 'redo' ]
* };
* ```
*
* Read also about configuring the main editor toolbar in {@link module:core/editor/editorconfig~EditorConfig#toolbar}.
*
* ## Configuring items grouping
*
* You can prevent automatic items grouping by setting the `shouldNotGroupWhenFull` option:
*
* ```ts
* const config = {
* balloonToolbar: {
* items: [ 'bold', 'italic', 'undo', 'redo' ],
* shouldNotGroupWhenFull: true
* },
* };
* ```
*/
balloonToolbar?: ToolbarConfig;
/**
* The block toolbar configuration. Used by the {@link module:ui/toolbar/block/blocktoolbar~BlockToolbar}
* feature.
*
* ```ts
* const config = {
* blockToolbar: [ 'paragraph', 'heading1', 'heading2', 'bulletedList', 'numberedList' ]
* };
* ```
*
* You can also use `'|'` to create a separator between groups of items:
*
* ```ts
* const config = {
* blockToolbar: [ 'paragraph', 'heading1', 'heading2', '|', 'bulletedList', 'numberedList' ]
* };
* ```
*
* ## Configuring items grouping
*
* You can prevent automatic items grouping by setting the `shouldNotGroupWhenFull` option:
*
* ```ts
* const config = {
* blockToolbar: {
* items: [ 'paragraph', 'heading1', 'heading2', '|', 'bulletedList', 'numberedList' ],
* shouldNotGroupWhenFull: true
* },
* };
* ```
*
* Read more about configuring the main editor toolbar in {@link module:core/editor/editorconfig~EditorConfig#toolbar}.
*/
blockToolbar?: ToolbarConfig;
}
interface PluginsMap {
[BalloonToolbar.pluginName]: BalloonToolbar;
[BlockToolbar.pluginName]: BlockToolbar;
[ContextualBalloon.pluginName]: ContextualBalloon;
[Dialog.pluginName]: Dialog;
[Notification.pluginName]: Notification;
[AccessibilityHelp.pluginName]: AccessibilityHelp;
}
}

View File

@ -0,0 +1,81 @@
/**
* @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
*/
/**
* @module ui/autocomplete/autocompleteview
*/
import { type PositioningFunction, type Locale } from '@ckeditor/ckeditor5-utils';
import SearchTextView, { type SearchTextViewConfig } from '@ckeditor/ckeditor5-ui/src/search/text/searchtextview.js';
import type SearchResultsView from '@ckeditor/ckeditor5-ui/src/search/searchresultsview.js';
import type InputBase from '@ckeditor/ckeditor5-ui/src/input/inputbase.js';
import '@ckeditor/ckeditor5-ui/theme/components/autocomplete/autocomplete.css';
/**
* The autocomplete component's view class. It extends the {@link module:ui/search/text/searchtextview~SearchTextView} class
* with a floating {@link #resultsView} that shows up when the user starts typing and hides when they blur
* the component.
*/
export default class AutocompleteView<TQueryFieldView extends InputBase<HTMLInputElement | HTMLTextAreaElement>> extends SearchTextView<TQueryFieldView> {
/**
* The configuration of the autocomplete view.
*/
protected _config: AutocompleteViewConfig<TQueryFieldView>;
resultsView: AutocompleteResultsView;
/**
* @inheritDoc
*/
constructor(locale: Locale, config: AutocompleteViewConfig<TQueryFieldView>);
/**
* Updates the position of the results view on demand.
*/
private _updateResultsViewWidthAndPosition;
/**
* Updates the visibility of the results view on demand.
*/
private _updateResultsVisibility;
/**
* Positions for the autocomplete results view. Two positions are defined by default:
* * `s` - below the search field,
* * `n` - above the search field.
*/
static defaultResultsPositions: Array<PositioningFunction>;
/**
* A function used to calculate the optimal position for the dropdown panel.
*/
private static _getOptimalPosition;
}
/**
* An interface describing additional properties of the floating search results view used by the autocomplete plugin.
*/
export interface AutocompleteResultsView extends SearchResultsView {
/**
* Controls the visibility of the results view.
*
* @observable
*/
isVisible: boolean;
/**
* Controls the position (CSS class suffix) of the results view.
*
* @internal
*/
_position?: string;
/**
* The observable property determining the CSS width of the results view.
*
* @internal
*/
_width: number;
}
export interface AutocompleteViewConfig<TConfigInputCreator extends InputBase<HTMLInputElement | HTMLTextAreaElement>> extends SearchTextViewConfig<TConfigInputCreator> {
/**
* When set `true`, the query view will be reset when the autocomplete view loses focus.
*/
resetOnBlur?: boolean;
/**
* Minimum number of characters that need to be typed before the search is performed.
*
* @default 0
*/
queryMinChars?: number;
}

View File

@ -0,0 +1,27 @@
/**
* @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
*/
/**
* @module ui/bindings/addkeyboardhandlingforgrid
*/
import type { FocusTracker, KeystrokeHandler } from '@ckeditor/ckeditor5-utils';
import type ViewCollection from '@ckeditor/ckeditor5-ui/src/viewcollection.js';
/**
* A helper that adds a keyboard navigation support (arrow up/down/left/right) for grids.
*
* @param options Configuration options.
* @param options.keystrokeHandler Keystroke handler to register navigation with arrow keys.
* @param options.focusTracker A focus tracker for grid elements.
* @param options.gridItems A collection of grid items.
* @param options.numberOfColumns Number of columns in the grid. Can be specified as a function that returns
* the number (e.g. for responsive grids).
* @param options.uiLanguageDirection String of ui language direction.
*/
export default function addKeyboardHandlingForGrid({ keystrokeHandler, focusTracker, gridItems, numberOfColumns, uiLanguageDirection }: {
keystrokeHandler: KeystrokeHandler;
focusTracker: FocusTracker;
gridItems: ViewCollection;
numberOfColumns: number | (() => number);
uiLanguageDirection?: string;
}): void;

View File

@ -0,0 +1,40 @@
/**
* @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
*/
/**
* @module ui/bindings/csstransitiondisablermixin
*/
import type { Constructor, Mixed } from '@ckeditor/ckeditor5-utils';
import type View from '@ckeditor/ckeditor5-ui/src/view.js';
/**
* A mixin that brings the possibility to temporarily disable CSS transitions using
* {@link module:ui/view~View} methods. It is helpful when, for instance, the transitions should not happen
* when the view is first displayed but they should work normal in other cases.
*
* The methods to control the CSS transitions are:
* * `disableCssTransitions()` Adds the `.ck-transitions-disabled` class to the
* {@link module:ui/view~View#element view element}.
* * `enableCssTransitions()` Removes the `.ck-transitions-disabled` class from the
* {@link module:ui/view~View#element view element}.
*
* The usage comes down to:
*
* ```ts
* const MyViewWithCssTransitionDisabler = CssTransitionDisablerMixin( MyView );
* const view = new MyViewWithCssTransitionDisabler();
*
* // ...
*
* view.disableCssTransitions();
* view.show();
* view.enableCssTransitions();
* ```
*
* @param view View instance that should get this functionality.
*/
export default function CssTransitionDisablerMixin<Base extends Constructor<View>>(view: Base): Mixed<Base, ViewWithCssTransitionDisabler>;
export type ViewWithCssTransitionDisabler = View & {
disableCssTransitions(): void;
enableCssTransitions(): void;
};

View File

@ -0,0 +1,46 @@
/**
* @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
*/
/**
* @module ui/bindings/draggableviewmixin
*/
import type View from '@ckeditor/ckeditor5-ui/src/view.js';
import { type Constructor, type Mixed } from '@ckeditor/ckeditor5-utils';
/**
* A mixin that brings the possibility to observe dragging of the view element.
* The view has to implement the {@link ~DraggableView} interface to use it:
*
* ```js
* export default class MyDraggableView extends DraggableViewMixin( View ) implements DraggableView {
* // ...
* }
* ```
*
* Creating a class extending it attaches a set of mouse and touch listeners allowing to observe dragging of the view element:
* * `mousedown` and `touchstart` on the view element - starting the dragging.
* * `mousemove` and `touchmove` on the document - updating the view coordinates.
* * `mouseup` and `touchend` on the document - stopping the dragging.
*
* The mixin itself does not provide a visual feedback (that is, the dragged element does not change its position) -
* it is up to the developer to implement it.
*/
export default function DraggableViewMixin<Base extends Constructor<View>>(view: Base): Mixed<Base, DraggableView>;
/**
* An interface that should be implemented by views that want to be draggable.
*/
export interface DraggableView extends View {
get dragHandleElement(): HTMLElement | null;
}
/**
* An event data object for the {@link ~DraggableView} `drag` event. Fired when the view is dragged.
*/
export type DraggableViewDragEvent = {
name: 'drag';
args: [
{
deltaX: number;
deltaY: number;
}
];
};

View File

@ -0,0 +1,59 @@
/**
* @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
*/
/**
* @module ui/bindings/injectcsstransitiondisabler
*/
import type View from '@ckeditor/ckeditor5-ui/src/view.js';
/**
* A decorator that brings the possibility to temporarily disable CSS transitions using
* {@link module:ui/view~View} methods. It is helpful when, for instance, the transitions should not happen
* when the view is first displayed but they should work normal in other cases.
*
* The methods to control the CSS transitions are:
* * `disableCssTransitions()` Adds the `.ck-transitions-disabled` class to the
* {@link module:ui/view~View#element view element}.
* * `enableCssTransitions()` Removes the `.ck-transitions-disabled` class from the
* {@link module:ui/view~View#element view element}.
*
* **Note**: This helper extends the {@link module:ui/view~View#template template} and must be used **after**
* {@link module:ui/view~View#setTemplate} is called:
*
* ```ts
* import injectCssTransitionDisabler from '@ckeditor/ckeditor5-ui/src/bindings/injectcsstransitiondisabler';
*
* class MyView extends View {
* constructor() {
* super();
*
* // ...
*
* this.setTemplate( { ... } );
*
* // ...
*
* injectCssTransitionDisabler( this );
*
* // ...
* }
* }
* ```
*
* The usage comes down to:
*
* ```ts
* const view = new MyView();
*
* // ...
*
* view.disableCssTransitions();
* view.show();
* view.enableCssTransitions();
* ```
*
* @deprecated
* @see module:ui/bindings/csstransitiondisablermixin~CssTransitionDisablerMixin
* @param view View instance that should get this functionality.
*/
export default function injectCssTransitionDisabler(view: View): void;

View File

@ -0,0 +1,57 @@
/**
* @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
*/
/**
* @module ui/bindings/submithandler
*/
import type View from '@ckeditor/ckeditor5-ui/src/view.js';
/**
* A handler useful for {@link module:ui/view~View views} working as HTML forms. It intercepts a native DOM
* `submit` event, prevents the default web browser behavior (navigation and page reload) and
* fires the `submit` event on a view instead. Such a custom event can be then used by any
* {@link module:utils/dom/emittermixin~DomEmitter emitter}, e.g. to serialize the form data.
*
* ```ts
* import submitHandler from '@ckeditor/ckeditor5-ui/src/bindings/submithandler';
*
* // ...
*
* class AnyFormView extends View {
* constructor() {
* super();
*
* // ...
*
* submitHandler( {
* view: this
* } );
* }
* }
*
* // ...
*
* const view = new AnyFormView();
*
* // A sample listener attached by an emitter working with the view.
* this.listenTo( view, 'submit', () => {
* saveTheFormData();
* hideTheForm();
* } );
* ```
*
* @param options Configuration options.
* @param options.view The view which DOM `submit` events should be handled.
*/
export default function submitHandler({ view }: {
view: View;
}): void;
/**
* Fired by {@link module:ui/bindings/submithandler~submitHandler} helper.
*
* @eventName module:ui/view~View#submit
*/
export type SubmitHandlerEvent = {
name: 'submit';
args: [];
};

View File

@ -0,0 +1,34 @@
/**
* @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
*/
/**
* @module ui/button/buttonlabel
*/
import type View from '@ckeditor/ckeditor5-ui/src/view.js';
/**
* The button label interface. Implemented by the {@link module:ui/button/buttonlabelview~ButtonLabelView}
* and any label view that can be used with the {@link module:ui/button/buttonview~ButtonView}.
*/
export default interface ButtonLabel extends View {
/**
* The `id` attribute of the button label. It is used for accessibility purposes
* to describe the button.
*
* @observable
*/
id: string | undefined;
/**
* The `style` attribute of the button label. It allows customizing the presentation
* of the label.
*
* @observable
*/
style: string | undefined;
/**
* The human-readable text of the label.
*
* @observable
*/
text: string | undefined;
}

View File

@ -0,0 +1,31 @@
/**
* @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
*/
/**
* @module ui/button/buttonlabelview
*/
import View from '@ckeditor/ckeditor5-ui/src/view.js';
import type ButtonLabel from '@ckeditor/ckeditor5-ui/src/button/buttonlabel.js';
/**
* A default implementation of the button view's label. It comes with a dynamic text support
* via {@link module:ui/button/buttonlabelview~ButtonLabelView#text} property.
*/
export default class ButtonLabelView extends View implements ButtonLabel {
/**
* @inheritDoc
*/
id: string | undefined;
/**
* @inheritDoc
*/
style: string | undefined;
/**
* @inheritDoc
*/
text: string | undefined;
/**
* @inheritDoc
*/
constructor();
}

View File

@ -0,0 +1,185 @@
/**
* @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
*/
/**
* @module ui/button/buttonview
*/
import View from '@ckeditor/ckeditor5-ui/src/view.js';
import IconView from '@ckeditor/ckeditor5-ui/src/icon/iconview.js';
import type ViewCollection from '@ckeditor/ckeditor5-ui/src/viewcollection.js';
import type { default as Button } from '@ckeditor/ckeditor5-ui/src/button/button.js';
import type ButtonLabel from '@ckeditor/ckeditor5-ui/src/button/buttonlabel.js';
import { type Locale } from '@ckeditor/ckeditor5-utils';
import '@ckeditor/ckeditor5-ui/theme/components/button/button.css';
/**
* The button view class.
*
* ```ts
* const view = new ButtonView();
*
* view.set( {
* label: 'A button',
* keystroke: 'Ctrl+B',
* tooltip: true,
* withText: true
* } );
*
* view.render();
*
* document.body.append( view.element );
* ```
*/
export default class ButtonView extends View<HTMLButtonElement> implements Button {
/**
* Collection of the child views inside of the button {@link #element}.
*/
readonly children: ViewCollection;
/**
* Label of the button view. Its text is configurable using the {@link #label label attribute}.
*
* If not configured otherwise in the `constructor()`, by default the label is an instance
* of {@link module:ui/button/buttonlabelview~ButtonLabelView}.
*/
readonly labelView: ButtonLabel;
/**
* The icon view of the button. Will be added to {@link #children} when the
* {@link #icon icon attribute} is defined.
*/
readonly iconView: IconView;
/**
* A view displaying the keystroke of the button next to the {@link #labelView label}.
* Added to {@link #children} when the {@link #withKeystroke `withKeystroke` attribute}
* is defined.
*/
readonly keystrokeView: View;
/**
* @inheritDoc
*/
class: string | undefined;
/**
* @inheritDoc
*/
labelStyle: string | undefined;
/**
* @inheritDoc
*/
icon: string | undefined;
/**
* @inheritDoc
*/
isEnabled: boolean;
/**
* @inheritDoc
*/
isOn: boolean;
/**
* @inheritDoc
*/
isVisible: boolean;
/**
* @inheritDoc
*/
isToggleable: boolean;
/**
* @inheritDoc
*/
keystroke: string | undefined;
/**
* @inheritDoc
*/
label: string | undefined;
/**
* @inheritDoc
*/
tabindex: number;
/**
* @inheritDoc
*/
tooltip: Button['tooltip'];
/**
* @inheritDoc
*/
tooltipPosition: Button['tooltipPosition'];
/**
* @inheritDoc
*/
type: Button['type'];
/**
* @inheritDoc
*/
withText: boolean;
/**
* @inheritDoc
*/
withKeystroke: boolean;
/**
* @inheritDoc
*/
role: string | undefined;
/**
* @inheritDoc
*/
ariaChecked: boolean | undefined;
/**
* @inheritDoc
*/
ariaLabel?: string | undefined;
/**
* @inheritDoc
*/
ariaLabelledBy: string | undefined;
/**
* Tooltip of the button bound to the template.
*
* @see #tooltip
* @see module:ui/button/buttonview~ButtonView#_getTooltipString
* @internal
* @observable
*/
_tooltipString: string;
/**
* Delayed focus function for focus handling in Safari.
*/
private _focusDelayed;
/**
* Creates an instance of the button view class.
*
* @param locale The {@link module:core/editor/editor~Editor#locale} instance.
* @param labelView The instance of the button's label. If not provided, an instance of
* {@link module:ui/button/buttonlabelview~ButtonLabelView} is used.
*/
constructor(locale?: Locale, labelView?: ButtonLabel);
/**
* @inheritDoc
*/
render(): void;
/**
* Focuses the {@link #element} of the button.
*/
focus(): void;
/**
* @inheritDoc
*/
destroy(): void;
/**
* Binds the label view instance it with button attributes.
*/
private _setupLabelView;
/**
* Creates a view that displays a keystroke next to a {@link #labelView label }
* and binds it with button attributes.
*/
private _createKeystrokeView;
/**
* Gets the text for the tooltip from the combination of
* {@link #tooltip}, {@link #label} and {@link #keystroke} attributes.
*
* @see #tooltip
* @see #_tooltipString
* @param tooltip Button tooltip.
* @param label Button label.
* @param keystroke Button keystroke.
*/
private _getTooltipString;
}

View File

@ -0,0 +1,80 @@
/**
* @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 ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview.js';
import type { Locale } from '@ckeditor/ckeditor5-utils';
/**
* The file dialog button view.
*
* This component provides a button that opens the native file selection dialog.
* It can be used to implement the UI of a file upload feature.
*
* ```ts
* const view = new FileDialogButtonView( locale );
*
* view.set( {
* acceptedType: 'image/*',
* allowMultipleFiles: true
* label: t( 'Insert image' ),
* icon: imageIcon,
* tooltip: true
* } );
*
* view.on( 'done', ( evt, files ) => {
* for ( const file of Array.from( files ) ) {
* console.log( 'Selected file', file );
* }
* } );
* ```
*/
export default class FileDialogButtonView extends ButtonView {
/**
* The button view of the component.
*
* @deprecated
*/
buttonView: ButtonView;
/**
* A hidden `<input>` view used to execute file dialog.
*/
private _fileInputView;
/**
* Accepted file types. Can be provided in form of file extensions, media type or one of:
* * `audio/*`,
* * `video/*`,
* * `image/*`.
*
* @observable
*/
acceptedType: string;
/**
* Indicates if multiple files can be selected. Defaults to `true`.
*
* @observable
*/
allowMultipleFiles: boolean;
/**
* @inheritDoc
*/
constructor(locale?: Locale);
/**
* @inheritDoc
*/
render(): void;
}
/**
* Fired when file dialog is closed with file selected.
*
* ```ts
* view.on( 'done', ( evt, files ) => {
* for ( const file of files ) {
* console.log( 'Selected file', file );
* }
* }
* ```
*/
export type FileInputViewDoneEvent = {
name: 'done';
args: [files: FileList];
};

View File

@ -0,0 +1,45 @@
/**
* @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
*/
/**
* @module ui/button/switchbuttonview
*/
import View from '@ckeditor/ckeditor5-ui/src/view.js';
import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview.js';
import type { Locale } from '@ckeditor/ckeditor5-utils';
import '@ckeditor/ckeditor5-ui/theme/components/button/switchbutton.css';
/**
* The switch button view class.
*
* ```ts
* const view = new SwitchButtonView();
*
* view.set( {
* withText: true,
* label: 'Switch me!'
* } );
*
* view.render();
*
* document.body.append( view.element );
* ```
*/
export default class SwitchButtonView extends ButtonView {
/**
* The toggle switch of the button.
*/
readonly toggleSwitchView: View;
/**
* @inheritDoc
*/
constructor(locale?: Locale);
/**
* @inheritDoc
*/
render(): void;
/**
* Creates a toggle child view.
*/
private _createToggleView;
}

View File

@ -0,0 +1,70 @@
/**
* @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
*/
/**
* @module ui/collapsible/collapsibleview
*/
import type { Locale } from '@ckeditor/ckeditor5-utils';
import View from '@ckeditor/ckeditor5-ui/src/view.js';
import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview.js';
import type ViewCollection from '@ckeditor/ckeditor5-ui/src/viewcollection.js';
import type { FocusableView } from '@ckeditor/ckeditor5-ui/src/focuscycler.js';
import '@ckeditor/ckeditor5-ui/theme/components/collapsible/collapsible.css';
/**
* A collapsible UI component. Consists of a labeled button and a container which can be collapsed
* by clicking the button. The collapsible container can be a host to other UI views.
*
* @internal
*/
export default class CollapsibleView extends View {
/**
* `true` when the container with {@link #children} is collapsed. `false` otherwise.
*
* @observable
*/
isCollapsed: boolean;
/**
* The text label of the {@link #buttonView}.
*
* @observable
* @default 'Show more'
*/
label: string;
/**
* The ID of the label inside the {@link #buttonView} that describes the collapsible
* container for assistive technologies. Set after the button was {@link #render rendered}.
*
* @internal
* @readonly
* @observable
*/
_collapsibleAriaLabelUid: string | undefined;
/**
* The main button that, when clicked, collapses or expands the container with {@link #children}.
*/
readonly buttonView: ButtonView;
/**
* A collection of the child views that can be collapsed by clicking the {@link #buttonView}.
*/
readonly children: ViewCollection<FocusableView>;
/**
* Creates an instance of the collapsible view.
*
* @param locale The {@link module:core/editor/editor~Editor#locale} instance.
* @param childViews An optional array of initial child views to be inserted into the collapsible.
*/
constructor(locale: Locale, childViews?: Array<FocusableView>);
/**
* @inheritDoc
*/
render(): void;
/**
* Focuses the first focusable.
*/
focus(): void;
/**
* Creates the main {@link #buttonView} of the collapsible.
*/
private _createButtonView;
}

View File

@ -0,0 +1,132 @@
/**
* @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
*/
/**
* @module ui/colorgrid/colorgridview
*/
import View from '@ckeditor/ckeditor5-ui/src/view.js';
import ColorTileView from '@ckeditor/ckeditor5-ui/src/colorgrid/colortileview.js';
import type DropdownPanelFocusable from '@ckeditor/ckeditor5-ui/src/dropdown/dropdownpanelfocusable.js';
import type ViewCollection from '@ckeditor/ckeditor5-ui/src/viewcollection.js';
import { FocusTracker, KeystrokeHandler, type Locale } from '@ckeditor/ckeditor5-utils';
import '@ckeditor/ckeditor5-ui/theme/components/colorgrid/colorgrid.css';
/**
* A grid of {@link module:ui/colorgrid/colortileview~ColorTileView color tiles}.
*/
export default class ColorGridView extends View implements DropdownPanelFocusable {
/**
* A number of columns for the tiles grid.
*/
readonly columns: number;
/**
* Collection of the child tile views.
*/
readonly items: ViewCollection<ColorTileView>;
/**
* Tracks information about DOM focus in the grid.
*/
readonly focusTracker: FocusTracker;
/**
* Instance of the {@link module:utils/keystrokehandler~KeystrokeHandler}.
*/
readonly keystrokes: KeystrokeHandler;
/**
* The color of the currently selected color tile in {@link #items}.
*
* @observable
*/
selectedColor: string | undefined | null;
/**
* Creates an instance of a color grid containing {@link module:ui/colorgrid/colortileview~ColorTileView tiles}.
*
* @fires execute
* @param locale The localization services instance.
* @param options Component configuration
* @param options.colorDefinitions Array with definitions
* required to create the {@link module:ui/colorgrid/colortileview~ColorTileView tiles}.
* @param options.columns A number of columns to display the tiles.
*/
constructor(locale?: Locale, options?: {
colorDefinitions?: Array<ColorDefinition>;
columns?: number;
});
/**
* Focuses the first focusable in {@link #items}.
*/
focus(): void;
/**
* Focuses the last focusable in {@link #items}.
*/
focusLast(): void;
/**
* @inheritDoc
*/
render(): void;
/**
* @inheritDoc
*/
destroy(): void;
}
/**
* A color definition used to create a {@link module:ui/colorgrid/colortileview~ColorTileView}.
*
* ```json
* {
* color: 'hsl(0, 0%, 75%)',
* label: 'Light Grey',
* options: {
* hasBorder: true
* }
* }
* ```
*/
export interface ColorDefinition {
/**
* String representing a color.
* It is used as value of background-color style in {@link module:ui/colorgrid/colortileview~ColorTileView}.
*/
color: string;
/**
* String used as label for {@link module:ui/colorgrid/colortileview~ColorTileView}.
*/
label: string;
/**
* Additional options passed to create a {@link module:ui/colorgrid/colortileview~ColorTileView}.
*/
options: {
/**
* A flag that indicates if special a CSS class should be added
* to {@link module:ui/colorgrid/colortileview~ColorTileView}, which renders a border around it.
*/
hasBorder: boolean;
};
}
/**
* Fired when the `ColorTileView` for the picked item is executed.
*
* @eventName ~ColorGridView#execute
* @param data Additional information about the event.
*/
export type ColorGridViewExecuteEvent = {
name: 'execute';
args: [data: ColorGridViewExecuteEventData];
};
/**
* The data of {@link ~ColorGridViewExecuteEvent execute event}.
*/
export interface ColorGridViewExecuteEventData {
/**
* The value of the selected color ({@link module:ui/colorgrid/colorgridview~ColorDefinition#color `color.color`}).
*/
value: string;
/**
* The `hasBorder` property of the selected color
* ({@link module:ui/colorgrid/colorgridview~ColorDefinition#options `color.options.hasBorder`}).
*/
hasBorder: boolean;
/**
* The label of the selected color ({@link module:ui/colorgrid/colorgridview~ColorDefinition#label `color.label`})
*/
label: string;
}

View File

@ -0,0 +1,28 @@
/**
* @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
*/
/**
* @module ui/colorgrid/colortileview
*/
import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview.js';
import type { Locale } from '@ckeditor/ckeditor5-utils';
/**
* This class represents a single color tile in the {@link module:ui/colorgrid/colorgridview~ColorGridView}.
*/
export default class ColorTileView extends ButtonView {
/**
* String representing a color shown as tile's background.
*/
color: string | undefined;
/**
* A flag that toggles a special CSS class responsible for displaying
* a border around the button.
*/
hasBorder: boolean;
constructor(locale?: Locale);
/**
* @inheritDoc
*/
render(): void;
}

View File

@ -0,0 +1,142 @@
/**
* @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
*/
/**
* @module ui/colorpicker/colorpickerview
*/
import { type ColorPickerViewConfig } from '@ckeditor/ckeditor5-ui/src/colorpicker/utils.js';
import { type Locale } from '@ckeditor/ckeditor5-utils';
import View from '@ckeditor/ckeditor5-ui/src/view.js';
import type ViewCollection from '@ckeditor/ckeditor5-ui/src/viewcollection.js';
import { HexBase } from 'vanilla-colorful/lib/entrypoints/hex';
import '@ckeditor/ckeditor5-ui/theme/components/colorpicker/colorpicker.css';
declare global {
interface HTMLElementTagNameMap {
'hex-color-picker': HexBase;
}
}
/**
* A class which represents a color picker with an input field for defining custom colors.
*/
export default class ColorPickerView extends View {
/**
* Element with saturation and hue sliders.
*/
picker: HexBase;
/**
* Container for a `#` sign prefix and an input for displaying and defining custom colors
* in HEX format.
*/
hexInputRow: ColorPickerInputRowView;
/**
* Current color selected in the color picker. It can be set by the component itself
* (through the palette or input) or from the outside (e.g. to reflect the current selection color).
*/
color: string;
/**
* List of slider views of the color picker.
*/
slidersView: ViewCollection<SliderView>;
/**
* An internal representation of a color.
*
* Since the picker uses a hex format, that's how we store it.
*
* Since this is unified color format it won't fire a change event if color is changed
* from `#f00` to `#ff0000` (same value, different format).
*
* @observable
* @private
*/
_hexColor: string;
/**
* Debounced function updating the `color` property in the component
* and firing the `ColorPickerColorSelectedEvent`. Executed whenever color in component
* is changed by the user interaction (through the palette or input).
*
* @private
*/
private _debounceColorPickerEvent;
/**
* A reference to the configuration of the color picker specified in the constructor.
*
* @private
*/
private _config;
/**
* Creates a view of color picker.
*
* @param locale
* @param config
*/
constructor(locale: Locale | undefined, config?: ColorPickerViewConfig);
/**
* Renders color picker in the view.
*/
render(): void;
/**
* Focuses the first pointer in color picker.
*
*/
focus(): void;
/**
* Creates collection of sliders in color picker.
*
* @private
*/
private _createSlidersView;
/**
* Creates input row for defining custom colors in color picker.
*
* @private
*/
private _createInputRow;
/**
* Creates the input where user can type or paste the color in hex format.
*
* @private
*/
private _createColorInput;
}
declare class SliderView extends View {
/**
* @param element HTML element of slider in color picker.
*/
constructor(element: HTMLElement);
/**
* Focuses element.
*/
focus(): void;
}
declare class ColorPickerInputRowView extends View {
/**
* A collection of row items (buttons, dropdowns, etc.).
*/
readonly children: ViewCollection;
/**
* Creates an instance of the form row class.
*
* @param locale The locale instance.
*/
constructor(locale: Locale, children?: Array<View>);
}
/**
* An event fired whenever the color was selected through the color picker palette
* or the color picker input.
*
* This even fires only when the user changes the color. It does not fire when the color
* is changed programmatically, e.g. via
* {@link module:ui/colorpicker/colorpickerview~ColorPickerView#color}.
*
* @eventName ~ColorPickerView#colorSelected
*/
export type ColorPickerColorSelectedEvent = {
name: 'colorSelected';
args: [
{
color: string;
}
];
};
export {};

View File

@ -0,0 +1,195 @@
/**
* @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
*/
/**
* @module ui/colorselector/colorgridsfragmentview
*/
import View from '@ckeditor/ckeditor5-ui/src/view.js';
import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview.js';
import ColorGridView, { type ColorDefinition } from '@ckeditor/ckeditor5-ui/src/colorgrid/colorgridview.js';
import DocumentColorCollection from '@ckeditor/ckeditor5-ui/src/colorselector/documentcolorcollection.js';
import type { Model } from '@ckeditor/ckeditor5-engine';
import type { FocusTracker, Locale } from '@ckeditor/ckeditor5-utils';
import type ViewCollection from '@ckeditor/ckeditor5-ui/src/viewcollection.js';
import type { FocusableView } from '@ckeditor/ckeditor5-ui/src/focuscycler.js';
/**
* One of the fragments of {@link module:ui/colorselector/colorselectorview~ColorSelectorView}.
*
* It provides a UI that allows users to select colors from the a predefined set and from existing document colors.
*
* It consists of the following subcomponents:
*
* * A "Remove color" button,
* * A static {@link module:ui/colorgrid/colorgridview~ColorGridView} of colors defined in the configuration,
* * A dynamic {@link module:ui/colorgrid/colorgridview~ColorGridView} of colors used in the document.
* * If color picker is configured, the "Color Picker" button is visible too.
*/
export default class ColorGridsFragmentView extends View {
/**
* A collection of the children of the table.
*/
readonly items: ViewCollection;
/**
* An array with objects representing colors to be displayed in the grid.
*/
colorDefinitions: Array<ColorDefinition>;
/**
* Tracks information about the DOM focus in the list.
*/
readonly focusTracker: FocusTracker;
/**
* The number of columns in the color grid.
*/
columns: number;
/**
* Preserves the reference to {@link module:ui/colorselector/documentcolorcollection~DocumentColorCollection} used to collect
* definitions that store the document colors.
*
* @readonly
*/
documentColors: DocumentColorCollection;
/**
* The maximum number of colors in the document colors section.
* If it equals 0, the document colors section is not added.
*
* @readonly
*/
documentColorsCount?: number;
/**
* Keeps the value of the command associated with the table for the current selection.
*/
selectedColor: string;
/**
* Preserves the reference to {@link module:ui/colorgrid/colorgridview~ColorGridView} used to create
* the default (static) color set.
*
* The property is loaded once the the parent dropdown is opened the first time.
*
* @readonly
*/
staticColorsGrid: ColorGridView | undefined;
/**
* Preserves the reference to {@link module:ui/colorgrid/colorgridview~ColorGridView} used to create
* the document colors. It remains undefined if the document colors feature is disabled.
*
* The property is loaded once the the parent dropdown is opened the first time.
*
* @readonly
*/
documentColorsGrid: ColorGridView | undefined;
/**
* The "Color picker" button view.
*/
colorPickerButtonView?: ButtonView;
/**
* The "Remove color" button view.
*/
removeColorButtonView: ButtonView;
/**
* The property which is responsible for is component visible or not.
*/
isVisible: boolean;
/**
* A collection of views that can be focused in the view.
*
* @readonly
*/
protected _focusables: ViewCollection<FocusableView>;
/**
* Document color section's label.
*
* @readonly
*/
private _documentColorsLabel?;
/**
* The label of the button responsible for removing color attributes.
*/
private _removeButtonLabel;
/**
* The label of the button responsible for switching to the color picker component.
*/
private _colorPickerLabel;
/**
* Creates an instance of the view.
*
* @param locale The localization services instance.
* @param colors An array with definitions of colors to be displayed in the table.
* @param columns The number of columns in the color grid.
* @param removeButtonLabel The label of the button responsible for removing the color.
* @param colorPickerLabel The label of the button responsible for color picker appearing.
* @param documentColorsLabel The label for the section with the document colors.
* @param documentColorsCount The number of colors in the document colors section inside the color dropdown.
* @param focusTracker Tracks information about the DOM focus in the list.
* @param focusables A collection of views that can be focused in the view.
*/
constructor(locale: Locale, { colors, columns, removeButtonLabel, documentColorsLabel, documentColorsCount, colorPickerLabel, focusTracker, focusables }: {
colors: Array<ColorDefinition>;
columns: number;
removeButtonLabel: string;
colorPickerLabel: string;
documentColorsLabel?: string;
documentColorsCount?: number;
focusTracker: FocusTracker;
focusables: ViewCollection<FocusableView>;
});
/**
* Scans through the editor model and searches for text node attributes with the given attribute name.
* Found entries are set as document colors.
*
* All the previously stored document colors will be lost in the process.
*
* @param model The model used as a source to obtain the document colors.
* @param attributeName Determines the name of the related model's attribute for a given dropdown.
*/
updateDocumentColors(model: Model, attributeName: string): void;
/**
* Refreshes the state of the selected color in one or both {@link module:ui/colorgrid/colorgridview~ColorGridView}s
* available in the {@link module:ui/colorselector/colorselectorview~ColorSelectorView}. It guarantees that the selection will
* occur only in one of them.
*/
updateSelectedColors(): void;
/**
* @inheritDoc
*/
render(): void;
/**
* Focuses the component.
*/
focus(): void;
/**
* @inheritDoc
*/
destroy(): void;
/**
* Handles displaying the color picker button (if it was previously created) and making it focusable.
*/
addColorPickerButton(): void;
/**
* Adds color selector elements to focus tracker.
*/
private _addColorSelectorElementsToFocusTracker;
/**
* Creates the button responsible for displaying the color picker component.
*/
private _createColorPickerButton;
/**
* Adds the remove color button as a child of the current view.
*/
private _createRemoveColorButton;
/**
* Creates a static color grid based on the editor configuration.
*/
private _createStaticColorsGrid;
/**
* Creates the document colors section view and binds it to {@link #documentColors}.
*/
private _createDocumentColorsGrid;
/**
* Adds a given color to the document colors list. If possible, the method will attempt to use
* data from the {@link #colorDefinitions} (label, color options).
*
* @param color A string that stores the value of the recently applied color.
*/
private _addColorToDocumentColors;
}

View File

@ -0,0 +1,129 @@
/**
* @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
*/
/**
* @module ui/colorselector/colorpickerfragmentview
*/
import View from '@ckeditor/ckeditor5-ui/src/view.js';
import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview.js';
import type ViewCollection from '@ckeditor/ckeditor5-ui/src/viewcollection.js';
import type { FocusableView } from '@ckeditor/ckeditor5-ui/src/focuscycler.js';
import { default as ColorPickerView } from '@ckeditor/ckeditor5-ui/src/colorpicker/colorpickerview.js';
import type { FocusTracker, KeystrokeHandler, Locale } from '@ckeditor/ckeditor5-utils';
import type { ColorPickerViewConfig } from '@ckeditor/ckeditor5-ui/src/colorpicker/utils.js';
/**
* One of the fragments of {@link module:ui/colorselector/colorselectorview~ColorSelectorView}.
*
* It allows users to select a color from a color picker.
*
* It consists of the following subcomponents:
*
* * A color picker saturation and hue sliders,
* * A text input accepting colors in HEX format,
* * "Save" and "Cancel" action buttons.
*/
export default class ColorPickerFragmentView extends View {
/**
* A collection of component's children.
*/
readonly items: ViewCollection;
/**
* A view with saturation and hue sliders and color input.
*/
colorPickerView?: ColorPickerView;
/**
* The "Save" button view.
*/
saveButtonView: ButtonView;
/**
* The "Cancel" button view.
*/
cancelButtonView: ButtonView;
/**
* The action bar where are "Save" button and "Cancel" button.
*/
actionBarView: View;
/**
* Tracks information about the DOM focus in the list.
*/
readonly focusTracker: FocusTracker;
/**
* An instance of the {@link module:utils/keystrokehandler~KeystrokeHandler}.
*/
readonly keystrokes: KeystrokeHandler;
/**
* Indicates whether the component is visible or not.
*/
isVisible: boolean;
/**
* Keeps the value of the command associated with the component for the current selection.
*/
selectedColor?: string;
/**
* A collection of views that can be focused in the view.
*
* @readonly
*/
protected _focusables: ViewCollection<FocusableView>;
/**
* A reference to the configuration of {@link #colorPickerView}. `false` when the view was
* configured without a color picker.
*
* @readonly
*/
private _colorPickerViewConfig;
/**
* Creates an instance of the view.
*
* @param locale The localization services instance.
* @param focusTracker Tracks information about the DOM focus in the list.
* @param focusables A collection of views that can be focused in the view..
* @param keystrokes An instance of the {@link module:utils/keystrokehandler~KeystrokeHandler}.
* @param colorPickerViewConfig The configuration of color picker feature. If set to `false`, the color picker
* will not be rendered.
*/
constructor(locale: Locale, { focusTracker, focusables, keystrokes, colorPickerViewConfig }: {
focusTracker: FocusTracker;
focusables: ViewCollection<FocusableView>;
keystrokes: KeystrokeHandler;
colorPickerViewConfig: ColorPickerViewConfig | false;
});
/**
* @inheritDoc
*/
render(): void;
/**
* @inheritDoc
*/
destroy(): void;
/**
* Focuses the color picker.
*/
focus(): void;
/**
* When color picker is focused and "enter" is pressed it executes command.
*/
private _executeOnEnterPress;
/**
* Removes default behavior of arrow keys in dropdown.
*/
private _stopPropagationOnArrowsKeys;
/**
* Adds color picker elements to focus tracker.
*/
private _addColorPickersElementsToFocusTracker;
/**
* Creates bar containing "Save" and "Cancel" buttons.
*/
private _createActionBarView;
/**
* Creates "Save" and "Cancel" buttons.
*/
private _createActionButtons;
/**
* Fires the `execute` event if color in color picker has been changed
* by the user.
*/
private _executeUponColorChange;
}

View File

@ -0,0 +1,242 @@
/**
* @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
*/
/**
* @module ui/colorselector/colorselectorview
*/
import FocusCycler, { type FocusableView } from '@ckeditor/ckeditor5-ui/src/focuscycler.js';
import View from '@ckeditor/ckeditor5-ui/src/view.js';
import ViewCollection from '@ckeditor/ckeditor5-ui/src/viewcollection.js';
import { FocusTracker, KeystrokeHandler, type Locale } from '@ckeditor/ckeditor5-utils';
import type { ColorPickerViewConfig } from '@ckeditor/ckeditor5-ui/src/colorpicker/utils.js';
import type { ColorDefinition } from '@ckeditor/ckeditor5-ui/src/colorgrid/colorgridview.js';
import type { Model } from '@ckeditor/ckeditor5-engine';
import ColorGridsFragmentView from '@ckeditor/ckeditor5-ui/src/colorselector/colorgridsfragmentview.js';
import ColorPickerFragmentView from '@ckeditor/ckeditor5-ui/src/colorselector/colorpickerfragmentview.js';
import '@ckeditor/ckeditor5-ui/theme/components/colorselector/colorselector.css';
/**
* The configurable color selector view class. It allows users to select colors from a predefined set of colors as well as from
* a color picker.
*
* This meta-view is is made of two components (fragments):
*
* * {@link module:ui/colorselector/colorselectorview~ColorSelectorView#colorGridsFragmentView},
* * {@link module:ui/colorselector/colorselectorview~ColorSelectorView#colorPickerFragmentView}.
*
* ```ts
* const colorDefinitions = [
* { color: '#000', label: 'Black', options: { hasBorder: false } },
* { color: 'rgb(255, 255, 255)', label: 'White', options: { hasBorder: true } },
* { color: 'red', label: 'Red', options: { hasBorder: false } }
* ];
*
* const selectorView = new ColorSelectorView( locale, {
* colors: colorDefinitions,
* columns: 5,
* removeButtonLabel: 'Remove color',
* documentColorsLabel: 'Document colors',
* documentColorsCount: 4,
* colorPickerViewConfig: {
* format: 'hsl'
* }
* } );
*
* selectorView.appendUI();
* selectorView.selectedColor = 'red';
* selectorView.updateSelectedColors();
*
* selectorView.on<ColorSelectorExecuteEvent>( 'execute', ( evt, data ) => {
* console.log( 'Color changed', data.value, data.source );
* } );
*
* selectorView.on<ColorSelectorColorPickerShowEvent>( 'colorPicker:show', ( evt ) => {
* console.log( 'Color picker showed up', evt );
* } );
*
* selectorView.on<ColorSelectorColorPickerCancelEvent>( 'colorPicker:cancel', ( evt ) => {
* console.log( 'Color picker cancel', evt );
* } );
*
* selectorView.render();
*
* document.body.appendChild( selectorView.element );
* ```
*/
export default class ColorSelectorView extends View {
/**
* Tracks information about the DOM focus in the list.
*/
readonly focusTracker: FocusTracker;
/**
* An instance of the {@link module:utils/keystrokehandler~KeystrokeHandler}.
*/
readonly keystrokes: KeystrokeHandler;
/**
* A collection of components.
*/
readonly items: ViewCollection;
/**
* A fragment that allows users to select colors from the a predefined set and from existing document colors.
*/
readonly colorGridsFragmentView: ColorGridsFragmentView;
/**
* A fragment that allows users to select a color from a color picker.
*/
readonly colorPickerFragmentView: ColorPickerFragmentView;
/**
* Keeps the value of the command associated with the component for the current selection.
*/
selectedColor?: string;
/**
* Reflects the visibility state of the color grids fragment.
*
* @internal
*/
_isColorGridsFragmentVisible: boolean;
/**
* Reflects the visibility state of the color picker fragment.
*
* @internal
*/
_isColorPickerFragmentVisible: boolean;
/**
* Helps cycling over focusable {@link #items} in the list.
*
* @readonly
*/
protected _focusCycler: FocusCycler;
/**
* A collection of views that can be focused in the view.
*
* @readonly
*/
protected _focusables: ViewCollection<FocusableView>;
/**
* The configuration of color picker sub-component.
*/
private _colorPickerViewConfig;
/**
* Creates a view to be inserted as a child of {@link module:ui/dropdown/dropdownview~DropdownView}.
*
* @param locale The localization services instance.
* @param colors An array with definitions of colors to be displayed in the table.
* @param columns The number of columns in the color grid.
* @param removeButtonLabel The label of the button responsible for removing the color.
* @param colorPickerLabel The label of the button responsible for color picker appearing.
* @param documentColorsLabel The label for the section with the document colors.
* @param documentColorsCount The number of colors in the document colors section inside the color dropdown.
* @param colorPickerViewConfig The configuration of color picker feature. If set to `false`, the color picker will be hidden.
*/
constructor(locale: Locale, { colors, columns, removeButtonLabel, documentColorsLabel, documentColorsCount, colorPickerLabel, colorPickerViewConfig }: {
colors: Array<ColorDefinition>;
columns: number;
removeButtonLabel: string;
documentColorsLabel?: string;
documentColorsCount?: number;
colorPickerLabel: string;
colorPickerViewConfig: ColorPickerViewConfig | false;
});
/**
* @inheritDoc
*/
render(): void;
/**
* @inheritDoc
*/
destroy(): void;
/**
* Renders the internals of the component on demand:
* * {@link #colorPickerFragmentView},
* * {@link #colorGridsFragmentView}.
*
* It allows for deferring component initialization to improve the performance.
*
* See {@link #showColorPickerFragment}, {@link #showColorGridsFragment}.
*/
appendUI(): void;
/**
* Shows the {@link #colorPickerFragmentView} and hides the {@link #colorGridsFragmentView}.
*
* **Note**: It requires {@link #appendUI} to be called first.
*
* See {@link #showColorGridsFragment}, {@link ~ColorSelectorView#event:colorPicker:show}.
*/
showColorPickerFragment(): void;
/**
* Shows the {@link #colorGridsFragmentView} and hides the {@link #colorPickerFragmentView}.
*
* See {@link #showColorPickerFragment}.
*
* **Note**: It requires {@link #appendUI} to be called first.
*/
showColorGridsFragment(): void;
/**
* Focuses the first focusable element in {@link #items}.
*/
focus(): void;
/**
* Focuses the last focusable element in {@link #items}.
*/
focusLast(): void;
/**
* Scans through the editor model and searches for text node attributes with the given `attributeName`.
* Found entries are set as document colors in {@link #colorGridsFragmentView}.
*
* All the previously stored document colors will be lost in the process.
*
* @param model The model used as a source to obtain the document colors.
* @param attributeName Determines the name of the related model's attribute for a given dropdown.
*/
updateDocumentColors(model: Model, attributeName: string): void;
/**
* Refreshes the state of the selected color in one or both grids located in {@link #colorGridsFragmentView}.
*
* It guarantees that the selection will occur only in one of them.
*/
updateSelectedColors(): void;
/**
* Appends the view containing static and document color grid views.
*/
private _appendColorGridsFragment;
/**
* Appends the view with the color picker.
*/
private _appendColorPickerFragment;
}
/**
* Fired whenever the color was changed. There are multiple sources of this event and you can distinguish them
* using the `source` property passed along this event.
*
* @eventName ~ColorSelectorView#execute
*/
export type ColorSelectorExecuteEvent = {
name: 'execute';
args: [
{
value: string;
source: 'staticColorsGrid' | 'documentColorsGrid' | 'removeColorButton' | 'colorPicker' | 'colorPickerSaveButton';
}
];
};
/**
* Fired when the user pressed the "Cancel" button in the
* {@link module:ui/colorselector/colorselectorview~ColorSelectorView#colorPickerFragmentView}.
*
* @eventName ~ColorSelectorView#colorPicker:cancel
*/
export type ColorSelectorColorPickerCancelEvent = {
name: 'colorPicker:cancel';
args: [];
};
/**
* Fired whenever {@link module:ui/colorselector/colorselectorview~ColorSelectorView#colorPickerFragmentView} is shown.
*
* See {@link ~ColorSelectorView#showColorPickerFragment}.
*
* @eventName ~ColorSelectorView#colorPicker:show
*/
export type ColorSelectorColorPickerShowEvent = {
name: 'colorPicker:show';
args: [];
};

View File

@ -0,0 +1,70 @@
/**
* @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
*/
/**
* @module ui/colorselector/documentcolorcollection
*/
import type { ColorDefinition } from '@ckeditor/ckeditor5-ui/src/colorgrid/colorgridview.js';
import { Collection, type CollectionAddEvent, type CollectionRemoveEvent, type CollectionChangeEvent } from '@ckeditor/ckeditor5-utils';
declare const DocumentColorCollection_base: import("@ckeditor/ckeditor5-utils").Mixed<{
new (options?: {
readonly idProperty?: string | undefined;
} | undefined): Collection<ColorDefinition>;
new (initialItems: Iterable<ColorDefinition>, options?: {
readonly idProperty?: string | undefined;
} | undefined): Collection<ColorDefinition>;
}, import("@ckeditor/ckeditor5-utils").Observable>;
/**
* A collection to store document colors. It enforces colors to be unique.
*/
export default class DocumentColorCollection extends DocumentColorCollection_base {
/**
* Indicates whether the document color collection is empty.
*
* @observable
*/
readonly isEmpty: boolean;
constructor(options?: any);
/**
* Adds a color to the document color collection.
*
* This method ensures that no color duplicates are inserted (compared using
* the color value of the {@link module:ui/colorgrid/colorgridview~ColorDefinition}).
*
* If the item does not have an ID, it will be automatically generated and set on the item.
*
* @param index The position of the item in the collection. The item is pushed to the collection when `index` is not specified.
* @fires add
* @fires change
*/
add(item: ColorDefinition, index?: number): this;
/**
* Checks if an object with given colors is present in the document color collection.
*/
hasColor(color: string): boolean;
}
/**
* Fired when the collection was changed due to adding or removing items.
*
* @eventName ~DocumentColorCollection#change
* @param data Changed items.
*/
export type DocumentColorCollectionChangeEvent = CollectionChangeEvent;
/**
* Fired when an item is added to the collection.
*
* @eventName ~DocumentColorCollection#add
* @param item The added item.
* @param index An index where the addition occurred.
*/
export type DocumentColorCollectionAddEvent = CollectionAddEvent;
/**
* Fired when an item is removed from the collection.
*
* @eventName ~DocumentColorCollection#remove
* @param item The removed item.
* @param index Index from which item was removed.
*/
export type DocumentColorCollectionRemoveEvent = CollectionRemoveEvent;
export {};

View File

@ -0,0 +1,81 @@
/**
* @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
*/
/**
* @module ui/componentfactory
*/
import { type Locale } from '@ckeditor/ckeditor5-utils';
import type { Editor } from '@ckeditor/ckeditor5-core';
import type View from '@ckeditor/ckeditor5-ui/src/view.js';
/**
* A helper class implementing the UI component ({@link module:ui/view~View view}) factory.
*
* It allows functions producing specific UI components to be registered under their unique names
* in the factory. A registered component can be then instantiated by providing its name.
* Note that the names are case insensitive.
*
* ```ts
* // The editor provides localization tools for the factory.
* const factory = new ComponentFactory( editor );
*
* factory.add( 'foo', locale => new FooView( locale ) );
* factory.add( 'bar', locale => new BarView( locale ) );
*
* // An instance of FooView.
* const fooInstance = factory.create( 'foo' );
*
* // Names are case insensitive so this is also allowed:
* const barInstance = factory.create( 'Bar' );
* ```
*
* The {@link module:core/editor/editor~Editor#locale editor locale} is passed to the factory
* function when {@link module:ui/componentfactory~ComponentFactory#create} is called.
*/
export default class ComponentFactory {
/**
* The editor instance that the factory belongs to.
*/
readonly editor: Editor;
/**
* Registered component factories.
*/
private readonly _components;
/**
* Creates an instance of the factory.
*
* @param editor The editor instance.
*/
constructor(editor: Editor);
/**
* Returns an iterator of registered component names. Names are returned in lower case.
*/
names(): IterableIterator<string>;
/**
* Registers a component factory function that will be used by the
* {@link #create create} method and called with the
* {@link module:core/editor/editor~Editor#locale editor locale} as an argument,
* allowing localization of the {@link module:ui/view~View view}.
*
* @param name The name of the component.
* @param callback The callback that returns the component.
*/
add(name: string, callback: (locale: Locale) => View): void;
/**
* Creates an instance of a component registered in the factory under a specific name.
*
* When called, the {@link module:core/editor/editor~Editor#locale editor locale} is passed to
* the previously {@link #add added} factory function, allowing localization of the
* {@link module:ui/view~View view}.
*
* @param name The name of the component.
* @returns The instantiated component view.
*/
create(name: string): View;
/**
* Checks if a component of a given name is registered in the factory.
*
* @param name The name of the component.
*/
has(name: string): boolean;
}

View File

@ -0,0 +1,273 @@
/**
* @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
*/
/**
* @module ui/dialog/dialog
*/
import type View from '@ckeditor/ckeditor5-ui/src/view.js';
import { type Editor, Plugin } from '@ckeditor/ckeditor5-core';
import DialogView, { DialogViewPosition } from '@ckeditor/ckeditor5-ui/src/dialog/dialogview.js';
import type { DialogActionButtonDefinition } from '@ckeditor/ckeditor5-ui/src/dialog/dialogactionsview.js';
/**
* The dialog controller class. It is used to show and hide the {@link module:ui/dialog/dialogview~DialogView}.
*/
export default class Dialog extends Plugin {
/**
* The name of the currently visible dialog view instance.
*
* @observable
*/
id: string | null;
/**
* The currently visible dialog view instance.
*/
view?: DialogView;
/**
* The `Dialog` plugin instance which most recently showed the dialog.
*
* Only one dialog can be visible at once, even if there are many editor instances on the page.
* If an editor wants to show a dialog, it should first hide the dialog that is already opened.
* But only the `Dialog` instance that showed the dialog is able able to properly hide it.
* This is why we need to store it in a globally available space (static property).
* This way every `Dialog` plugin in every editor is able to correctly close any open dialog window.
*/
private static _visibleDialogPlugin;
/**
* A flag indicating whether the dialog is currently visible.
*
* @observable
*/
isOpen: boolean;
/**
* A configurable callback called when the dialog is hidden.
*/
private _onHide;
/**
* @inheritDoc
*/
static get pluginName(): "Dialog";
/**
* @inheritDoc
*/
constructor(editor: Editor);
/**
* Initiates listeners for the `show` and `hide` events emitted by this plugin.
*
* We could not simply decorate the {@link #show} and {@link #hide} methods to fire events,
* because they would be fired in the wrong order &ndash; first would be `show` and then `hide`
* (because showing the dialog actually starts with hiding the previously visible one).
* Hence, we added private methods {@link #_show} and {@link #_hide} which are called on events
* in the desired sequence.
*/
private _initShowHideListeners;
/**
* Initiates keystroke handler for toggling the focus between the editor and the dialog view.
*/
private _initFocusToggler;
/**
* Provides an integration between the root attaching and detaching and positioning of the view.
*/
private _initMultiRootIntegration;
/**
* Displays a dialog window.
*
* This method requires a {@link ~DialogDefinition} that defines the dialog's content, title, icon, action buttons, etc.
*
* For example, the following definition will create a dialog with:
* * A header consisting of an icon, a title, and a "Close" button (it is added by default).
* * A content consisting of a view with a single paragraph.
* * A footer consisting of two buttons: "Yes" and "No".
*
* ```js
* // Create the view that will be used as the dialog's content.
* const textView = new View( locale );
*
* textView.setTemplate( {
* tag: 'div',
* attributes: {
* style: {
* padding: 'var(--ck-spacing-large)',
* whiteSpace: 'initial',
* width: '100%',
* maxWidth: '500px'
* },
* tabindex: -1
* },
* children: [
* 'Lorem ipsum dolor sit amet...'
* ]
* } );
*
* // Show the dialog.
* editor.plugins.get( 'Dialog' ).show( {
* id: 'myDialog',
* icon: 'myIcon', // This should be an SVG string.
* title: 'My dialog',
* content: textView,
* actionButtons: [
* {
* label: t( 'Yes' ),
* class: 'ck-button-action',
* withText: true,
* onExecute: () => dialog.hide()
* },
* {
* label: t( 'No' ),
* withText: true,
* onExecute: () => dialog.hide()
* }
* ]
* } );
* ```
*
* By specifying the {@link ~DialogDefinition#onShow} and {@link ~DialogDefinition#onHide} callbacks
* it is also possible to add callbacks that will be called when the dialog is shown or hidden.
*
* For example, the callbacks in the following definition:
* * Disable the default behavior of the <kbd>Esc</kbd> key.
* * Fire a custom event when the dialog gets hidden.
*
* ```js
* editor.plugins.get( 'Dialog' ).show( {
* // ...
* onShow: dialog => {
* dialog.view.on( 'close', ( evt, data ) => {
* // Only prevent the event from the "Esc" key - do not affect the other ways of closing the dialog.
* if ( data.source === 'escKeyPress' ) {
* evt.stop();
* }
* } );
* },
* onHide: dialog => {
* dialog.fire( 'dialogDestroyed' );
* }
* } );
* ```
*
* Internally, calling this method:
* 1. Hides the currently visible dialog (if any) calling the {@link #hide} method
* (fires the {@link ~DialogHideEvent hide event}).
* 2. Fires the {@link ~DialogShowEvent show event} which allows for adding callbacks that customize the
* behavior of the dialog.
* 3. Shows the dialog.
*/
show(dialogDefinition: DialogDefinition): void;
/**
* Handles creating the {@link module:ui/dialog/dialogview~DialogView} instance and making it visible.
*/
private _show;
/**
* Hides the dialog. This method is decorated to enable interacting on the {@link ~DialogHideEvent hide event}.
*
* See {@link #show}.
*/
hide(): void;
/**
* Destroys the {@link module:ui/dialog/dialogview~DialogView} and cleans up the stored dialog state.
*/
private _hide;
}
/**
* The definition that describes a dialog window and its content. Passed to the {@link module:ui/dialog/dialog~Dialog#show} method.
*/
export interface DialogDefinition {
/**
* A unique identifier of the dialog. It allows for distinguishing between different dialogs and their visibility.
* For instance, when open, the ID of the currently visible dialog is stored in {@link module:ui/dialog/dialog~Dialog#id}.
*
* The `id` is also passed along the {@link module:ui/dialog/dialog~DialogShowEvent} and {@link module:ui/dialog/dialog~DialogHideEvent}
* events.
*/
id: string;
/**
* The SVG string of an icon displayed in dialogs's header. Used only when {@link #title} is also set
* and the header is displayed.
*
* See more in {@link module:ui/icon/iconview~IconView#content}.
*/
icon?: string;
/**
* A title displayed in the dialogs's header. It also works as an accessible name of the dialog used by assistive technologies.
*
* When not set, the header is not displayed. Affects {@link #icon} and {@link #hasCloseButton}.
*/
title?: string;
/**
* A flag indicating whether the dialog should have a close button in the header.
* `true` by default. Works when {@link #title} is also set and the header is displayed.
*
* **Note**: If you hide the close button, make sure that the dialog can be closed in another way.
*/
hasCloseButton?: boolean;
/**
* The content of the dialog. It can be a single {@link module:ui/view~View} or an array of views.
*/
content?: View | Array<View>;
/**
* The action buttons displayed in the dialog's footer.
*/
actionButtons?: Array<DialogActionButtonDefinition>;
/**
* An additional CSS class set on the outermost (`.ck.ck-dialog`) container element allowing for visual customization.
*/
className?: string;
/**
* When set to `true`, the dialog will become a modal, that is, it will block the UI until it is closed.
*/
isModal?: boolean;
/**
* Available dialog positions. By default `DialogViewPosition.EDITOR_CENTER` is used for {@link #isModal non-modals}
* and `DialogViewPosition.SCREEN_CENTER` for modals.
*
* {@link module:ui/dialog/dialogview#DialogViewPosition Learn more} about available positions.
*/
position?: typeof DialogViewPosition[keyof typeof DialogViewPosition];
/**
* A callback called when the dialog shows up with a `low` priority. It allows for setting up the dialog's {@link #content}.
*/
onShow?: (dialog: Dialog) => void;
/**
* A callback called when the dialog hides with a `low` priority.
* It allows for cleaning up (for example, resetting) the dialog's {@link #content}.
*/
onHide?: (dialog: Dialog) => void;
}
/**
* An event fired after {@link module:ui/dialog/dialog~Dialog#show} is called. You can use it to customize the behavior
* of any dialog.
*
* ```js
* import { DialogViewPosition } from 'ckeditor5/src/ui.js';
*
* // ...
*
* // Changes the position of the "Find and Replace" dialog.
* editor.plugins.get( 'Dialog' ).on( 'show:findAndReplace', ( evt, data ) => {
* Object.assign( data, { position: DialogViewPosition.EDITOR_BOTTOM_CENTER } );
* }, { priority: 'high' } );
* ```
*
* @eventName ~Dialog#show
*/
export type DialogShowEvent = {
name: 'show' | `show:${string}`;
args: [dialogDefinition: DialogDefinition];
};
/**
* An event fired after {@link module:ui/dialog/dialog~Dialog#hide} is called. You can use it to customize the behavior
* of any dialog.
*
* ```js
* // Logs after the "Find and Replace" dialog gets hidden
* editor.plugins.get( 'Dialog' ).on( 'hide:findAndReplace', () => {
* console.log( 'The "Find and Replace" dialog was hidden.' );
* } );
* ```
*
* @eventName ~Dialog#hide
*/
export type DialogHideEvent = {
name: 'hide' | `hide:${string}`;
args: [];
};

View File

@ -0,0 +1,69 @@
/**
* @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
*/
/**
* @module ui/dialog/dialogactionsview
*/
import { KeystrokeHandler, type Locale } from '@ckeditor/ckeditor5-utils';
import type { default as Button } from '@ckeditor/ckeditor5-ui/src/button/button.js';
import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview.js';
import View from '@ckeditor/ckeditor5-ui/src/view.js';
import ViewCollection from '@ckeditor/ckeditor5-ui/src/viewcollection.js';
import FocusCycler, { type FocusableView } from '@ckeditor/ckeditor5-ui/src/focuscycler.js';
import '@ckeditor/ckeditor5-ui/theme/components/dialog/dialogactions.css';
/**
* A dialog actions view class. It contains button views which are used to execute dialog actions.
*/
export default class DialogActionsView extends View {
/**
* A collection of button views.
*/
readonly children: ViewCollection<FocusableView>;
/**
* A keystroke handler instance.
*/
readonly keystrokes: KeystrokeHandler;
/**
* A focus cycler instance.
*/
readonly focusCycler: FocusCycler;
/**
* A focus tracker instance.
*/
private readonly _focusTracker;
/**
* A collection of focusable views.
*/
private readonly _focusables;
/**
* @inheritDoc
*/
constructor(locale?: Locale);
/**
* @inheritDoc
*/
render(): void;
/**
* Creates the button views based on the given definitions.
* Then adds them to the {@link #children} collection and to the focus cycler.
*/
setButtons(definitions: Array<DialogActionButtonDefinition>): void;
/**
* @inheritDoc
*/
focus(direction?: 1 | -1): void;
/**
* Adds all elements from the {@link #children} collection to the {@link #_focusables} collection
* and to the {@link #_focusTracker} instance.
*/
private _updateFocusCyclableItems;
}
/**
* A dialog action button definition. It is a slightly modified version
* of the {@link module:ui/button/button~Button} definition.
*/
export type DialogActionButtonDefinition = Pick<Button, 'label'> & Partial<Pick<Button, 'withText' | 'class' | 'icon'>> & {
onExecute: Function;
onCreate?: (button: ButtonView) => void;
};

View File

@ -0,0 +1,27 @@
/**
* @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
*/
/**
* @module ui/dialog/dialogcontentview
*/
import View from '@ckeditor/ckeditor5-ui/src/view.js';
import type ViewCollection from '@ckeditor/ckeditor5-ui/src/viewcollection.js';
import type { Locale } from '@ckeditor/ckeditor5-utils';
/**
* A dialog content view class.
*/
export default class DialogContentView extends View {
/**
* A collection of content items.
*/
readonly children: ViewCollection;
/**
* @inheritDoc
*/
constructor(locale: Locale | undefined);
/**
* Removes all the child views.
*/
reset(): void;
}

View File

@ -0,0 +1,256 @@
/**
* @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
*/
/**
* @module ui/dialog/dialogview
*/
import { KeystrokeHandler, FocusTracker, type Locale, type DecoratedMethodEvent } from '@ckeditor/ckeditor5-utils';
import ViewCollection from '@ckeditor/ckeditor5-ui/src/viewcollection.js';
import View from '@ckeditor/ckeditor5-ui/src/view.js';
import FormHeaderView from '@ckeditor/ckeditor5-ui/src/formheader/formheaderview.js';
import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview.js';
import { type DraggableView } from '@ckeditor/ckeditor5-ui/src/bindings/draggableviewmixin.js';
import DialogActionsView, { type DialogActionButtonDefinition } from '@ckeditor/ckeditor5-ui/src/dialog/dialogactionsview.js';
import DialogContentView from '@ckeditor/ckeditor5-ui/src/dialog/dialogcontentview.js';
import type EditorUI from '@ckeditor/ckeditor5-ui/src/editorui/editorui.js';
import '@ckeditor/ckeditor5-ui/theme/components/dialog/dialog.css';
/**
* Available dialog view positions:
*
* * `DialogViewPosition.SCREEN_CENTER` &ndash; A fixed position in the center of the screen.
* * `DialogViewPosition.EDITOR_CENTER` &ndash; A dynamic position in the center of the editor editable area.
* * `DialogViewPosition.EDITOR_TOP_SIDE` &ndash; A dynamic position at the top-right (for the left-to-right languages)
* or top-left (for right-to-left languages) corner of the editor editable area.
* * `DialogViewPosition.EDITOR_TOP_CENTER` &ndash; A dynamic position at the top-center of the editor editable area.
* * `DialogViewPosition.EDITOR_BOTTOM_CENTER` &ndash; A dynamic position at the bottom-center of the editor editable area.
* * `DialogViewPosition.EDITOR_ABOVE_CENTER` &ndash; A dynamic position centered above the editor editable area.
* * `DialogViewPosition.EDITOR_BELOW_CENTER` &ndash; A dynamic position centered below the editor editable area.
*
* The position of a dialog is specified by a {@link module:ui/dialog/dialog~DialogDefinition#position `position` property} of a
* definition passed to the {@link module:ui/dialog/dialog~Dialog#show} method.
*/
export declare const DialogViewPosition: {
readonly SCREEN_CENTER: "screen-center";
readonly EDITOR_CENTER: "editor-center";
readonly EDITOR_TOP_SIDE: "editor-top-side";
readonly EDITOR_TOP_CENTER: "editor-top-center";
readonly EDITOR_BOTTOM_CENTER: "editor-bottom-center";
readonly EDITOR_ABOVE_CENTER: "editor-above-center";
readonly EDITOR_BELOW_CENTER: "editor-below-center";
};
declare const DialogView_base: import("@ckeditor/ckeditor5-utils").Mixed<typeof View, DraggableView>;
/**
* A dialog view class.
*/
export default class DialogView extends DialogView_base implements DraggableView {
/**
* A collection of the child views inside of the dialog.
* A dialog can have 3 optional parts: header, content, and actions.
*/
readonly parts: ViewCollection;
/**
* A header view of the dialog. It is also a drag handle of the dialog.
*/
headerView?: FormHeaderView;
/**
* A close button view. It is automatically added to the header view if present.
*/
closeButtonView?: ButtonView;
/**
* A view with the action buttons available to the user.
*/
actionsView?: DialogActionsView;
/**
* A default dialog element offset from the reference element (e.g. editor editable area).
*/
static defaultOffset: number;
/**
* A view with the dialog content.
*/
contentView?: DialogContentView;
/**
* A keystroke handler instance.
*/
readonly keystrokes: KeystrokeHandler;
/**
* A focus tracker instance.
*/
readonly focusTracker: FocusTracker;
/**
* A flag indicating if the dialog was moved manually. If so, its position
* will not be updated automatically upon window resize or document scroll.
*/
wasMoved: boolean;
/**
* A flag indicating if this dialog view is a modal.
*
* @observable
*/
isModal: boolean;
/**
* A label for the view dialog element to be used by the assistive technologies.
*
* @observable
*/
ariaLabel: string;
/**
* A custom class name to be added to the dialog element.
*
* @observable
*/
className: string | undefined;
/**
* The position of the dialog view.
*
* @observable
*/
position: typeof DialogViewPosition[keyof typeof DialogViewPosition];
/**
* A flag indicating that the dialog should be shown. Once set to `true`, the dialog will be shown
* after its position is calculated. Until then, the dialog is transparent and not visible.
*
* See {@link #_isTransparent} property.
*
* @observable
* @internal
*/
_isVisible: boolean;
/**
* A flag indicating if a dialog is transparent. It is used to prevent the dialog from being visible
* before its position is calculated.
*
* @observable
* @internal
*/
_isTransparent: boolean;
/**
* The calculated dialog `top` CSS property used for positioning.
*
* @observable
* @internal
*/
_top: number;
/**
* The calculated dialog `left` CSS property used for positioning.
*
* @observable
* @internal
*/
_left: number;
/**
* A callback returning the DOM root that requested the dialog.
*/
private _getCurrentDomRoot;
/**
* A callback returning the configured editor viewport offset.
*/
private _getViewportOffset;
/**
* The list of the focusable elements inside the dialog view.
*/
private readonly _focusables;
/**
* The focus cycler instance.
*/
private readonly _focusCycler;
/**
* @inheritDoc
*/
constructor(locale: Locale, { getCurrentDomRoot, getViewportOffset }: {
getCurrentDomRoot: () => HTMLElement;
getViewportOffset: () => EditorUI['viewportOffset'];
});
/**
* @inheritDoc
*/
render(): void;
/**
* Returns the element that should be used as a drag handle.
*/
get dragHandleElement(): HTMLElement | null;
/**
* Creates the dialog parts. Which of them are created depends on the arguments passed to the method.
* There are no rules regarding the dialog construction, that is, no part is mandatory.
* Each part can only be created once.
*
* @internal
*/
setupParts({ icon, title, hasCloseButton, content, actionButtons }: {
icon?: string;
title?: string;
hasCloseButton?: boolean;
content?: View | Array<View>;
actionButtons?: Array<DialogActionButtonDefinition>;
}): void;
/**
* Focuses the first focusable element inside the dialog.
*/
focus(): void;
/**
* Normalizes the passed coordinates to make sure the dialog view
* is displayed within the visible viewport and moves it there.
*
* @internal
*/
moveTo(left: number, top: number): void;
/**
* Moves the dialog to the specified coordinates.
*/
private _moveTo;
/**
* Moves the dialog by the specified offset.
*
* @internal
*/
moveBy(left: number, top: number): void;
/**
* Moves the dialog view to the off-screen position.
* Used when there is no space to display the dialog.
*/
private _moveOffScreen;
/**
* Recalculates the dialog according to the set position and viewport,
* and moves it to the new position.
*/
updatePosition(): void;
/**
* Calculates the visible DOM root part.
*/
private _getVisibleDomRootRect;
/**
* Calculates the dialog element rect.
*/
private _getDialogRect;
/**
* Calculates the viewport rect.
*/
private _getViewportRect;
/**
* Collects all focusable elements inside the dialog parts
* and adds them to the focus tracker and focus cycler.
*/
private _updateFocusCyclableItems;
/**
* Creates the close button view that is displayed in the header view corner.
*/
private _createCloseButton;
}
/**
* An event fired when the dialog is closed.
*
* @eventName ~DialogView#close
*/
export type DialogViewCloseEvent = {
name: 'close';
args: [{
source: 'closeButton' | 'escKeyPress';
}];
};
/**
* An event fired when the dialog is moved.
*
* @eventName ~DialogView#moveTo
*/
export type DialogViewMoveToEvent = DecoratedMethodEvent<DialogView, 'moveTo'>;
export {};

View File

@ -0,0 +1,25 @@
/**
* @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
*/
/**
* @module ui/dropdown/button/dropdownbutton
*/
import type Button from '@ckeditor/ckeditor5-ui/src/button/button.js';
import type ViewCollection from '@ckeditor/ckeditor5-ui/src/viewcollection.js';
/**
* The dropdown button interface.
*/
export default interface DropdownButton extends Button {
children: ViewCollection;
}
/**
* Fired when the dropdown should be opened.
* It will not be fired when the button {@link module:ui/dropdown/button/dropdownbutton~DropdownButton#isEnabled is disabled}.
*
* @eventName ~DropdownButton#open
*/
export type DropdownButtonOpenEvent = {
name: 'open';
args: [];
};

View File

@ -0,0 +1,48 @@
/**
* @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
*/
/**
* @module ui/dropdown/button/dropdownbuttonview
*/
import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview.js';
import type DropdownButton from '@ckeditor/ckeditor5-ui/src/dropdown/button/dropdownbutton.js';
import IconView from '@ckeditor/ckeditor5-ui/src/icon/iconview.js';
import type { Locale } from '@ckeditor/ckeditor5-utils';
/**
* The default dropdown button view class.
*
* ```ts
* const view = new DropdownButtonView();
*
* view.set( {
* label: 'A button',
* keystroke: 'Ctrl+B',
* tooltip: true
* } );
*
* view.render();
*
* document.body.append( view.element );
* ```
*
* Also see the {@link module:ui/dropdown/utils~createDropdown `createDropdown()` util}.
*/
export default class DropdownButtonView extends ButtonView implements DropdownButton {
/**
* An icon that displays arrow to indicate a dropdown button.
*/
readonly arrowView: IconView;
/**
* @inheritDoc
*/
constructor(locale?: Locale);
/**
* @inheritDoc
*/
render(): void;
/**
* Creates a {@link module:ui/icon/iconview~IconView} instance as {@link #arrowView}.
*/
private _createArrowView;
}

View File

@ -0,0 +1,162 @@
/**
* @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
*/
/**
* @module ui/dropdown/button/splitbuttonview
*/
import View from '@ckeditor/ckeditor5-ui/src/view.js';
import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview.js';
import type ViewCollection from '@ckeditor/ckeditor5-ui/src/viewcollection.js';
import type Button from '@ckeditor/ckeditor5-ui/src/button/button.js';
import type DropdownButton from '@ckeditor/ckeditor5-ui/src/dropdown/button/dropdownbutton.js';
import type { FocusableView } from '@ckeditor/ckeditor5-ui/src/focuscycler.js';
import { KeystrokeHandler, FocusTracker, type Locale } from '@ckeditor/ckeditor5-utils';
import '@ckeditor/ckeditor5-ui/theme/components/dropdown/splitbutton.css';
/**
* The split button view class.
*
* ```ts
* const view = new SplitButtonView();
*
* view.set( {
* label: 'A button',
* keystroke: 'Ctrl+B',
* tooltip: true
* } );
*
* view.render();
*
* document.body.append( view.element );
* ```
*
* Also see the {@link module:ui/dropdown/utils~createDropdown `createDropdown()` util}.
*/
export default class SplitButtonView extends View<HTMLDivElement> implements DropdownButton {
/**
* Collection of the child views inside of the split button {@link #element}.
*/
readonly children: ViewCollection;
/**
* A main button of split button.
*/
readonly actionView: ButtonView;
/**
* A secondary button of split button that opens dropdown.
*/
readonly arrowView: ButtonView;
/**
* Instance of the {@link module:utils/keystrokehandler~KeystrokeHandler}. It manages
* keystrokes of the split button:
*
* * <kbd></kbd> moves focus to arrow view when action view is focused,
* * <kbd></kbd> moves focus to action view when arrow view is focused.
*/
readonly keystrokes: KeystrokeHandler;
/**
* Tracks information about DOM focus in the dropdown.
*/
readonly focusTracker: FocusTracker;
/**
* @inheritDoc
*/
label: string | undefined;
/**
* @inheritDoc
*/
keystroke: string | undefined;
/**
* @inheritDoc
*/
tooltip: Button['tooltip'];
/**
* @inheritDoc
*/
tooltipPosition: Button['tooltipPosition'];
/**
* @inheritDoc
*/
type: Button['type'];
/**
* @inheritDoc
*/
isOn: boolean;
/**
* @inheritDoc
*/
isEnabled: boolean;
/**
* @inheritDoc
*/
isVisible: boolean;
/**
* @inheritDoc
*/
isToggleable: boolean;
/**
* @inheritDoc
*/
withText: boolean;
/**
* @inheritDoc
*/
withKeystroke: boolean;
/**
* @inheritDoc
*/
icon: string | undefined;
/**
* @inheritDoc
*/
tabindex: number;
/**
* @inheritDoc
*/
class: string | undefined;
/**
* @inheritDoc
*/
labelStyle: string | undefined;
/**
* @inheritDoc
*/
role: string | undefined;
/**
* @inheritDoc
*/
ariaChecked: boolean | undefined;
/**
* @inheritDoc
*/
ariaLabel?: string | undefined;
/**
* @inheritDoc
*/
ariaLabelledBy: string | undefined;
/**
* @inheritDoc
*/
constructor(locale?: Locale, actionButton?: ButtonView & FocusableView);
/**
* @inheritDoc
*/
render(): void;
/**
* @inheritDoc
*/
destroy(): void;
/**
* Focuses the {@link module:ui/button/buttonview~ButtonView#element} of the action part of split button.
*/
focus(): void;
/**
* Creates a {@link module:ui/button/buttonview~ButtonView} instance as {@link #actionView} and binds it with main split button
* attributes.
*/
private _createActionView;
/**
* Creates a {@link module:ui/button/buttonview~ButtonView} instance as {@link #arrowView} and binds it with main split button
* attributes.
*/
private _createArrowView;
}

View File

@ -0,0 +1,62 @@
/**
* @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
*/
/**
* @module ui/dropdown/dropdownpanelview
*/
import View from '@ckeditor/ckeditor5-ui/src/view.js';
import type ViewCollection from '@ckeditor/ckeditor5-ui/src/viewcollection.js';
import type DropdownPanelFocusable from '@ckeditor/ckeditor5-ui/src/dropdown/dropdownpanelfocusable.js';
import { type Locale } from '@ckeditor/ckeditor5-utils';
/**
* The dropdown panel view class.
*
* See {@link module:ui/dropdown/dropdownview~DropdownView} to learn about the common usage.
*/
export default class DropdownPanelView extends View implements DropdownPanelFocusable {
/**
* Collection of the child views in this panel.
*
* A common child type is the {@link module:ui/list/listview~ListView} and {@link module:ui/toolbar/toolbarview~ToolbarView}.
* See {@link module:ui/dropdown/utils~addListToDropdown} and
* {@link module:ui/dropdown/utils~addToolbarToDropdown} to learn more about child views of dropdowns.
*/
readonly children: ViewCollection;
/**
* Controls whether the panel is visible.
*
* @observable
*/
isVisible: boolean;
/**
* The position of the panel, relative to the parent.
*
* This property is reflected in the CSS class set to {@link #element} that controls
* the position of the panel.
*
* @observable
* @default 'se'
*/
position: PanelPosition;
/**
* @inheritDoc
*/
constructor(locale?: Locale);
/**
* Focuses the first view in the {@link #children} collection.
*
* See also {@link module:ui/dropdown/dropdownpanelfocusable~DropdownPanelFocusable}.
*/
focus(): void;
/**
* Focuses the view element or last item in view collection on opening dropdown's panel.
*
* See also {@link module:ui/dropdown/dropdownpanelfocusable~DropdownPanelFocusable}.
*/
focusLast(): void;
}
/**
* The position of the panel, relative to the parent.
*/
export type PanelPosition = 's' | 'se' | 'sw' | 'sme' | 'smw' | 'n' | 'ne' | 'nw' | 'nme' | 'nmw';

View File

@ -0,0 +1,315 @@
/**
* @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
*/
/**
* @module ui/dropdown/dropdownview
*/
import View from '@ckeditor/ckeditor5-ui/src/view.js';
import type { default as DropdownButton } from '@ckeditor/ckeditor5-ui/src/dropdown/button/dropdownbutton.js';
import type { default as DropdownPanelView, PanelPosition } from '@ckeditor/ckeditor5-ui/src/dropdown/dropdownpanelview.js';
import type { FocusableView } from '@ckeditor/ckeditor5-ui/src/focuscycler.js';
import type ListView from '@ckeditor/ckeditor5-ui/src/list/listview.js';
import type ToolbarView from '@ckeditor/ckeditor5-ui/src/toolbar/toolbarview.js';
import { KeystrokeHandler, FocusTracker, type Locale, type PositioningFunction } from '@ckeditor/ckeditor5-utils';
import '@ckeditor/ckeditor5-ui/theme/components/dropdown/dropdown.css';
/**
* The dropdown view class. It manages the dropdown button and dropdown panel.
*
* In most cases, the easiest way to create a dropdown is by using the {@link module:ui/dropdown/utils~createDropdown}
* util:
*
* ```ts
* const dropdown = createDropdown( locale );
*
* // Configure dropdown's button properties:
* dropdown.buttonView.set( {
* label: 'A dropdown',
* withText: true
* } );
*
* dropdown.render();
*
* dropdown.panelView.element.textContent = 'Content of the panel';
*
* // Will render a dropdown with a panel containing a "Content of the panel" text.
* document.body.appendChild( dropdown.element );
* ```
*
* If you want to add a richer content to the dropdown panel, you can use the {@link module:ui/dropdown/utils~addListToDropdown}
* and {@link module:ui/dropdown/utils~addToolbarToDropdown} helpers. See more examples in
* {@link module:ui/dropdown/utils~createDropdown} documentation.
*
* If you want to create a completely custom dropdown, then you can compose it manually:
*
* ```ts
* const button = new DropdownButtonView( locale );
* const panel = new DropdownPanelView( locale );
* const dropdown = new DropdownView( locale, button, panel );
*
* button.set( {
* label: 'A dropdown',
* withText: true
* } );
*
* dropdown.render();
*
* panel.element.textContent = 'Content of the panel';
*
* // Will render a dropdown with a panel containing a "Content of the panel" text.
* document.body.appendChild( dropdown.element );
* ```
*
* However, dropdown created this way will contain little behavior. You will need to implement handlers for actions
* such as {@link module:ui/bindings/clickoutsidehandler~clickOutsideHandler clicking outside an open dropdown}
* (which should close it) and support for arrow keys inside the panel. Therefore, unless you really know what
* you do and you really need to do it, it is recommended to use the {@link module:ui/dropdown/utils~createDropdown} helper.
*/
export default class DropdownView extends View<HTMLDivElement> {
/**
* Button of the dropdown view. Clicking the button opens the {@link #panelView}.
*/
readonly buttonView: DropdownButton & FocusableView;
/**
* Panel of the dropdown. It opens when the {@link #buttonView} is
* {@link module:ui/button/button~Button#event:execute executed} (i.e. clicked).
*
* Child views can be added to the panel's `children` collection:
*
* ```ts
* dropdown.panelView.children.add( childView );
* ```
*
* See {@link module:ui/dropdown/dropdownpanelview~DropdownPanelView#children} and
* {@link module:ui/viewcollection~ViewCollection#add}.
*/
readonly panelView: DropdownPanelView;
/**
* Tracks information about the DOM focus in the dropdown.
*/
readonly focusTracker: FocusTracker;
/**
* Instance of the {@link module:utils/keystrokehandler~KeystrokeHandler}. It manages
* keystrokes of the dropdown:
*
* * <kbd></kbd> opens the dropdown,
* * <kbd></kbd> and <kbd>Esc</kbd> closes the dropdown.
*/
readonly keystrokes: KeystrokeHandler;
/**
* A child {@link module:ui/list/listview~ListView list view} of the dropdown located
* in its {@link module:ui/dropdown/dropdownview~DropdownView#panelView panel}.
*
* **Note**: Only supported when dropdown has list view added using {@link module:ui/dropdown/utils~addListToDropdown}.
*/
listView?: ListView;
/**
* A child toolbar of the dropdown located in the
* {@link module:ui/dropdown/dropdownview~DropdownView#panelView panel}.
*
* **Note**: Only supported when dropdown has list view added using {@link module:ui/dropdown/utils~addToolbarToDropdown}.
*/
toolbarView?: ToolbarView;
/**
* Controls whether the dropdown view is open, i.e. shows or hides the {@link #panelView panel}.
*
* **Note**: When the dropdown gets open, it will attempt to call `focus()` on the first child of its {@link #panelView}.
* See {@link module:ui/dropdown/utils~addToolbarToDropdown}, {@link module:ui/dropdown/utils~addListToDropdown}, and
* {@link module:ui/dropdown/utils~focusChildOnDropdownOpen} to learn more about focus management in dropdowns.
*
* @observable
*/
isOpen: boolean;
/**
* Controls whether the dropdown is enabled, i.e. it can be clicked and execute an action.
*
* See {@link module:ui/button/buttonview~ButtonView#isEnabled}.
*
* @observable
*/
isEnabled: boolean;
/**
* (Optional) The additional CSS class set on the dropdown {@link #element}.
*
* @observable
*/
class: string | undefined;
/**
* (Optional) The `id` attribute of the dropdown (i.e. to pair with a `<label>` element).
*
* @observable
*/
id: string | undefined;
/**
* The position of the panel, relative to the dropdown.
*
* **Note**: When `'auto'`, the panel will use one of the remaining positions to stay
* in the viewport, visible to the user. The positions correspond directly to
* {@link module:ui/dropdown/dropdownview~DropdownView.defaultPanelPositions default panel positions}.
*
* **Note**: This value has an impact on the
* {@link module:ui/dropdown/dropdownpanelview~DropdownPanelView#position} property
* each time the panel becomes {@link #isOpen open}.
*
* @observable
* @default 'auto'
*/
panelPosition: PanelPosition | 'auto';
/**
* @observable
*/
ariaDescribedById: string | undefined;
/**
* Creates an instance of the dropdown.
*
* Also see {@link #render}.
*
* @param locale The localization services instance.
*/
constructor(locale: Locale | undefined, buttonView: DropdownButton & FocusableView, panelView: DropdownPanelView);
/**
* @inheritDoc
*/
render(): void;
/**
* Focuses the {@link #buttonView}.
*/
focus(): void;
/**
* Returns {@link #panelView panel} positions to be used by the
* {@link module:utils/dom/position~getOptimalPosition `getOptimalPosition()`}
* utility considering the direction of the language the UI of the editor is displayed in.
*/
private get _panelPositions();
/**
* A set of positioning functions used by the dropdown view to determine
* the optimal position (i.e. fitting into the browser viewport) of its
* {@link module:ui/dropdown/dropdownview~DropdownView#panelView panel} when
* {@link module:ui/dropdown/dropdownview~DropdownView#panelPosition} is set to 'auto'`.
*
* The available positioning functions are as follow:
*
* **South**
*
* * `south`
*
* ```
* [ Button ]
* +-----------------+
* | Panel |
* +-----------------+
* ```
*
* * `southEast`
*
* ```
* [ Button ]
* +-----------------+
* | Panel |
* +-----------------+
* ```
*
* * `southWest`
*
* ```
* [ Button ]
* +-----------------+
* | Panel |
* +-----------------+
* ```
*
* * `southMiddleEast`
*
* ```
* [ Button ]
* +-----------------+
* | Panel |
* +-----------------+
* ```
*
* * `southMiddleWest`
*
* ```
* [ Button ]
* +-----------------+
* | Panel |
* +-----------------+
* ```
*
* **North**
*
* * `north`
*
* ```
* +-----------------+
* | Panel |
* +-----------------+
* [ Button ]
* ```
*
* * `northEast`
*
* ```
* +-----------------+
* | Panel |
* +-----------------+
* [ Button ]
* ```
*
* * `northWest`
*
* ```
* +-----------------+
* | Panel |
* +-----------------+
* [ Button ]
* ```
*
* * `northMiddleEast`
*
* ```
* +-----------------+
* | Panel |
* +-----------------+
* [ Button ]
* ```
*
* * `northMiddleWest`
*
* ```
* +-----------------+
* | Panel |
* +-----------------+
* [ Button ]
* ```
*
* Positioning functions are compatible with {@link module:utils/dom/position~DomPoint}.
*
* The name that position function returns will be reflected in dropdown panel's class that
* controls its placement. See {@link module:ui/dropdown/dropdownview~DropdownView#panelPosition}
* to learn more.
*/
static defaultPanelPositions: Record<string, PositioningFunction>;
/**
* A function used to calculate the optimal position for the dropdown panel.
*/
private static _getOptimalPosition;
}
/**
* Fired when the toolbar button or list item is executed.
*
* For {@link ~DropdownView#listView} It fires when a child of some {@link module:ui/list/listitemview~ListItemView}
* fired `execute`.
*
* For {@link ~DropdownView#toolbarView} It fires when one of the buttons has been
* {@link module:ui/button/button~Button#event:execute executed}.
*
* **Note**: Only supported when dropdown has list view added using {@link module:ui/dropdown/utils~addListToDropdown}
* or {@link module:ui/dropdown/utils~addToolbarToDropdown}.
*
* @eventName ~DropdownView#execute
*/
export type DropdownViewEvent = {
name: 'execute';
args: [];
};

View File

@ -0,0 +1,235 @@
/**
* @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 DropdownView from '@ckeditor/ckeditor5-ui/src/dropdown/dropdownview.js';
import ViewCollection from '@ckeditor/ckeditor5-ui/src/viewcollection.js';
import type { default as View } from '@ckeditor/ckeditor5-ui/src/view.js';
import type Model from '@ckeditor/ckeditor5-ui/src/model.js';
import type DropdownButton from '@ckeditor/ckeditor5-ui/src/dropdown/button/dropdownbutton.js';
import type { FocusableView } from '@ckeditor/ckeditor5-ui/src/focuscycler.js';
import type { FalsyValue } from '@ckeditor/ckeditor5-ui/src/template.js';
import { type Collection, type Locale } from '@ckeditor/ckeditor5-utils';
import '@ckeditor/ckeditor5-ui/theme/components/dropdown/toolbardropdown.css';
import '@ckeditor/ckeditor5-ui/theme/components/dropdown/listdropdown.css';
/**
* A helper for creating dropdowns. It creates an instance of a {@link module:ui/dropdown/dropdownview~DropdownView dropdown},
* with a {@link module:ui/dropdown/button/dropdownbutton~DropdownButton button},
* {@link module:ui/dropdown/dropdownpanelview~DropdownPanelView panel} and all standard dropdown's behaviors.
*
* # Creating dropdowns
*
* By default, the default {@link module:ui/dropdown/button/dropdownbuttonview~DropdownButtonView} class is used as
* definition of the button:
*
* ```ts
* const dropdown = createDropdown( model );
*
* // Configure dropdown's button properties:
* dropdown.buttonView.set( {
* label: 'A dropdown',
* withText: true
* } );
*
* dropdown.render();
*
* // Will render a dropdown labeled "A dropdown" with an empty panel.
* document.body.appendChild( dropdown.element );
* ```
*
* You can also provide other button views (they need to implement the
* {@link module:ui/dropdown/button/dropdownbutton~DropdownButton} interface). For instance, you can use
* {@link module:ui/dropdown/button/splitbuttonview~SplitButtonView} to create a dropdown with a split button.
*
* ```ts
* const dropdown = createDropdown( locale, SplitButtonView );
*
* // Configure dropdown's button properties:
* dropdown.buttonView.set( {
* label: 'A dropdown',
* withText: true
* } );
*
* dropdown.buttonView.on( 'execute', () => {
* // Add the behavior of the "action part" of the split button.
* // Split button consists of the "action part" and "arrow part".
* // The arrow opens the dropdown while the action part can have some other behavior.
* } );
*
* dropdown.render();
*
* // Will render a dropdown labeled "A dropdown" with an empty panel.
* document.body.appendChild( dropdown.element );
* ```
*
* # Adding content to the dropdown's panel
*
* The content of the panel can be inserted directly into the `dropdown.panelView.element`:
*
* ```ts
* dropdown.panelView.element.textContent = 'Content of the panel';
* ```
*
* However, most of the time you will want to add there either a {@link module:ui/list/listview~ListView list of options}
* or a list of buttons (i.e. a {@link module:ui/toolbar/toolbarview~ToolbarView toolbar}).
* To simplify the task, you can use, respectively, {@link module:ui/dropdown/utils~addListToDropdown} or
* {@link module:ui/dropdown/utils~addToolbarToDropdown} utils.
*
* @param locale The locale instance.
* @param ButtonClassOrInstance The dropdown button view class. Needs to implement the
* {@link module:ui/dropdown/button/dropdownbutton~DropdownButton} interface.
* @returns The dropdown view instance.
*/
export declare function createDropdown(locale: Locale | undefined, ButtonClassOrInstance?: (new (locale?: Locale) => DropdownButton & FocusableView) | (DropdownButton & FocusableView)): DropdownView;
/**
* Adds an instance of {@link module:ui/toolbar/toolbarview~ToolbarView} to a dropdown.
*
* ```ts
* const buttonsCreator = () => {
* const buttons = [];
*
* // Either create a new ButtonView instance or create existing.
* buttons.push( new ButtonView() );
* buttons.push( editor.ui.componentFactory.create( 'someButton' ) );
* };
*
* const dropdown = createDropdown( locale );
*
* addToolbarToDropdown( dropdown, buttonsCreator, { isVertical: true } );
*
* // Will render a vertical button dropdown labeled "A button dropdown"
* // with a button group in the panel containing two buttons.
* // Buttons inside the dropdown will be created on first dropdown panel open.
* dropdown.render()
* document.body.appendChild( dropdown.element );
* ```
*
* **Note:** To improve the accessibility, you can tell the dropdown to focus the first active button of the toolbar when the dropdown
* {@link module:ui/dropdown/dropdownview~DropdownView#isOpen gets open}. See the documentation of `options` to learn more.
*
* **Note:** Toolbar view will be created on first open of the dropdown.
*
* See {@link module:ui/dropdown/utils~createDropdown} and {@link module:ui/toolbar/toolbarview~ToolbarView}.
*
* @param dropdownView A dropdown instance to which `ToolbarView` will be added.
* @param options.enableActiveItemFocusOnDropdownOpen When set `true`, the focus will automatically move to the first
* active {@link module:ui/toolbar/toolbarview~ToolbarView#items item} of the toolbar upon
* {@link module:ui/dropdown/dropdownview~DropdownView#isOpen opening} the dropdown. Active items are those with the `isOn` property set
* `true` (for instance {@link module:ui/button/buttonview~ButtonView buttons}). If no active items is found, the toolbar will be focused
* as a whole resulting in the focus moving to its first focusable item (default behavior of
* {@link module:ui/dropdown/dropdownview~DropdownView}).
* @param options.ariaLabel Label used by assistive technologies to describe toolbar element.
* @param options.maxWidth The maximum width of the toolbar element.
* Details: {@link module:ui/toolbar/toolbarview~ToolbarView#maxWidth}.
* @param options.class An additional CSS class added to the toolbar element.
* @param options.isCompact When set true, makes the toolbar look compact with toolbar element.
* @param options.isVertical Controls the orientation of toolbar items.
*/
export declare function addToolbarToDropdown(dropdownView: DropdownView, buttonsOrCallback: Array<View> | ViewCollection | (() => Array<View> | ViewCollection), options?: {
enableActiveItemFocusOnDropdownOpen?: boolean;
ariaLabel?: string;
maxWidth?: string;
class?: string;
isCompact?: boolean;
isVertical?: boolean;
}): void;
/**
* Adds an instance of {@link module:ui/list/listview~ListView} to a dropdown.
*
* ```ts
* const items = new Collection<ListDropdownItemDefinition>();
*
* items.add( {
* type: 'button',
* model: new Model( {
* withText: true,
* label: 'First item',
* labelStyle: 'color: red'
* } )
* } );
*
* items.add( {
* type: 'button',
* model: new Model( {
* withText: true,
* label: 'Second item',
* labelStyle: 'color: green',
* class: 'foo'
* } )
* } );
*
* const dropdown = createDropdown( locale );
*
* addListToDropdown( dropdown, items );
*
* // Will render a dropdown with a list in the panel containing two items.
* dropdown.render()
* document.body.appendChild( dropdown.element );
* ```
*
* The `items` collection passed to this methods controls the presence and attributes of respective
* {@link module:ui/list/listitemview~ListItemView list items}.
*
* **Note:** To improve the accessibility, when a list is added to the dropdown using this helper the dropdown will automatically attempt
* to focus the first active item (a host to a {@link module:ui/button/buttonview~ButtonView} with
* {@link module:ui/button/buttonview~ButtonView#isOn} set `true`) or the very first item when none are active.
*
* **Note:** List view will be created on first open of the dropdown.
*
* See {@link module:ui/dropdown/utils~createDropdown} and {@link module:list/list~List}.
*
* @param dropdownView A dropdown instance to which `ListVIew` will be added.
* @param itemsOrCallback A collection of the list item definitions or a callback returning a list item definitions to populate the list.
* @param options.ariaLabel Label used by assistive technologies to describe list element.
* @param options.role Will be reflected by the `role` DOM attribute in `ListVIew` and used by assistive technologies.
*/
export declare function addListToDropdown(dropdownView: DropdownView, itemsOrCallback: Collection<ListDropdownItemDefinition> | (() => Collection<ListDropdownItemDefinition>), options?: {
ariaLabel?: string;
role?: string;
}): void;
/**
* A helper to be used on an existing {@link module:ui/dropdown/dropdownview~DropdownView} that focuses
* a specific child in DOM when the dropdown {@link module:ui/dropdown/dropdownview~DropdownView#isOpen gets open}.
*
* @param dropdownView A dropdown instance to which the focus behavior will be added.
* @param childSelectorCallback A callback executed when the dropdown gets open. It should return a {@link module:ui/view~View}
* instance (child of {@link module:ui/dropdown/dropdownview~DropdownView#panelView}) that will get focused or a falsy value.
* If falsy value is returned, a default behavior of the dropdown will engage focusing the first focusable child in
* the {@link module:ui/dropdown/dropdownview~DropdownView#panelView}.
*/
export declare function focusChildOnDropdownOpen(dropdownView: DropdownView, childSelectorCallback: () => View | FalsyValue): void;
/**
* A definition of the list item used by the {@link module:ui/dropdown/utils~addListToDropdown}
* utility.
*/
export type ListDropdownItemDefinition = ListDropdownSeparatorDefinition | ListDropdownButtonDefinition | ListDropdownGroupDefinition;
/**
* A definition of the 'separator' list item.
*/
export type ListDropdownSeparatorDefinition = {
type: 'separator';
};
/**
* A definition of the 'button' or 'switchbutton' list item.
*/
export type ListDropdownButtonDefinition = {
type: 'button' | 'switchbutton';
/**
* Model of the item. Its properties fuel the newly created list item (or its children, depending on the `type`).
*/
model: Model;
};
/**
* A definition of the group inside the list. A group can contain one or more list items (buttons).
*/
export type ListDropdownGroupDefinition = {
type: 'group';
/**
* The visible label of the group.
*/
label: string;
/**
* The collection of the child list items inside this group.
*/
items: Collection<ListDropdownButtonDefinition>;
};

View File

@ -0,0 +1,72 @@
/**
* @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
*/
/**
* @module ui/editableui/editableuiview
*/
import View from '@ckeditor/ckeditor5-ui/src/view.js';
import type { EditingView } from '@ckeditor/ckeditor5-engine';
import type { Locale } from '@ckeditor/ckeditor5-utils';
/**
* The editable UI view class.
*/
export default class EditableUIView extends View {
/**
* The name of the editable UI view.
*/
name: string | null;
/**
* Controls whether the editable is focused, i.e. the user is typing in it.
*
* @observable
*/
isFocused: boolean;
/**
* The editing view instance the editable is related to. Editable uses the editing
* view to dynamically modify its certain DOM attributes after {@link #render rendering}.
*
* **Note**: The DOM attributes are performed by the editing view and not UI
* {@link module:ui/view~View#bindTemplate template bindings} because once rendered,
* the editable DOM element must remain under the full control of the engine to work properly.
*/
protected _editingView: EditingView;
/**
* The element which is the main editable element (usually the one with `contentEditable="true"`).
*/
private _editableElement;
/**
* Whether an external {@link #_editableElement} was passed into the constructor, which also means
* the view will not render its {@link #template}.
*/
private _hasExternalElement;
/**
* Creates an instance of EditableUIView class.
*
* @param locale The locale instance.
* @param editingView The editing view instance the editable is related to.
* @param editableElement The editable element. If not specified, this view
* should create it. Otherwise, the existing element should be used.
*/
constructor(locale: Locale, editingView: EditingView, editableElement?: HTMLElement);
/**
* Renders the view by either applying the {@link #template} to the existing
* {@link module:ui/editableui/editableuiview~EditableUIView#_editableElement} or assigning {@link #element}
* as {@link module:ui/editableui/editableuiview~EditableUIView#_editableElement}.
*/
render(): void;
/**
* @inheritDoc
*/
destroy(): void;
/**
* Whether an external {@link #_editableElement} was passed into the constructor, which also means
* the view will not render its {@link #template}.
*/
get hasExternalElement(): boolean;
/**
* Updates the `ck-focused` and `ck-blurred` CSS classes on the {@link #element} according to
* the {@link #isFocused} property value using the {@link #_editingView editing view} API.
*/
private _updateIsFocusedClasses;
}

View File

@ -0,0 +1,40 @@
/**
* @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
*/
/**
* @module ui/editableui/inline/inlineeditableuiview
*/
import EditableUIView from '@ckeditor/ckeditor5-ui/src/editableui/editableuiview.js';
import type { EditingView } from '@ckeditor/ckeditor5-engine';
import type { Locale } from '@ckeditor/ckeditor5-utils';
/**
* The inline editable UI class implementing an inline {@link module:ui/editableui/editableuiview~EditableUIView}.
*/
export default class InlineEditableUIView extends EditableUIView {
/**
* A function that gets called with the instance of this view as an argument and should return a string that
* represents the label of the editable for assistive technologies.
*/
private readonly _generateLabel;
/**
* Creates an instance of the InlineEditableUIView class.
*
* @param locale The locale instance.
* @param editingView The editing view instance the editable is related to.
* @param editableElement The editable element. If not specified, the
* {@link module:ui/editableui/editableuiview~EditableUIView}
* will create it. Otherwise, the existing element will be used.
* @param options Additional configuration of the view.
* @param options.label A function that gets called with the instance of this view as an argument
* and should return a string that represents the label of the editable for assistive technologies. If not provided,
* a default label generator is used.
*/
constructor(locale: Locale, editingView: EditingView, editableElement?: HTMLElement, options?: {
label?: (view: InlineEditableUIView) => string;
});
/**
* @inheritDoc
*/
render(): void;
}

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