Skip to content

Commit 00fcfdb

Browse files
committed
feat: add initial class name option
1 parent 8bb4c44 commit 00fcfdb

File tree

9 files changed

+61
-19
lines changed

9 files changed

+61
-19
lines changed

packages/core/README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ const adhesive = new Adhesive({
5454
zIndex: 999,
5555
outerClassName: 'custom-outer',
5656
innerClassName: 'custom-inner',
57+
initialClassName: 'custom-initial',
5758
fixedClassName: 'custom-fixed',
5859
relativeClassName: 'custom-relative'
5960
});
@@ -93,8 +94,9 @@ adhesive.cleanup();
9394
| `enabled` | `boolean` | `true` | Whether to enable sticky behavior |
9495
| `outerClassName` | `string` | `'adhesive__outer'` | Class for the outer wrapper |
9596
| `innerClassName` | `string` | `'adhesive__inner'` | Class for the inner wrapper |
97+
| `initialClassName` | `string` | `'adhesive--initial'` | Class when element is in its initial state |
9698
| `fixedClassName` | `string` | `'adhesive--fixed'` | Class when element is sticky |
97-
| `relativeClassName` | `string` | `'adhesive--relative'` | Class when element returns to normal |
99+
| `relativeClassName` | `string` | `'adhesive--relative'` | Class when element reaches its boundary |
98100

99101
#### Methods
100102

