Skip to content

Commit

Permalink
Merge pull request #79 from swup/feat/optimize-get-fragment-visit
Browse files Browse the repository at this point in the history
  • Loading branch information
hirasso authored Jun 4, 2024
2 parents 740a3ce + 6b6d10d commit bf0c98b
Show file tree
Hide file tree
Showing 7 changed files with 2,079 additions and 1,017 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Changelog

## [1.1.1] - 2024-06-01

- Optimize logic of persisting fragment elements
- Optimize API usage of `getFragmentVisit`

## [1.1.0] - 2024-05-29

- Match rules conditionally: [`rule.if`](https://swup.js.org/plugins/fragment-plugin/#rule-if)
Expand Down Expand Up @@ -81,6 +86,7 @@

- Initial Release

[1.1.1]: https://github.com/swup/fragment-plugin/releases/tag/1.1.1
[1.1.0]: https://github.com/swup/fragment-plugin/releases/tag/1.1.0
[1.0.2]: https://github.com/swup/fragment-plugin/releases/tag/1.0.2
[1.0.1]: https://github.com/swup/fragment-plugin/releases/tag/1.0.1
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ Type: `boolean`. Set to `true` for debug information in the console. Defaults to

- The `containers` of the matching rule **need to be shared between the current and the incoming document**
- For each selector in the `containers` array, the **first** matching element in the DOM will be selected
- The plugin will check if an element already matches the new URL before replacing it
- If a visit isn't be considered a reload of the current page, fragment elements that already match the new URL will be ignored

## Advanced use cases

Expand Down
2,950 changes: 1,969 additions & 981 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@swup/fragment-plugin",
"amdName": "SwupFragmentPlugin",
"version": "1.1.0",
"version": "1.1.1",
"description": "A swup plugin for dynamically replacing containers based on rules",
"type": "module",
"source": "src/index.ts",
Expand Down
14 changes: 11 additions & 3 deletions src/SwupFragmentPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,12 @@ export default class SwupFragmentPlugin extends PluginBase {
* @access public
*/
getFragmentVisit(route: Route, visit?: Visit): FragmentVisit | undefined {
const rule = getFirstMatchingRule(route, this.parsedRules, visit || this.swup.visit);
const rule = getFirstMatchingRule(
route,
this.parsedRules,
// @ts-expect-error createVisit is protected
visit || this.swup.createVisit(route)
);

// Bail early if no rule matched
if (!rule) return;
Expand All @@ -154,8 +159,11 @@ export default class SwupFragmentPlugin extends PluginBase {
this.swup,
this.logger
);
// Bail early if there are no containers to be replaced for this visit
if (!containers.length) return;

/** Bail early if there are no fragment elements found for this visit */
if (!containers.length) {
return;
}

// Pick properties from the current rule that should be projected into the fragmentVisit object
const { name, scroll, focus } = rule;
Expand Down
77 changes: 53 additions & 24 deletions src/inc/functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,41 +116,70 @@ function prepareFragmentElements({ parsedRules, swup, logger }: FragmentPlugin):
}

/**
* Get all containers that should be replaced for a given visit's route
* Get all containers that should be replaced for a given visit's route.
* Ignores containers that already match the current URL, if the visit can't be considered a reload.
*
* A visit is being considered a reload, if one of these conditions apply:
* - `route.from` equal to `route.to`
* - all containers match the current url and swup is set to navigate on `linkToSelf`
*/
export const getFragmentVisitContainers = (
route: Route,
selectors: string[],
swup: Swup,
logger?: Logger
) => {
const isReload = isEqualUrl(route.from, route.to);

return selectors.filter((selector) => {
const el = document.querySelector<FragmentElement>(selector);
): string[] => {
let fragments: { selector: string; el: FragmentElement }[] = selectors
.map((selector) => {
const el = document.querySelector<FragmentElement>(selector);

if (!el) {
if (__DEV__) logger?.log(`${highlight(selector)} missing in current document`);
return false;
}

if (!el) {
if (__DEV__) logger?.log(`${highlight(selector)} missing in current document`);
return false;
}
const fragmentElement = queryFragmentElement(selector, swup);

if (!queryFragmentElement(selector, swup)) {
if (__DEV__) {
// prettier-ignore
logger?.error(`${highlight(selector)} is outside of swup's default containers`);
if (!fragmentElement) {
if (__DEV__) {
// prettier-ignore
logger?.error(`${highlight(selector)} is outside of swup's default containers`);
}
return false;
}
return false;
}

if (!isReload && elementMatchesFragmentUrl(el, route.to)) {
if (__DEV__)
// prettier-ignore
logger?.log(`ignoring fragment ${highlight(selector)} as it already matches the current URL`);
return false;
}
return {
selector,
el
};
})
.filter((record): record is { selector: string; el: FragmentElement } => !!record);

return true;
});
const isLinkToSelf = fragments.every((fragment) =>
elementMatchesFragmentUrl(fragment.el, route.to)
);

const isReload =
isEqualUrl(route.from, route.to) ||
(isLinkToSelf && swup.options.linkToSelf === 'navigate');

/**
* If this is NOT a reload, ignore fragments that already match `route.to`
*/
if (!isReload) {
fragments = fragments.filter((fragment) => {
if (elementMatchesFragmentUrl(fragment.el, route.to)) {
if (__DEV__) {
// prettier-ignore
logger?.log(`ignoring fragment ${highlight(fragment.selector)} as it already matches the current URL`);
}
return false;
}
return true;
});
}

return fragments.map((fragment) => fragment.selector);
};

/**
Expand Down
45 changes: 38 additions & 7 deletions tests/vitest/getFragmentVisitContainers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,44 @@ describe('getFragmentVisitContainers()', () => {
});

it('should get the correct containers when navigating to the same URL', () => {
const fragmentContainers = getFragmentVisitContainers(
{ from: '/page-1/', to: '/page-1/' },
['#fragment-1', '#fragment-2', '#fragment-3', '#fragment-outside', '#fragment-missing'],
fragmentPlugin.swup,
fragmentPlugin.logger
);
// route.from is equal to route.to
expect(
getFragmentVisitContainers(
{ from: '/page-1/', to: '/page-1/' },
[
'#fragment-1',
'#fragment-2',
'#fragment-3',
'#fragment-outside',
'#fragment-missing'
],
fragmentPlugin.swup,
fragmentPlugin.logger
)
).toEqual(['#fragment-1', '#fragment-2', '#fragment-3']);
});

it("should reload fragments if swup.options.linkToSelf equals 'navigate'", () => {
fragmentPlugin.swup.options.linkToSelf = 'navigate';
expect(
getFragmentVisitContainers(
{ from: '/page-1/', to: '/page-2/' },
['#fragment-3'],
fragmentPlugin.swup,
fragmentPlugin.logger
)
).toEqual(['#fragment-3']);
});

expect(fragmentContainers).toEqual(['#fragment-1', '#fragment-2', '#fragment-3']);
it("should ignore fragments if swup.options.linkToSelf equals 'scroll'", () => {
fragmentPlugin.swup.options.linkToSelf = 'scroll';
expect(
getFragmentVisitContainers(
{ from: '/page-1/', to: '/page-2/' },
['#fragment-3'],
fragmentPlugin.swup,
fragmentPlugin.logger
)
).toEqual([]);
});
});

0 comments on commit bf0c98b

Please sign in to comment.