Skip to content

Commit 269b572

Browse files
committed
refactor: better structure and deprecation
1 parent 5098274 commit 269b572

File tree

2 files changed

+20
-14
lines changed

2 files changed

+20
-14
lines changed

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ export default function App() {
144144
}
145145
```
146146

147-
The `<A>` tag also has an `active` class if its href matches the current location, an `exactActive` class if its href matches the current location exactly and `inactive` class otherwise. **Note:** `active` and `exactActive` are mutually exclusive - there is no class merging! By default matching includes locations that are descendents (eg. href `/users` matches locations `/users` and `/users/123`). `exactActive` is particularly useful for links to the root route `/` which would match everything.
147+
The `<A>` tag also has an `active` class if its href matches the current location and `inactive` class otherwise. By providing the property `exactActiveClass`, you can opt in to a third state, which is `exactActive` and is set when the href matches the current location exactly. **Note:** By default matching includes locations that are descendents (eg. href `/users` matches locations `/users` and `/users/123`). If no `exactActiveClass` property was provided, `active` class will be set for both partially and exactly matching routes.
148148

149149

150150
| prop | type | description |
@@ -155,8 +155,8 @@ The `<A>` tag also has an `active` class if its href matches the current locatio
155155
| state | unknown | [Push this value](https://developer.mozilla.org/en-US/docs/Web/API/History/pushState) to the history stack when navigating | |
156156
| inactiveClass | string | The class to show when the link is inactive (when the current location doesn't match the link) |
157157
| activeClass | string | The class to show when the link is active, i.e. the current location _starts with_ `href` |
158-
| exactActiveClass | string | The class to show when the link matches the `href` exactly |
159-
| end | boolean | If `true`, only considers the link to be active when the curent location matches the `href` exactly; if `false`, check if the current location _starts with_ `href` |
158+
| exactActiveClass | string or true | The class to show when the link matches the `href` exactly. If `true`, applies `exactActive` class and enables strict matching - i.e. `activeClass` will not apply for an exact match.
159+
| end | boolean | **Deprecated** If `true`, only considers the link to be active when the curent location matches the `href` exactly; if `false`, check if the current location _starts with_ `href` - providing `exactActiveClass` overrides this behavior | |
160160

161161
### The Navigate Component
162162
Solid Router provides a `Navigate` component that works similarly to `A`, but it will _immediately_ navigate to the provided path as soon as the component is rendered. It also uses the `href` prop, but you have the additional option of passing a function to `href` that returns a path to navigate to:

src/components.tsx

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -208,14 +208,14 @@ export interface AnchorProps extends Omit<JSX.AnchorHTMLAttributes<HTMLAnchorEle
208208
state?: unknown;
209209
inactiveClass?: string;
210210
activeClass?: string;
211-
exactActiveClass?: string;
211+
exactActiveClass?: true | string;
212212
/**
213-
* @deprecated end property deprecated in favor of 'exactActiveClass'
214-
*/
213+
* @deprecated end property deprecated in favor of 'exactActiveClass'
214+
*/
215215
end?: boolean;
216216
}
217217
export function A(props: AnchorProps) {
218-
props = mergeProps({ inactiveClass: "inactive", activeClass: "active", exactActiveClass: 'exactActive' }, props);
218+
props = mergeProps({ inactiveClass: "inactive", activeClass: "active" }, props);
219219
const [, rest] = splitProps(props, [
220220
"href",
221221
"state",
@@ -228,15 +228,21 @@ export function A(props: AnchorProps) {
228228
const to = useResolvedPath(() => props.href);
229229
const href = useHref(to);
230230
const location = useLocation();
231-
const isActive = createMemo(() => {
231+
const matchedHref = createMemo(() => {
232232
const to_ = to();
233233
if (to_ === undefined) return [false, false];
234234
const path = normalizePath(to_.split(/[?#]/, 1)[0]).toLowerCase();
235235
const loc = normalizePath(location.pathname).toLowerCase();
236-
// To be replaced with [loc.startsWith(path), path === loc] when end is patched out
237-
return [props.end ? path === loc : loc.startsWith(path), path === loc];
236+
return [loc.startsWith(path), path === loc];
238237
});
239238

239+
const isLooseActive = createMemo(() => matchedHref()[0])
240+
const isExactActive = createMemo(() => matchedHref()[1] && Boolean(props.exactActiveClass))
241+
242+
// Remove together with `end` property
243+
// If end was provided return an exact match, else return loose match (as long as users don't opt in for new behavior)
244+
const isActiveDeprecated = createMemo(() => props.end ? matchedHref()[1] : !props.exactActiveClass && isLooseActive())
245+
240246
return (
241247
<a
242248
link
@@ -245,12 +251,12 @@ export function A(props: AnchorProps) {
245251
state={JSON.stringify(props.state)}
246252
classList={{
247253
...(props.class && { [props.class]: true }),
248-
[props.inactiveClass!]: !isActive()[0],
249-
[props.activeClass!]: isActive()[0] && !isActive()[1],
250-
[props.exactActiveClass!]: isActive()[1],
254+
[props.inactiveClass!]: !isLooseActive(),
255+
[props.activeClass!]: isLooseActive() && !isExactActive() || isActiveDeprecated(),
256+
...(props.exactActiveClass && { [props.exactActiveClass === true ? 'exactActive' : props.exactActiveClass]: isExactActive() }),
251257
...rest.classList
252258
}}
253-
aria-current={isActive()[1] ? "page" : undefined}
259+
aria-current={isLooseActive() ? "page" : undefined}
254260
/>
255261
);
256262
}

0 commit comments

Comments
 (0)