Skip to content

Commit b4bfffa

Browse files
JiuqingSongCopilot
andauthored
Retain segments with undeletable links during model pruning (#3380)
* Retain segments with undeletable links during model pruning Keep segments that have link.format.undeletable set to true in pruneUnselectedModel, even when they are not selected. This prevents undeletable link segments from being lost during selection-based pruning. Add unit tests covering the new behavior. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * improve --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent aa54077 commit b4bfffa

2 files changed

Lines changed: 216 additions & 2 deletions

File tree

packages/roosterjs-content-model-dom/lib/domUtils/selection/pruneUnselectedModel.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,18 @@ function pruneUnselectedModelInternal(
4747
case 'Paragraph':
4848
const newSegments: ContentModelSegment[] = [];
4949
for (const segment of block.segments) {
50+
const isSelectedOrUndeletable =
51+
segment.isSelected || segment.link?.format.undeletable;
52+
5053
if (segment.segmentType == 'General') {
5154
pruneUnselectedModel(segment);
52-
if (segment.blocks.length > 0 || segment.isSelected) {
55+
if (segment.blocks.length > 0 || isSelectedOrUndeletable) {
5356
newSegments.push(segment);
5457
}
55-
} else if (segment.isSelected && segment.segmentType != 'SelectionMarker') {
58+
} else if (
59+
isSelectedOrUndeletable &&
60+
segment.segmentType != 'SelectionMarker'
61+
) {
5662
newSegments.push(segment);
5763
}
5864
}

packages/roosterjs-content-model-dom/test/domUtils/selection/pruneUnselectedModelTest.ts

Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1474,4 +1474,212 @@ describe('pruneUnselectedModel', () => {
14741474
],
14751475
});
14761476
});
1477+
1478+
it('retains unselected segment with undeletable link', () => {
1479+
const group = createContentModelDocument();
1480+
const para = createParagraph();
1481+
const text1 = createText('undeletable link');
1482+
const text2 = createText('normal text');
1483+
1484+
text1.link = {
1485+
format: { href: 'http://example.com', undeletable: true },
1486+
dataset: {},
1487+
};
1488+
1489+
para.segments.push(text1);
1490+
para.segments.push(text2);
1491+
group.blocks.push(para);
1492+
1493+
pruneUnselectedModel(group);
1494+
1495+
expect(group).toEqual({
1496+
blockGroupType: 'Document',
1497+
blocks: [
1498+
{
1499+
blockType: 'Paragraph',
1500+
segments: [
1501+
{
1502+
segmentType: 'Text',
1503+
text: 'undeletable link',
1504+
format: {},
1505+
link: {
1506+
format: { href: 'http://example.com', undeletable: true },
1507+
dataset: {},
1508+
},
1509+
},
1510+
],
1511+
format: {},
1512+
isImplicit: true,
1513+
},
1514+
],
1515+
});
1516+
});
1517+
1518+
it('retains both selected segment and unselected undeletable link segment', () => {
1519+
const group = createContentModelDocument();
1520+
const para = createParagraph();
1521+
const text1 = createText('selected');
1522+
const text2 = createText('undeletable link');
1523+
const text3 = createText('normal');
1524+
1525+
text1.isSelected = true;
1526+
text2.link = {
1527+
format: { href: 'http://example.com', undeletable: true },
1528+
dataset: {},
1529+
};
1530+
1531+
para.segments.push(text1);
1532+
para.segments.push(text2);
1533+
para.segments.push(text3);
1534+
group.blocks.push(para);
1535+
1536+
pruneUnselectedModel(group);
1537+
1538+
expect(group).toEqual({
1539+
blockGroupType: 'Document',
1540+
blocks: [
1541+
{
1542+
blockType: 'Paragraph',
1543+
segments: [
1544+
{
1545+
segmentType: 'Text',
1546+
text: 'selected',
1547+
format: {},
1548+
isSelected: true,
1549+
},
1550+
{
1551+
segmentType: 'Text',
1552+
text: 'undeletable link',
1553+
format: {},
1554+
link: {
1555+
format: { href: 'http://example.com', undeletable: true },
1556+
dataset: {},
1557+
},
1558+
},
1559+
],
1560+
format: {},
1561+
isImplicit: true,
1562+
},
1563+
],
1564+
});
1565+
});
1566+
1567+
it('does not retain unselected segment with link that is not undeletable', () => {
1568+
const group = createContentModelDocument();
1569+
const para = createParagraph();
1570+
const text1 = createText('selected');
1571+
const text2 = createText('deletable link');
1572+
1573+
text1.isSelected = true;
1574+
text2.link = {
1575+
format: { href: 'http://example.com' },
1576+
dataset: {},
1577+
};
1578+
1579+
para.segments.push(text1);
1580+
para.segments.push(text2);
1581+
group.blocks.push(para);
1582+
1583+
pruneUnselectedModel(group);
1584+
1585+
expect(group).toEqual({
1586+
blockGroupType: 'Document',
1587+
blocks: [
1588+
{
1589+
blockType: 'Paragraph',
1590+
segments: [
1591+
{
1592+
segmentType: 'Text',
1593+
text: 'selected',
1594+
format: {},
1595+
isSelected: true,
1596+
},
1597+
],
1598+
format: {},
1599+
isImplicit: true,
1600+
},
1601+
],
1602+
});
1603+
});
1604+
1605+
it('retains unselected general segment with undeletable link', () => {
1606+
const group = createContentModelDocument();
1607+
const para = createParagraph();
1608+
const generalSpan = createGeneralSegment(document.createElement('span'));
1609+
const text = createText('normal text');
1610+
1611+
generalSpan.link = {
1612+
format: { href: 'http://example.com', undeletable: true },
1613+
dataset: {},
1614+
};
1615+
1616+
para.segments.push(generalSpan);
1617+
para.segments.push(text);
1618+
group.blocks.push(para);
1619+
1620+
pruneUnselectedModel(group);
1621+
1622+
expect(group).toEqual({
1623+
blockGroupType: 'Document',
1624+
blocks: [
1625+
{
1626+
blockType: 'Paragraph',
1627+
segments: [
1628+
{
1629+
blockType: 'BlockGroup',
1630+
blockGroupType: 'General',
1631+
segmentType: 'General',
1632+
format: {},
1633+
blocks: [],
1634+
element: jasmine.anything(),
1635+
link: {
1636+
format: { href: 'http://example.com', undeletable: true },
1637+
dataset: {},
1638+
},
1639+
},
1640+
],
1641+
format: {},
1642+
isImplicit: true,
1643+
},
1644+
],
1645+
});
1646+
});
1647+
1648+
it('does not retain selection marker with undeletable link', () => {
1649+
const group = createContentModelDocument();
1650+
const para = createParagraph();
1651+
const text = createText('selected');
1652+
const marker = createSelectionMarker();
1653+
1654+
text.isSelected = true;
1655+
marker.link = {
1656+
format: { href: 'http://example.com', undeletable: true },
1657+
dataset: {},
1658+
};
1659+
1660+
para.segments.push(text);
1661+
para.segments.push(marker);
1662+
group.blocks.push(para);
1663+
1664+
pruneUnselectedModel(group);
1665+
1666+
expect(group).toEqual({
1667+
blockGroupType: 'Document',
1668+
blocks: [
1669+
{
1670+
blockType: 'Paragraph',
1671+
segments: [
1672+
{
1673+
segmentType: 'Text',
1674+
text: 'selected',
1675+
format: {},
1676+
isSelected: true,
1677+
},
1678+
],
1679+
format: {},
1680+
isImplicit: true,
1681+
},
1682+
],
1683+
});
1684+
});
14771685
});

0 commit comments

Comments
 (0)