diff --git a/app/editor/components/SelectionToolbar.tsx b/app/editor/components/SelectionToolbar.tsx
index e27290fe7b..9f28eb1a90 100644
--- a/app/editor/components/SelectionToolbar.tsx
+++ b/app/editor/components/SelectionToolbar.tsx
@@ -1,5 +1,6 @@
import some from "lodash/some";
import { EditorState, NodeSelection, TextSelection } from "prosemirror-state";
+import { CellSelection } from "prosemirror-tables";
import * as React from "react";
import filterExcessSeparators from "@shared/editor/lib/filterExcessSeparators";
import { getMarkRange } from "@shared/editor/queries/getMarkRange";
@@ -22,6 +23,7 @@ import getImageMenuItems from "../menus/image";
import getNoticeMenuItems from "../menus/notice";
import getReadOnlyMenuItems from "../menus/readOnly";
import getTableMenuItems from "../menus/table";
+import getTableCellMenuItems from "../menus/tableCell";
import getTableColMenuItems from "../menus/tableCol";
import getTableRowMenuItems from "../menus/tableRow";
import { useEditor } from "./EditorContext";
@@ -183,6 +185,7 @@ export default function SelectionToolbar(props: Props) {
const colIndex = getColumnIndex(state);
const rowIndex = getRowIndex(state);
const isTableSelection = colIndex !== undefined && rowIndex !== undefined;
+ const isCellSelection = selection instanceof CellSelection;
const link = getMarkRange(selection.$from, state.schema.marks.link);
const isImageSelection =
selection instanceof NodeSelection && selection.node.type.name === "image";
@@ -202,6 +205,8 @@ export default function SelectionToolbar(props: Props) {
items = getTableColMenuItems(state, colIndex, rtl, dictionary);
} else if (rowIndex !== undefined) {
items = getTableRowMenuItems(state, rowIndex, dictionary);
+ } else if (isCellSelection) {
+ items = getTableCellMenuItems(state, dictionary);
} else if (isImageSelection) {
items = readOnly ? [] : getImageMenuItems(state, dictionary);
} else if (isAttachmentSelection) {
diff --git a/app/editor/menus/tableCell.tsx b/app/editor/menus/tableCell.tsx
new file mode 100644
index 0000000000..d55b321002
--- /dev/null
+++ b/app/editor/menus/tableCell.tsx
@@ -0,0 +1,36 @@
+import { TableSplitCellsIcon, TableMergeCellsIcon } from "outline-icons";
+import { EditorState } from "prosemirror-state";
+import { CellSelection } from "prosemirror-tables";
+import {
+ isMergedCellSelection,
+ isMultipleCellSelection,
+} from "@shared/editor/queries/table";
+import { MenuItem } from "@shared/editor/types";
+import { Dictionary } from "~/hooks/useDictionary";
+
+export default function tableCellMenuItems(
+ state: EditorState,
+ dictionary: Dictionary
+): MenuItem[] {
+ const { selection } = state;
+
+ // Only show menu items if we have a CellSelection
+ if (!(selection instanceof CellSelection)) {
+ return [];
+ }
+
+ return [
+ {
+ name: "mergeCells",
+ label: dictionary.mergeCells,
+ icon: ,
+ visible: isMultipleCellSelection(state),
+ },
+ {
+ name: "splitCell",
+ label: dictionary.splitCell,
+ icon: ,
+ visible: isMergedCellSelection(state),
+ },
+ ];
+}
diff --git a/app/editor/menus/tableCol.tsx b/app/editor/menus/tableCol.tsx
index 25a31aefc5..ff7ead10de 100644
--- a/app/editor/menus/tableCol.tsx
+++ b/app/editor/menus/tableCol.tsx
@@ -8,10 +8,17 @@ import {
ArrowIcon,
MoreIcon,
TableHeaderColumnIcon,
+ TableMergeCellsIcon,
+ TableSplitCellsIcon,
} from "outline-icons";
import { EditorState } from "prosemirror-state";
+import { CellSelection } from "prosemirror-tables";
import styled from "styled-components";
import { isNodeActive } from "@shared/editor/queries/isNodeActive";
+import {
+ isMergedCellSelection,
+ isMultipleCellSelection,
+} from "@shared/editor/queries/table";
import { MenuItem } from "@shared/editor/types";
import { Dictionary } from "~/hooks/useDictionary";
@@ -21,7 +28,11 @@ export default function tableColMenuItems(
rtl: boolean,
dictionary: Dictionary
): MenuItem[] {
- const { schema } = state;
+ const { schema, selection } = state;
+
+ if (!(selection instanceof CellSelection)) {
+ return [];
+ }
return [
{
@@ -96,6 +107,21 @@ export default function tableColMenuItems(
icon: ,
attrs: { index },
},
+ {
+ name: "mergeCells",
+ label: dictionary.mergeCells,
+ icon: ,
+ visible: isMultipleCellSelection(state),
+ },
+ {
+ name: "splitCell",
+ label: dictionary.splitCell,
+ icon: ,
+ visible: isMergedCellSelection(state),
+ },
+ {
+ name: "separator",
+ },
{
name: "deleteColumn",
dangerous: true,
diff --git a/app/editor/menus/tableRow.tsx b/app/editor/menus/tableRow.tsx
index 6c48a9c2db..41cfa8720c 100644
--- a/app/editor/menus/tableRow.tsx
+++ b/app/editor/menus/tableRow.tsx
@@ -4,8 +4,15 @@ import {
InsertBelowIcon,
MoreIcon,
TableHeaderRowIcon,
+ TableSplitCellsIcon,
+ TableMergeCellsIcon,
} from "outline-icons";
import { EditorState } from "prosemirror-state";
+import { CellSelection } from "prosemirror-tables";
+import {
+ isMergedCellSelection,
+ isMultipleCellSelection,
+} from "@shared/editor/queries/table";
import { MenuItem } from "@shared/editor/types";
import { Dictionary } from "~/hooks/useDictionary";
@@ -14,6 +21,11 @@ export default function tableRowMenuItems(
index: number,
dictionary: Dictionary
): MenuItem[] {
+ const { selection } = state;
+ if (!(selection instanceof CellSelection)) {
+ return [];
+ }
+
return [
{
icon: ,
@@ -36,6 +48,21 @@ export default function tableRowMenuItems(
icon: ,
attrs: { index },
},
+ {
+ name: "mergeCells",
+ label: dictionary.mergeCells,
+ icon: ,
+ visible: isMultipleCellSelection(state),
+ },
+ {
+ name: "splitCell",
+ label: dictionary.splitCell,
+ icon: ,
+ visible: isMergedCellSelection(state),
+ },
+ {
+ name: "separator",
+ },
{
name: "deleteRow",
label: dictionary.deleteRow,
diff --git a/app/hooks/useDictionary.ts b/app/hooks/useDictionary.ts
index 7493659bc4..804abdb3a7 100644
--- a/app/hooks/useDictionary.ts
+++ b/app/hooks/useDictionary.ts
@@ -87,6 +87,8 @@ export default function useDictionary() {
toggleHeader: t("Toggle header"),
mathInline: t("Math inline (LaTeX)"),
mathBlock: t("Math block (LaTeX)"),
+ mergeCells: t("Merge cells"),
+ splitCell: t("Split cell"),
tip: t("Tip"),
tipNotice: t("Tip notice"),
warning: t("Warning"),
diff --git a/package.json b/package.json
index 8e45d9ce84..7e9b1b39a0 100644
--- a/package.json
+++ b/package.json
@@ -173,7 +173,7 @@
"node-fetch": "2.7.0",
"nodemailer": "^6.10.0",
"octokit": "^3.2.1",
- "outline-icons": "^3.10.0",
+ "outline-icons": "^3.12.0",
"oy-vey": "^0.12.1",
"passport": "^0.7.0",
"passport-google-oauth2": "^0.2.0",
diff --git a/shared/editor/commands/table.ts b/shared/editor/commands/table.ts
index 0192bdc271..7fc08eda42 100644
--- a/shared/editor/commands/table.ts
+++ b/shared/editor/commands/table.ts
@@ -17,6 +17,8 @@ import {
deleteRow,
deleteColumn,
deleteTable,
+ mergeCells,
+ splitCell,
} from "prosemirror-tables";
import { ProsemirrorHelper } from "../../utils/ProsemirrorHelper";
import { CSVHelper } from "../../utils/csv";
@@ -597,3 +599,21 @@ export function deleteCellSelection(
}
return false;
}
+
+/**
+ * A command that splits a cell and collapses the selection.
+ *
+ * @returns The command
+ */
+export function splitCellAndCollapse(): Command {
+ return chainTransactions(splitCell, collapseSelection());
+}
+
+/**
+ * A command that merges selected cells and collapses the selection.
+ *
+ * @returns The command
+ */
+export function mergeCellsAndCollapse(): Command {
+ return chainTransactions(mergeCells, collapseSelection());
+}
diff --git a/shared/editor/nodes/Table.ts b/shared/editor/nodes/Table.ts
index aefbe753ae..0abbea450d 100644
--- a/shared/editor/nodes/Table.ts
+++ b/shared/editor/nodes/Table.ts
@@ -28,6 +28,8 @@ import {
moveOutOfTable,
createTableInner,
deleteTableIfSelected,
+ splitCellAndCollapse,
+ mergeCellsAndCollapse,
} from "../commands/table";
import { MarkdownSerializerState } from "../lib/markdown/serializer";
import { FixTablesPlugin } from "../plugins/FixTables";
@@ -89,6 +91,8 @@ export default class Table extends Node {
exportTable,
toggleHeaderColumn: () => toggleHeader("column"),
toggleHeaderRow: () => toggleHeader("row"),
+ mergeCells: () => mergeCellsAndCollapse(),
+ splitCell: () => splitCellAndCollapse(),
};
}
diff --git a/shared/editor/plugins/FixTables.ts b/shared/editor/plugins/FixTables.ts
index 5e983e12b1..e88c679c23 100644
--- a/shared/editor/plugins/FixTables.ts
+++ b/shared/editor/plugins/FixTables.ts
@@ -5,16 +5,19 @@ import { changedDescendants } from "../lib/changedDescendants";
import { getCellsInColumn } from "../queries/table";
/**
- * A ProseMirror plugin that fixes the last column in a table to ensure it fills the remaining width.
+ * A ProseMirror plugin that fixes various ways that tables can end up in an incorrect state:
+ *
+ * - The last column in a table should fill the remaining width
+ * - Header cells should only exist in the first row or column
*/
export class FixTablesPlugin extends Plugin {
constructor() {
super({
appendTransaction: (_transactions, oldState, state) => {
let tr: Transaction | undefined;
- const check = (node: Node) => {
+ const check = (node: Node, pos: number) => {
if (node.type.spec.tableRole === "table") {
- tr = this.fixTable(state, node, tr);
+ tr = this.fixTable(state, node, pos, tr);
}
};
if (!oldState) {
@@ -30,6 +33,7 @@ export class FixTablesPlugin extends Plugin {
private fixTable(
state: EditorState,
table: Node,
+ pos: number,
tr: Transaction | undefined
): Transaction | undefined {
let fixed = false;
@@ -41,11 +45,11 @@ export class FixTablesPlugin extends Plugin {
// If the table has only one column, remove the colwidth attribute on all cells
if (map.width === 1) {
const cells = getCellsInColumn(0)(state);
- cells.forEach((pos) => {
+ cells.forEach((cellPos) => {
const node = state.doc.nodeAt(pos);
if (node?.attrs.colspan) {
fixed = true;
- tr = tr!.setNodeMarkup(pos, undefined, {
+ tr = tr!.setNodeMarkup(cellPos, undefined, {
...node?.attrs,
colwidth: null,
});
@@ -53,6 +57,28 @@ export class FixTablesPlugin extends Plugin {
});
}
+ // If the table has header cells that are not in the first row or column
+ // then convert them to regular cells
+ const cellPositions = map.cellsInRect({
+ left: 1,
+ top: 1,
+ right: map.width,
+ bottom: map.height,
+ });
+
+ for (let i = 0; i < cellPositions.length; i++) {
+ const cellPos = cellPositions[i];
+ const cell = table.nodeAt(cellPos);
+ if (cell && cell.type === state.schema.nodes.th) {
+ fixed = true;
+ tr = tr!.setNodeMarkup(
+ cellPos + pos + 1,
+ state.schema.nodes.td,
+ cell.attrs
+ );
+ }
+ }
+
return fixed ? tr : undefined;
}
}
diff --git a/shared/editor/queries/table.ts b/shared/editor/queries/table.ts
index 7bad794269..a0bb4efe8c 100644
--- a/shared/editor/queries/table.ts
+++ b/shared/editor/queries/table.ts
@@ -174,3 +174,46 @@ export function isTableSelected(state: EditorState): boolean {
return false;
}
+
+/**
+ * Check if multiple cells are selected in the editor.
+ *
+ * @param state The editor state
+ * @returns Boolean indicating if multiple cells are selected
+ */
+export function isMultipleCellSelection(state: EditorState): boolean {
+ const { selection } = state;
+
+ return (
+ selection instanceof CellSelection &&
+ (selection.isColSelection() ||
+ selection.isRowSelection() ||
+ selection.$anchorCell.pos !== selection.$headCell.pos)
+ );
+}
+
+/**
+ * Check if the selection spans multiple merged cells.
+ *
+ * @param state The editor state
+ * @returns Boolean indicating if a merged cell is selected
+ */
+export function isMergedCellSelection(state: EditorState): boolean {
+ const { selection } = state;
+ if (selection instanceof CellSelection) {
+ // Check if any cell in the selection has a colspan or rowspan > 1
+ let hasMergedCells = false;
+ selection.forEachCell((cell) => {
+ if (cell.attrs.colspan > 1 || cell.attrs.rowspan > 1) {
+ hasMergedCells = true;
+ return false;
+ }
+
+ return true;
+ });
+
+ return hasMergedCells;
+ }
+
+ return false;
+}
diff --git a/shared/i18n/locales/en_US/translation.json b/shared/i18n/locales/en_US/translation.json
index e4c8d7ebbd..c1b387d92c 100644
--- a/shared/i18n/locales/en_US/translation.json
+++ b/shared/i18n/locales/en_US/translation.json
@@ -497,6 +497,8 @@
"Toggle header": "Toggle header",
"Math inline (LaTeX)": "Math inline (LaTeX)",
"Math block (LaTeX)": "Math block (LaTeX)",
+ "Merge cells": "Merge cells",
+ "Split cell": "Split cell",
"Tip": "Tip",
"Tip notice": "Tip notice",
"Warning": "Warning",
diff --git a/yarn.lock b/yarn.lock
index 86cfcd194b..3adfd235ff 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2236,30 +2236,18 @@
resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.25.3.tgz#b17a2171f9074df9e91bfb07ef99a892ac06412a"
integrity sha512-ICgUR+kPimx0vvRzf+N/7L7tVSQeE3BYY+NhHRHXS1kBuPO7z2+7ea2HbhDyZdTephgvNvKrlDDKUexuCVBVvg==
-"@eslint-community/eslint-utils@^4.2.0":
- version "4.4.0"
- resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59"
- integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==
- dependencies:
- eslint-visitor-keys "^3.3.0"
-
-"@eslint-community/eslint-utils@^4.7.0":
+"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.7.0":
version "4.7.0"
resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz#607084630c6c033992a082de6e6fbc1a8b52175a"
integrity sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==
dependencies:
eslint-visitor-keys "^3.4.3"
-"@eslint-community/regexpp@^4.10.0":
+"@eslint-community/regexpp@^4.10.0", "@eslint-community/regexpp@^4.6.1":
version "4.12.1"
resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.1.tgz#cfc6cffe39df390a3841cde2abccf92eaa7ae0e0"
integrity sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==
-"@eslint-community/regexpp@^4.6.1":
- version "4.10.0"
- resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.10.0.tgz#548f6de556857c8bb73bbee70c35dc82a2e74d63"
- integrity sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==
-
"@eslint/eslintrc@^2.1.4":
version "2.1.4"
resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.4.tgz#388a269f0f25c1b6adc317b5a2c55714894c70ad"
@@ -3578,7 +3566,7 @@
"@radix-ui/react-primitive" "2.0.1"
"@radix-ui/react-use-layout-effect" "1.1.0"
-"@radix-ui/react-portal@1.1.4", "@radix-ui/react-portal@^1.0.1":
+"@radix-ui/react-portal@1.1.4":
version "1.1.4"
resolved "https://registry.yarnpkg.com/@radix-ui/react-portal/-/react-portal-1.1.4.tgz#ff5401ff63c8a825c46eea96d3aef66074b8c0c8"
integrity sha512-sn2O9k1rPFYVyKd5LAJfo96JlSGVFpa1fS6UuBJfrZadudiw5tAmru+n1x7aMRQ84qDM71Zh1+SzK5QwU0tJfA==
@@ -3586,7 +3574,7 @@
"@radix-ui/react-primitive" "2.0.2"
"@radix-ui/react-use-layout-effect" "1.1.0"
-"@radix-ui/react-portal@1.1.9":
+"@radix-ui/react-portal@1.1.9", "@radix-ui/react-portal@^1.0.1":
version "1.1.9"
resolved "https://registry.yarnpkg.com/@radix-ui/react-portal/-/react-portal-1.1.9.tgz#14c3649fe48ec474ac51ed9f2b9f5da4d91c4472"
integrity sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==
@@ -3869,20 +3857,13 @@
dependencies:
"@radix-ui/react-primitive" "2.0.1"
-"@radix-ui/react-visually-hidden@1.2.3":
+"@radix-ui/react-visually-hidden@1.2.3", "@radix-ui/react-visually-hidden@^1.2.2":
version "1.2.3"
resolved "https://registry.yarnpkg.com/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.3.tgz#a8c38c8607735dc9f05c32f87ab0f9c2b109efbf"
integrity sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==
dependencies:
"@radix-ui/react-primitive" "2.1.3"
-"@radix-ui/react-visually-hidden@^1.2.2":
- version "1.2.2"
- resolved "https://registry.yarnpkg.com/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.2.tgz#aa6d0f95b0cd50f08b02393d25132f52ca7861dc"
- integrity sha512-ORCmRUbNiZIv6uV5mhFrhsIKw4UX/N3syZtyqvry61tbGm4JlgQuSn0hk5TwCARsCjkcnuRkSdCE3xfb+ADHew==
- dependencies:
- "@radix-ui/react-primitive" "2.1.2"
-
"@radix-ui/rect@0.1.1":
version "0.1.1"
resolved "https://registry.yarnpkg.com/@radix-ui/rect/-/rect-0.1.1.tgz#95b5ba51f469bea6b1b841e2d427e17e37d38419"
@@ -7691,12 +7672,7 @@ content-disposition@~0.5.2:
dependencies:
safe-buffer "5.1.2"
-content-type@^1.0.4:
- version "1.0.4"
- resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b"
- integrity "sha1-4TjMdeBAxyexlm/l5fjJruJW/js= sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
-
-content-type@^1.0.5:
+content-type@^1.0.4, content-type@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918"
integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==
@@ -7833,16 +7809,7 @@ cross-fetch@^3.0.4, cross-fetch@^3.1.5:
dependencies:
node-fetch "^2.6.11"
-cross-spawn@^7.0.2, cross-spawn@^7.0.3:
- version "7.0.5"
- resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.5.tgz#910aac880ff5243da96b728bc6521a5f6c2f2f82"
- integrity sha512-ZVJrKKYunU38/76t0RMOulHOnUcbU9GbpWKAOZ0mhjr7CX6FVrH+4FrAapSOekrgFQ3f/8gwMEuIft0aKq6Hug==
- dependencies:
- path-key "^3.1.0"
- shebang-command "^2.0.0"
- which "^2.0.1"
-
-cross-spawn@^7.0.6:
+cross-spawn@^7.0.2, cross-spawn@^7.0.3, cross-spawn@^7.0.6:
version "7.0.6"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f"
integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==
@@ -12402,12 +12369,12 @@ micromatch@4.0.5, micromatch@^4.0.2, micromatch@^4.0.4:
braces "^3.0.2"
picomatch "^2.3.1"
-mime-db@1.52.0, "mime-db@>= 1.43.0 < 2":
+mime-db@1.52.0:
version "1.52.0"
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
integrity "sha1-u6vNwChZ9JhzAchW4zh85exDv3A= sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="
-mime-db@^1.54.0:
+"mime-db@>= 1.43.0 < 2", mime-db@^1.54.0:
version "1.54.0"
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.54.0.tgz#cddb3ee4f9c64530dff640236661d42cb6a314f5"
integrity sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==
@@ -12949,10 +12916,10 @@ os-tmpdir@~1.0.2:
resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
integrity "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g=="
-outline-icons@^3.10.0:
- version "3.10.0"
- resolved "https://registry.yarnpkg.com/outline-icons/-/outline-icons-3.10.0.tgz#625ee232a807ccbc1cca2eda87849cf77ca25160"
- integrity sha512-LSHIlZRgNtFoFHj5lDG6hfflNpVpO5kQl/jV7dYSytTcVVjfOECEDACSFsQ34JP7HT1vFaubp1EZSVrKFnIyVw==
+outline-icons@^3.12.0:
+ version "3.12.0"
+ resolved "https://registry.yarnpkg.com/outline-icons/-/outline-icons-3.12.0.tgz#2bcc086c0086057b57b21991774d44ce826d2116"
+ integrity sha512-WnJr/yiJmWKLN2mol7tTcNkNwttYbQqldnAmBPjpHnH2aGJrXBlBEn35KsaiTLd664XDlWzU7XMfWDwhzrtbsA==
own-keys@^1.0.0:
version "1.0.1"