packages/core/src/lib/Adhesive.ts

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -63,22 +63,27 @@ export interface AdhesiveOptions {
6363
*/
6464
readonly zIndex?: number;
6565
/**
66-
* CSS class name for the outer wrapper element.
66+
* CSS class for the outer wrapper element.
6767
* @default "adhesive__outer"
6868
*/
6969
readonly outerClassName?: string;
7070
/**
71-
* CSS class name for the inner wrapper element.
71+
* CSS class for the inner wrapper element.
7272
* @default "adhesive__inner"
7373
*/
7474
readonly innerClassName?: string;
7575
/**
76-
* CSS class name applied when the element is sticky.
76+
* CSS class applied when the element is in its initial state.
77+
* @default "adhesive--initial"
78+
*/
79+
readonly initialClassName?: string;
80+
/**
81+
* CSS class applied when the element is sticky.
7782
* @default "adhesive--fixed"
7883
*/
7984
readonly fixedClassName?: string;
8085
/**
81-
* CSS class name applied when the element reaches its boundary.
86+
* CSS class applied when the element reaches its boundary.
8287
* @default "adhesive--relative"
8388
*/
8489
readonly relativeClassName?: string;
@@ -91,6 +96,7 @@ interface InternalAdhesiveOptions {
9196
zIndex: number;
9297
outerClassName: string;
9398
innerClassName: string;
99+
initialClassName: string;
94100
fixedClassName: string;
95101
relativeClassName: string;
96102
}
@@ -163,6 +169,7 @@ const DEFAULT_OPTIONS = {
163169
zIndex: 1 as number,
164170
outerClassName: "adhesive__outer",
165171
innerClassName: "adhesive__inner",
172+
initialClassName: "adhesive--initial",
166173
fixedClassName: "adhesive--fixed",
167174
relativeClassName: "adhesive--relative",
168175
} satisfies Omit<AdhesiveOptions, "targetEl" | "boundingEl">;
@@ -244,6 +251,7 @@ export class Adhesive {
244251
zIndex: DEFAULT_OPTIONS.zIndex,
245252
outerClassName: DEFAULT_OPTIONS.outerClassName,
246253
innerClassName: DEFAULT_OPTIONS.innerClassName,
254+
initialClassName: DEFAULT_OPTIONS.initialClassName,
247255
fixedClassName: DEFAULT_OPTIONS.fixedClassName,
248256
relativeClassName: DEFAULT_OPTIONS.relativeClassName,
249257
};
@@ -303,6 +311,8 @@ export class Adhesive {
303311
options.outerClassName ?? DEFAULT_OPTIONS.outerClassName;
304312
this.#options.innerClassName =
305313
options.innerClassName ?? DEFAULT_OPTIONS.innerClassName;
314+
this.#options.initialClassName =
315+
options.initialClassName ?? DEFAULT_OPTIONS.initialClassName;
306316
this.#options.fixedClassName =
307317
options.fixedClassName ?? DEFAULT_OPTIONS.fixedClassName;
308318
this.#options.relativeClassName =
@@ -394,6 +404,8 @@ export class Adhesive {
394404
this.#options.outerClassName = newOptions.outerClassName;
395405
if (newOptions.innerClassName)
396406
this.#options.innerClassName = newOptions.innerClassName;
407+
if (newOptions.initialClassName)
408+
this.#options.initialClassName = newOptions.initialClassName;
397409
if (newOptions.fixedClassName)
398410
this.#options.fixedClassName = newOptions.fixedClassName;
399411
if (newOptions.relativeClassName)
@@ -452,10 +464,13 @@ export class Adhesive {
452464
}
453465

454466
this.#outerWrapper = document.createElement("div");
455-
this.#outerWrapper.className = this.#options.outerClassName;
467+
this.#outerWrapper.classList.add(
468+
this.#options.outerClassName,
469+
this.#options.initialClassName,
470+
);
456471

457472
this.#innerWrapper = document.createElement("div");
458-
this.#innerWrapper.className = this.#options.innerClassName;
473+
this.#innerWrapper.classList.add(this.#options.innerClassName);
459474

460475
const parent = this.#targetEl.parentNode;
461476
parent.insertBefore(this.#outerWrapper, this.#targetEl);
@@ -646,6 +661,7 @@ export class Adhesive {
646661
this.#options.fixedClassName,
647662
this.#options.relativeClassName,
648663
);
664+
this.#outerWrapper.classList.add(this.#options.initialClassName);
649665

650666
this.#setState({
651667
status: ADHESIVE_STATUS.INITIAL,
@@ -674,7 +690,10 @@ export class Adhesive {
674690

675691
const outerStyle = this.#outerWrapper.style;
676692
outerStyle.height = `${this.#state.elementHeight}px`;
677-
this.#outerWrapper.classList.remove(this.#options.relativeClassName);
693+
this.#outerWrapper.classList.remove(
694+
this.#options.initialClassName,
695+
this.#options.relativeClassName,
696+
);
678697
this.#outerWrapper.classList.add(this.#options.fixedClassName);
679698

680699
if (!wasAlreadyFixed) {
@@ -703,7 +722,10 @@ export class Adhesive {
703722

704723
const outerStyle = this.#outerWrapper.style;
705724
outerStyle.height = "";
706-
this.#outerWrapper.classList.remove(this.#options.fixedClassName);
725+
this.#outerWrapper.classList.remove(
726+
this.#options.initialClassName,
727+
this.#options.fixedClassName,
728+
);
707729
this.#outerWrapper.classList.add(this.#options.relativeClassName);
708730

709731
this.#setState({

packages/react/README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ export function Component() {
7272
offset: 20,
7373
enabled,
7474
zIndex: 999,
75+
initialClassName: 'custom-initial',
7576
fixedClassName: 'custom-fixed',
7677
relativeClassName: 'custom-relative'
7778
}
@@ -142,5 +143,6 @@ For more control over the sticky behavior.
142143
| `enabled` | `boolean` | `true` | Whether to enable sticky behavior |
143144
| `outerClassName` | `string` | `'adhesive__outer'` | Class for the outer wrapper |
144145
| `innerClassName` | `string` | `'adhesive__inner'` | Class for the inner wrapper |
146+
| `initialClassName` | `string` | `'adhesive--initial'` | Class when element is in its initial state |
145147
| `fixedClassName` | `string` | `'adhesive--fixed'` | Class when element is sticky |
146-
| `relativeClassName` | `string` | `'adhesive--relative'` | Class when element returns to normal |
148+
| `relativeClassName` | `string` | `'adhesive--relative'` | Class when element reaches its boundary |

packages/react/src/components/AdhesiveContainer.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ export function AdhesiveContainer({
4646
zIndex,
4747
outerClassName,
4848
innerClassName,
49+
initialClassName,
4950
fixedClassName,
5051
relativeClassName,
5152
children,
@@ -63,6 +64,7 @@ export function AdhesiveContainer({
6364
zIndex,
6465
outerClassName,
6566
innerClassName,
67+
initialClassName,
6668
fixedClassName,
6769
relativeClassName,
6870
}),
@@ -75,6 +77,7 @@ export function AdhesiveContainer({
7577
zIndex,
7678
outerClassName,
7779
innerClassName,
80+
initialClassName,
7881
fixedClassName,
7982
relativeClassName,
8083
],

packages/vue/README.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ useAdhesive(
7777
offset: 20,
7878
enabled: enabled.value,
7979
zIndex: 999,
80+
initialClassName: 'custom-initial',
8081
fixedClassName: 'custom-fixed',
8182
relativeClassName: 'custom-relative'
8283
})
@@ -117,6 +118,7 @@ import { ref } from 'vue';
117118
const adhesiveOptions = ref({
118119
offset: 20,
119120
zIndex: 999,
121+
initialClassName: 'custom-initial',
120122
fixedClassName: 'custom-fixed',
121123
relativeClassName: 'custom-relative'
122124
});
@@ -196,7 +198,7 @@ A simple wrapper component that automatically applies sticky positioning to its
196198
- All props from `UseAdhesiveOptions` (see below)
197199

198200
> [!NOTE]
199-
> Class props like `outerClassName`, `innerClassName`, `fixedClassName`, and `relativeClassName` become `outerClass`, `innerClass`, `fixedClass`, and `relativeClass` in Vue for brevity.
201+
> Class props like `outerClassName`, `innerClassName`, `initialClassName`, `fixedClassName`, and `relativeClassName` become `outerClass`, `innerClass`, `initialClass`, `fixedClass`, and `relativeClass` in Vue for brevity.
200202
201203
```vue
202204
<AdhesiveContainer
@@ -229,8 +231,9 @@ For more control over the sticky behavior with full Vue reactivity support.
229231
| `enabled` | `boolean` | `true` | Whether to enable sticky behavior |
230232
| `outerClassName` | `string` | `'adhesive__outer'` | Class for the outer wrapper |
231233
| `innerClassName` | `string` | `'adhesive__inner'` | Class for the inner wrapper |
234+
| `initialClassName` | `string` | `'adhesive--initial'` | Class when element is in its initial state |
232235
| `fixedClassName` | `string` | `'adhesive--fixed'` | Class when element is sticky |
233-
| `relativeClassName` | `string` | `'adhesive--relative'` | Class when element returns to normal |
236+
| `relativeClassName` | `string` | `'adhesive--relative'` | Class when element reaches its boundary |
234237

235238
#### `v-adhesive` Directive
236239

packages/vue/src/components/AdhesiveContainer.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ type BaseProps = Omit<
1717
| "boundingRef"
1818
| "outerClassName"
1919
| "innerClassName"
20+
| "initialClassName"
2021
| "fixedClassName"
2122
| "relativeClassName"
2223
>;
@@ -28,13 +29,15 @@ type BaseProps = Omit<
2829
* Uses Vue-specific naming conventions (e.g., outerClass instead of outerClassName).
2930
*/
3031
export interface AdhesiveContainerProps extends BaseProps {
31-
/** CSS class for the outer wrapper element */
32+
/** CSS class for the outer wrapper element. */
3233
outerClass?: UseAdhesiveOptions["outerClassName"];
33-
/** CSS class for the inner wrapper element */
34+
/** CSS class for the inner wrapper element. */
3435
innerClass?: UseAdhesiveOptions["innerClassName"];
35-
/** CSS class name applied when the element is sticky */
36+
/** CSS class applied when the element is in its initial state. */
37+
initialClass?: UseAdhesiveOptions["initialClassName"];
38+
/** CSS class applied when the element is sticky. */
3639
fixedClass?: UseAdhesiveOptions["fixedClassName"];
37-
/** CSS class name applied when the element reaches its boundary */
40+
/** CSS class applied when the element reaches its boundary. */
3841
relativeClass?: UseAdhesiveOptions["relativeClassName"];
3942
}
4043

@@ -102,6 +105,10 @@ export const AdhesiveContainer: DefineComponent<AdhesiveContainerProps> =
102105
type: String as PropType<AdhesiveContainerProps["innerClass"]>,
103106
required: false,
104107
},
108+
initialClass: {
109+
type: String as PropType<AdhesiveContainerProps["initialClass"]>,
110+
required: false,
111+
},
105112
fixedClass: {
106113
type: String as PropType<AdhesiveContainerProps["fixedClass"]>,
107114
required: false,
@@ -122,6 +129,7 @@ export const AdhesiveContainer: DefineComponent<AdhesiveContainerProps> =
122129
zIndex: props.zIndex,
123130
outerClassName: props.outerClass,
124131
innerClassName: props.innerClass,
132+
initialClassName: props.initialClass,
125133
fixedClassName: props.fixedClass,
126134
relativeClassName: props.relativeClass,
127135
}));

playground/react/src/App.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ export function App() {
4848
className="custom-class"
4949
outerClassName="custom-outer"
5050
innerClassName="custom-inner"
51+
initialClassName="custom-initial"
5152
fixedClassName="custom-fixed"
5253
relativeClassName="custom-relative"
5354
style={{

playground/vue/src/App.vue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ useAdhesive(targetRef, () => ({
5252
class="custom-class"
5353
outer-class="custom-outer"
5454
inner-class="custom-inner"
55+
initial-class="custom-initial"
5556
fixed-class="custom-fixed"
5657
relative-class="custom-relative"
5758
style="width: 100%; height: 100px; background-color: lightblue"

test/unit/core.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1499,15 +1499,15 @@ describe("Core", () => {
14991499
}
15001500

15011501
expectElementToBeInState(adhesive, "INITIAL");
1502-
expectExactOuterClasses(["adhesive__outer"]);
1502+
expectExactOuterClasses(["adhesive__outer", "adhesive--initial"]);
15031503

15041504
await simulateScrollToPosition(200);
15051505
expectElementToBeInState(adhesive, "FIXED");
15061506
expectExactOuterClasses(["adhesive__outer", "adhesive--fixed"]);
15071507

15081508
await simulateScrollToPosition(0);
15091509
expectElementToBeInState(adhesive, "INITIAL");
1510-
expectExactOuterClasses(["adhesive__outer"]);
1510+
expectExactOuterClasses(["adhesive__outer", "adhesive--initial"]);
15111511

15121512
await simulateScrollToPosition(2000);
15131513
expectElementToBeInState(adhesive, "RELATIVE");
@@ -1519,7 +1519,7 @@ describe("Core", () => {
15191519

15201520
await simulateScrollToPosition(0);
15211521
expectElementToBeInState(adhesive, "INITIAL");
1522-
expectExactOuterClasses(["adhesive__outer"]);
1522+
expectExactOuterClasses(["adhesive__outer", "adhesive--initial"]);
15231523
});
15241524

15251525
it("handles rapid enable/disable cycles", () => {

0 commit comments

Comments
 (0)