@rcarls/rc-fab 0.3.2 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +58 -0
- package/README.md +5 -5
- package/dist/custom-elements.json +58 -56
- package/dist/{rc-fab-CKowGgDZ.js → rc-fab-CdJ-peVY.js} +41 -44
- package/dist/rc-fab-CdJ-peVY.js.map +1 -0
- package/dist/rc-fab-define.js +1 -1
- package/dist/rc-fab.js +1 -1
- package/dist/types/packages/rc-fab/src/rc-fab.d.ts +50 -19
- package/package.json +3 -3
- package/dist/rc-fab-CKowGgDZ.js.map +0 -1
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# @rcarls/rc-fab
|
|
2
|
+
|
|
3
|
+
## 0.4.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- @rcarls/rc-common@0.4.1
|
|
8
|
+
|
|
9
|
+
## 0.4.0
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 55e8ab5: Preserve UA-like control and surface styling until an optional theme supplies decorative tokens.
|
|
14
|
+
Restore themed button hover and pressed state layers, including a pointer-origin Material ripple.
|
|
15
|
+
- 9a4aa64: Fix `rc-fab`'s CSS custom property documentation to match the component's
|
|
16
|
+
actual `revert`/unset fallbacks (background, color, radius, shadow, size,
|
|
17
|
+
gap, padding, font, focus-ring, and transition properties all defer to the
|
|
18
|
+
native button or a packaged theme, not the previously documented literal
|
|
19
|
+
defaults), and document `position`, `scroll-reveal`, and the computed
|
|
20
|
+
`scroll-below-threshold` attribute.
|
|
21
|
+
- Updated dependencies [e57277f]
|
|
22
|
+
- Updated dependencies [ccca8e2]
|
|
23
|
+
- Updated dependencies [037b1b3]
|
|
24
|
+
- @rcarls/rc-common@0.4.0
|
|
25
|
+
|
|
26
|
+
## 0.3.2
|
|
27
|
+
|
|
28
|
+
### Patch Changes
|
|
29
|
+
|
|
30
|
+
- Updated dependencies [88b4086]
|
|
31
|
+
- @rcarls/rc-common@0.3.2
|
|
32
|
+
|
|
33
|
+
## 0.3.1
|
|
34
|
+
|
|
35
|
+
### Patch Changes
|
|
36
|
+
|
|
37
|
+
- @rcarls/rc-common@0.3.1
|
|
38
|
+
|
|
39
|
+
## 0.3.0
|
|
40
|
+
|
|
41
|
+
### Added
|
|
42
|
+
|
|
43
|
+
- Add scroll-reveal behavior for back-to-top floating action buttons.
|
|
44
|
+
|
|
45
|
+
### Changed
|
|
46
|
+
|
|
47
|
+
- Redesign the default behavior around the back-to-top use case.
|
|
48
|
+
- Update package metadata, README intro, and docs links.
|
|
49
|
+
|
|
50
|
+
### Fixed
|
|
51
|
+
|
|
52
|
+
- Generalize published FAB examples.
|
|
53
|
+
|
|
54
|
+
### Dependencies
|
|
55
|
+
|
|
56
|
+
- Sync internal dependencies to 0.3.0.
|
|
57
|
+
|
|
58
|
+
## 0.2.0
|
package/README.md
CHANGED
|
@@ -61,11 +61,11 @@ import '@rcarls/rc-fab/define';
|
|
|
61
61
|
| `--rc-fab-position` | `fixed` | CSS position value. Use `absolute` for layout-relative placement |
|
|
62
62
|
| `--rc-fab-scroll-threshold` | `300px` | Scroll distance before the FAB appears (requires `scroll-reveal`) |
|
|
63
63
|
| `--rc-fab-scroll-timeline` | `scroll(root block)` | Override scroll target for the CSS animation path (e.g. `scroll(nearest block)`) |
|
|
64
|
-
| `--rc-fab-radius` |
|
|
65
|
-
| `--rc-fab-size` |
|
|
66
|
-
| `--rc-fab-bg` |
|
|
67
|
-
| `--rc-fab-color` |
|
|
68
|
-
| `--rc-fab-shadow` |
|
|
64
|
+
| `--rc-fab-radius` | _(native)_ | Border-radius; defers to the native button's radius until set. Use `9999px` for pill-shaped, `50%` for a circle, or `1rem` for Material rounded-square |
|
|
65
|
+
| `--rc-fab-size` | _(native)_ | Height and minimum width; defers to native button sizing until set (packaged themes set `3.5rem`) |
|
|
66
|
+
| `--rc-fab-bg` | _(native)_ | Button background color; defers to the native button's background until set |
|
|
67
|
+
| `--rc-fab-color` | _(native)_ | Button foreground color; defers to the native button's color until set |
|
|
68
|
+
| `--rc-fab-shadow` | _(native)_ | Elevation shadow; defers to the native button's shadow until set |
|
|
69
69
|
| `--rc-fab-inset-block` | `1.5rem` | Distance from the block-axis edge |
|
|
70
70
|
| `--rc-fab-inset-inline` | `1.5rem` | Distance from the inline-axis edge |
|
|
71
71
|
|
|
@@ -70,94 +70,92 @@
|
|
|
70
70
|
"default": "10"
|
|
71
71
|
},
|
|
72
72
|
{
|
|
73
|
-
"description": "
|
|
74
|
-
"name": "--rc-fab-
|
|
75
|
-
"default": "ButtonFace"
|
|
73
|
+
"description": "Gap between icon and label text. Unset by default; packaged themes such as `rc-theme-material` set `0.5rem`.",
|
|
74
|
+
"name": "--rc-fab-gap"
|
|
76
75
|
},
|
|
77
76
|
{
|
|
78
|
-
"description": "
|
|
79
|
-
"name": "--rc-fab-
|
|
80
|
-
"default": "var(--rc-fab-bg)"
|
|
77
|
+
"description": "Minimum inline size and block size of the button. Unset by default (native button sizing applies); packaged themes such as `rc-theme-material` set `3.5rem`.",
|
|
78
|
+
"name": "--rc-fab-size"
|
|
81
79
|
},
|
|
82
80
|
{
|
|
83
|
-
"description": "Button
|
|
84
|
-
"name": "--rc-fab-
|
|
85
|
-
"default": "ButtonText"
|
|
81
|
+
"description": "Button block-axis padding (defers to native button padding when unset).",
|
|
82
|
+
"name": "--rc-fab-padding-block"
|
|
86
83
|
},
|
|
87
84
|
{
|
|
88
|
-
"description": "
|
|
89
|
-
"name": "--rc-fab-
|
|
90
|
-
"default": "3.5rem"
|
|
85
|
+
"description": "Button inline-axis padding (defers to native button padding when unset).",
|
|
86
|
+
"name": "--rc-fab-padding-inline"
|
|
91
87
|
},
|
|
92
88
|
{
|
|
93
|
-
"description": "
|
|
94
|
-
"name": "--rc-fab-
|
|
95
|
-
"default": "9999px"
|
|
89
|
+
"description": "Button background (defers to native button background when unset).",
|
|
90
|
+
"name": "--rc-fab-bg"
|
|
96
91
|
},
|
|
97
92
|
{
|
|
98
|
-
"description": "
|
|
99
|
-
"name": "--rc-fab-
|
|
100
|
-
"default": "none"
|
|
93
|
+
"description": "Button foreground color (defers to native button color when unset).",
|
|
94
|
+
"name": "--rc-fab-color"
|
|
101
95
|
},
|
|
102
96
|
{
|
|
103
|
-
"description": "
|
|
104
|
-
"name": "--rc-fab-
|
|
105
|
-
"default": "var(--rc-fab-shadow)"
|
|
97
|
+
"description": "Button border (defers to native button border when unset).",
|
|
98
|
+
"name": "--rc-fab-border"
|
|
106
99
|
},
|
|
107
100
|
{
|
|
108
|
-
"description": "
|
|
109
|
-
"name": "--rc-fab-
|
|
110
|
-
"default": "none"
|
|
101
|
+
"description": "Button border-radius (defers to native button radius when unset). Override to `9999px` for pill-shaped, `50%` for a circle (icon-only), `1rem` for Material rounded-square, etc.",
|
|
102
|
+
"name": "--rc-fab-radius"
|
|
111
103
|
},
|
|
112
104
|
{
|
|
113
|
-
"description": "
|
|
114
|
-
"name": "--rc-fab-
|
|
115
|
-
"default": "1rem"
|
|
105
|
+
"description": "Elevation shadow (defers to native button shadow when unset).",
|
|
106
|
+
"name": "--rc-fab-shadow"
|
|
116
107
|
},
|
|
117
108
|
{
|
|
118
|
-
"description": "
|
|
119
|
-
"name": "--rc-fab-
|
|
120
|
-
"default": "0.5rem"
|
|
109
|
+
"description": "Font family for label text (defers to native button font when unset).",
|
|
110
|
+
"name": "--rc-fab-font-family"
|
|
121
111
|
},
|
|
122
112
|
{
|
|
123
|
-
"description": "Font
|
|
124
|
-
"name": "--rc-fab-font-
|
|
125
|
-
"default": "inherit"
|
|
113
|
+
"description": "Font size for label text (defers to native button font when unset).",
|
|
114
|
+
"name": "--rc-fab-font-size"
|
|
126
115
|
},
|
|
127
116
|
{
|
|
128
|
-
"description": "Font
|
|
129
|
-
"name": "--rc-fab-font-
|
|
130
|
-
"default": "0.875rem"
|
|
117
|
+
"description": "Font weight for label text (defers to native button font when unset).",
|
|
118
|
+
"name": "--rc-fab-font-weight"
|
|
131
119
|
},
|
|
132
120
|
{
|
|
133
|
-
"description": "
|
|
134
|
-
"name": "--rc-fab-
|
|
135
|
-
"default": "500"
|
|
121
|
+
"description": "Letter spacing for label text (defers to native button styling when unset).",
|
|
122
|
+
"name": "--rc-fab-letter-spacing"
|
|
136
123
|
},
|
|
137
124
|
{
|
|
138
|
-
"description": "
|
|
139
|
-
"name": "--rc-fab-
|
|
140
|
-
"default": "0.00625em"
|
|
125
|
+
"description": "Transition shorthand for hover/active state changes (defers to native button transition when unset).",
|
|
126
|
+
"name": "--rc-fab-transition"
|
|
141
127
|
},
|
|
142
128
|
{
|
|
143
|
-
"description": "
|
|
144
|
-
"name": "--rc-fab-
|
|
145
|
-
"default": "2px solid currentColor"
|
|
129
|
+
"description": "Hover background (defers to native `:hover` styling when unset).",
|
|
130
|
+
"name": "--rc-fab-bg-hover"
|
|
146
131
|
},
|
|
147
132
|
{
|
|
148
|
-
"description": "
|
|
149
|
-
"name": "--rc-fab-
|
|
150
|
-
"default": "2px"
|
|
133
|
+
"description": "Hover shadow (defers to native `:hover` styling when unset).",
|
|
134
|
+
"name": "--rc-fab-shadow-hover"
|
|
151
135
|
},
|
|
152
136
|
{
|
|
153
|
-
"description": "
|
|
154
|
-
"name": "--rc-fab-
|
|
155
|
-
"default": "0.38"
|
|
137
|
+
"description": "Pressed shadow (defers to native `:active` styling when unset).",
|
|
138
|
+
"name": "--rc-fab-shadow-active"
|
|
156
139
|
},
|
|
157
140
|
{
|
|
158
|
-
"description": "
|
|
159
|
-
"name": "--rc-fab-
|
|
160
|
-
|
|
141
|
+
"description": "Transform applied while pressed, e.g. `scale(0.96)` (defers to native `:active` styling when unset).",
|
|
142
|
+
"name": "--rc-fab-active-transform"
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
"description": "Focus ring style (defers to native `:focus-visible` styling when unset).",
|
|
146
|
+
"name": "--rc-fab-focus-ring"
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
"description": "Focus ring offset (defers to native `:focus-visible` styling when unset).",
|
|
150
|
+
"name": "--rc-fab-focus-ring-offset"
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
"description": "Opacity applied when the button is disabled (defers to native `:disabled` styling when unset).",
|
|
154
|
+
"name": "--rc-fab-disabled-opacity"
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
"description": "Shadow applied when the button is disabled (defers to native `:disabled` styling when unset).",
|
|
158
|
+
"name": "--rc-fab-disabled-shadow"
|
|
161
159
|
},
|
|
162
160
|
{
|
|
163
161
|
"description": "Scroll distance at which the FAB becomes fully visible. Requires the `scroll-reveal` attribute. The JS fallback reads this value once on connect; px units only.",
|
|
@@ -230,22 +228,26 @@
|
|
|
230
228
|
],
|
|
231
229
|
"attributes": [
|
|
232
230
|
{
|
|
231
|
+
"description": "Viewport corner where the FAB is anchored. Uses logical inline/block directions.",
|
|
233
232
|
"name": "position",
|
|
234
233
|
"type": {
|
|
235
234
|
"text": "'bottom-end' | 'bottom-start' | 'top-end' | 'top-start'"
|
|
236
235
|
},
|
|
237
236
|
"default": "'bottom-end'",
|
|
238
|
-
"description": "Viewport corner where the FAB is anchored. Uses logical inline/block directions.",
|
|
239
237
|
"fieldName": "position"
|
|
240
238
|
},
|
|
241
239
|
{
|
|
240
|
+
"description": "Reveal the FAB only after the page scrolls past `--rc-fab-scroll-threshold` (default 300 px). Uses CSS scroll-driven animations; falls back to a passive scroll listener in unsupported browsers.",
|
|
242
241
|
"name": "scroll-reveal",
|
|
243
242
|
"type": {
|
|
244
243
|
"text": "boolean"
|
|
245
244
|
},
|
|
246
245
|
"default": "false",
|
|
247
|
-
"description": "Reveal the FAB only after the page scrolls past `--rc-fab-scroll-threshold` (default 300 px). Uses CSS scroll-driven animations; falls back to a passive scroll listener in unsupported browsers.",
|
|
248
246
|
"fieldName": "scrollReveal"
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
"description": "Present when `scroll-reveal` is active and the page hasn't scrolled past `--rc-fab-scroll-threshold` yet. Only used by the JS scroll-driven-animation fallback; unused when the browser supports `animation-timeline: scroll()`.",
|
|
250
|
+
"name": "scroll-below-threshold"
|
|
249
251
|
}
|
|
250
252
|
],
|
|
251
253
|
"superclass": {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { css as b, LitElement as
|
|
1
|
+
import { css as b, LitElement as v, html as d } from "lit";
|
|
2
2
|
import { property as n } from "lit/decorators.js";
|
|
3
|
-
import { NativeChildController as
|
|
3
|
+
import { NativeChildController as h, ScrollObserverController as f, findNearestScrollAncestor as p } from "@rcarls/rc-common";
|
|
4
4
|
const m = b`
|
|
5
5
|
:host {
|
|
6
6
|
position: var(--rc-fab-position, fixed);
|
|
@@ -35,52 +35,48 @@ const m = b`
|
|
|
35
35
|
display: inline-flex;
|
|
36
36
|
align-items: center;
|
|
37
37
|
justify-content: center;
|
|
38
|
-
gap: var(--rc-fab-gap
|
|
38
|
+
gap: var(--rc-fab-gap);
|
|
39
39
|
|
|
40
|
-
min-width: var(--rc-fab-size
|
|
41
|
-
height: var(--rc-fab-size
|
|
42
|
-
padding-block:
|
|
43
|
-
padding-inline: var(--rc-fab-padding-inline,
|
|
40
|
+
min-width: var(--rc-fab-size);
|
|
41
|
+
height: var(--rc-fab-size);
|
|
42
|
+
padding-block: var(--rc-fab-padding-block, revert);
|
|
43
|
+
padding-inline: var(--rc-fab-padding-inline, revert);
|
|
44
44
|
|
|
45
|
-
background: var(--rc-fab-bg,
|
|
46
|
-
color: var(--rc-fab-color,
|
|
45
|
+
background: var(--rc-fab-bg, revert);
|
|
46
|
+
color: var(--rc-fab-color, revert);
|
|
47
47
|
|
|
48
|
-
border:
|
|
49
|
-
border-radius: var(--rc-fab-radius,
|
|
50
|
-
box-shadow: var(--rc-fab-shadow,
|
|
48
|
+
border: var(--rc-fab-border, revert);
|
|
49
|
+
border-radius: var(--rc-fab-radius, revert);
|
|
50
|
+
box-shadow: var(--rc-fab-shadow, revert);
|
|
51
51
|
|
|
52
|
-
font-family: var(--rc-fab-font-family,
|
|
53
|
-
font-size: var(--rc-fab-font-size,
|
|
54
|
-
font-weight: var(--rc-fab-font-weight,
|
|
55
|
-
letter-spacing: var(--rc-fab-letter-spacing,
|
|
52
|
+
font-family: var(--rc-fab-font-family, revert);
|
|
53
|
+
font-size: var(--rc-fab-font-size, revert);
|
|
54
|
+
font-weight: var(--rc-fab-font-weight, revert);
|
|
55
|
+
letter-spacing: var(--rc-fab-letter-spacing, revert);
|
|
56
56
|
white-space: nowrap;
|
|
57
57
|
|
|
58
|
-
|
|
59
|
-
transition:
|
|
60
|
-
background var(--rc-fab-transition-duration, var(--rc-motion-duration, 200ms)) ease,
|
|
61
|
-
box-shadow var(--rc-fab-transition-duration, var(--rc-motion-duration, 200ms)) ease,
|
|
62
|
-
transform var(--rc-fab-transition-duration, var(--rc-motion-duration, 200ms)) ease;
|
|
58
|
+
transition: var(--rc-fab-transition, revert);
|
|
63
59
|
}
|
|
64
60
|
|
|
65
61
|
:host(:has(button:hover)) ::slotted(button) {
|
|
66
|
-
background: var(--rc-fab-bg-hover,
|
|
67
|
-
box-shadow: var(--rc-fab-shadow-hover,
|
|
62
|
+
background: var(--rc-fab-bg-hover, revert);
|
|
63
|
+
box-shadow: var(--rc-fab-shadow-hover, revert);
|
|
68
64
|
}
|
|
69
65
|
|
|
70
66
|
:host(:has(button:active)) ::slotted(button) {
|
|
71
|
-
box-shadow: var(--rc-fab-shadow-active,
|
|
72
|
-
transform:
|
|
67
|
+
box-shadow: var(--rc-fab-shadow-active, revert);
|
|
68
|
+
transform: var(--rc-fab-active-transform, revert);
|
|
73
69
|
}
|
|
74
70
|
|
|
75
71
|
:host(:has(button:focus-visible)) ::slotted(button) {
|
|
76
|
-
outline: var(--rc-fab-focus-ring,
|
|
77
|
-
outline-offset: var(--rc-fab-focus-ring-offset,
|
|
72
|
+
outline: var(--rc-fab-focus-ring, revert);
|
|
73
|
+
outline-offset: var(--rc-fab-focus-ring-offset, revert);
|
|
78
74
|
}
|
|
79
75
|
|
|
80
76
|
:host(:has(button:disabled)) ::slotted(button) {
|
|
81
|
-
opacity: var(--rc-fab-disabled-opacity,
|
|
77
|
+
opacity: var(--rc-fab-disabled-opacity, revert);
|
|
82
78
|
pointer-events: none;
|
|
83
|
-
box-shadow:
|
|
79
|
+
box-shadow: var(--rc-fab-disabled-shadow, revert);
|
|
84
80
|
}
|
|
85
81
|
|
|
86
82
|
@keyframes rc-fab-scroll-reveal {
|
|
@@ -165,40 +161,41 @@ const m = b`
|
|
|
165
161
|
}
|
|
166
162
|
}
|
|
167
163
|
`;
|
|
168
|
-
var u = Object.defineProperty, c = (
|
|
169
|
-
for (var
|
|
170
|
-
(
|
|
171
|
-
return
|
|
164
|
+
var u = Object.defineProperty, c = (l, e, r, y) => {
|
|
165
|
+
for (var t = void 0, i = l.length - 1, a; i >= 0; i--)
|
|
166
|
+
(a = l[i]) && (t = a(e, r, t) || t);
|
|
167
|
+
return t && u(e, r, t), t;
|
|
172
168
|
};
|
|
173
|
-
const s = class s extends
|
|
169
|
+
const s = class s extends v {
|
|
174
170
|
constructor() {
|
|
175
|
-
super(), this.position = "bottom-end", this.scrollReveal = !1, new
|
|
171
|
+
super(), this.position = "bottom-end", this.scrollReveal = !1, new h(this, {
|
|
176
172
|
selector: ":scope > button",
|
|
177
173
|
observe: !0,
|
|
178
174
|
onMissing: () => {
|
|
179
175
|
}
|
|
180
176
|
});
|
|
181
177
|
}
|
|
182
|
-
updated(
|
|
183
|
-
super.updated(
|
|
178
|
+
updated(e) {
|
|
179
|
+
super.updated(e), (e.has("scrollReveal") || this.scrollReveal && !this._scrollCtrl) && this._syncScrollFallback();
|
|
184
180
|
}
|
|
185
181
|
_syncScrollFallback() {
|
|
186
|
-
if (this._scrollCtrl && (this._scrollCtrl.setOptions({ disabled: !0 }), this.removeController(this._scrollCtrl), this._scrollCtrl = void 0, this.removeAttribute("scroll-below-threshold")), !this.scrollReveal || CSS.supports("animation-timeline: scroll()"))
|
|
187
|
-
|
|
182
|
+
if (this._scrollCtrl && (this._scrollCtrl.setOptions({ disabled: !0 }), this.removeController(this._scrollCtrl), this._scrollCtrl = void 0, this.removeAttribute("scroll-below-threshold")), !this.scrollReveal || CSS.supports("animation-timeline: scroll()"))
|
|
183
|
+
return;
|
|
184
|
+
const e = this._getThreshold();
|
|
188
185
|
this._scrollCtrl = new f(this, {
|
|
189
186
|
target: () => p(this),
|
|
190
|
-
threshold:
|
|
187
|
+
threshold: e,
|
|
191
188
|
onScroll: (r) => {
|
|
192
|
-
this.toggleAttribute("scroll-below-threshold", r <
|
|
189
|
+
this.toggleAttribute("scroll-below-threshold", r < e);
|
|
193
190
|
}
|
|
194
191
|
});
|
|
195
192
|
}
|
|
196
193
|
_getThreshold() {
|
|
197
|
-
const
|
|
194
|
+
const e = getComputedStyle(this).getPropertyValue("--rc-fab-scroll-threshold").trim(), r = parseFloat(e);
|
|
198
195
|
return Number.isFinite(r) ? r : 300;
|
|
199
196
|
}
|
|
200
197
|
render() {
|
|
201
|
-
return
|
|
198
|
+
return d`<slot></slot>`;
|
|
202
199
|
}
|
|
203
200
|
};
|
|
204
201
|
s.styles = m;
|
|
@@ -212,4 +209,4 @@ c([
|
|
|
212
209
|
export {
|
|
213
210
|
o as R
|
|
214
211
|
};
|
|
215
|
-
//# sourceMappingURL=rc-fab-
|
|
212
|
+
//# sourceMappingURL=rc-fab-CdJ-peVY.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rc-fab-CdJ-peVY.js","sources":["../src/rc-fab.styles.ts","../src/rc-fab.ts"],"sourcesContent":["import { css } from 'lit';\n\nexport const fabStyles = css`\n :host {\n position: var(--rc-fab-position, fixed);\n inset-block-end: var(--rc-fab-inset-block, 1.5rem);\n inset-inline-end: var(--rc-fab-inset-inline, 1.5rem);\n z-index: var(--rc-fab-z-index, 10);\n display: inline-flex;\n }\n\n :host([hidden]) {\n display: none;\n }\n\n :host([position='bottom-start']) {\n inset-inline-end: unset;\n inset-inline-start: var(--rc-fab-inset-inline, 1.5rem);\n }\n\n :host([position='top-end']) {\n inset-block-end: unset;\n inset-block-start: var(--rc-fab-inset-block, 1.5rem);\n }\n\n :host([position='top-start']) {\n inset-block-end: unset;\n inset-block-start: var(--rc-fab-inset-block, 1.5rem);\n inset-inline-end: unset;\n inset-inline-start: var(--rc-fab-inset-inline, 1.5rem);\n }\n\n ::slotted(button) {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--rc-fab-gap);\n\n min-width: var(--rc-fab-size);\n height: var(--rc-fab-size);\n padding-block: var(--rc-fab-padding-block, revert);\n padding-inline: var(--rc-fab-padding-inline, revert);\n\n background: var(--rc-fab-bg, revert);\n color: var(--rc-fab-color, revert);\n\n border: var(--rc-fab-border, revert);\n border-radius: var(--rc-fab-radius, revert);\n box-shadow: var(--rc-fab-shadow, revert);\n\n font-family: var(--rc-fab-font-family, revert);\n font-size: var(--rc-fab-font-size, revert);\n font-weight: var(--rc-fab-font-weight, revert);\n letter-spacing: var(--rc-fab-letter-spacing, revert);\n white-space: nowrap;\n\n transition: var(--rc-fab-transition, revert);\n }\n\n :host(:has(button:hover)) ::slotted(button) {\n background: var(--rc-fab-bg-hover, revert);\n box-shadow: var(--rc-fab-shadow-hover, revert);\n }\n\n :host(:has(button:active)) ::slotted(button) {\n box-shadow: var(--rc-fab-shadow-active, revert);\n transform: var(--rc-fab-active-transform, revert);\n }\n\n :host(:has(button:focus-visible)) ::slotted(button) {\n outline: var(--rc-fab-focus-ring, revert);\n outline-offset: var(--rc-fab-focus-ring-offset, revert);\n }\n\n :host(:has(button:disabled)) ::slotted(button) {\n opacity: var(--rc-fab-disabled-opacity, revert);\n pointer-events: none;\n box-shadow: var(--rc-fab-disabled-shadow, revert);\n }\n\n @keyframes rc-fab-scroll-reveal {\n from {\n opacity: 0;\n visibility: hidden;\n }\n 50% {\n visibility: visible;\n }\n to {\n opacity: 1;\n visibility: visible;\n }\n }\n\n @supports (animation-timeline: scroll()) {\n :host([scroll-reveal]) {\n /*\n * Override for embedded/demo contexts where root does not scroll:\n * style=\"--rc-fab-scroll-timeline: scroll(nearest block)\"\n */\n --rc-fab-scroll-timeline: scroll(root block);\n\n animation-name: rc-fab-scroll-reveal;\n animation-duration: 1ms; /* required; scroll position drives progress, not time */\n animation-timing-function: linear;\n animation-fill-mode: both;\n animation-timeline: var(--rc-fab-scroll-timeline);\n animation-range: calc(var(--rc-fab-scroll-threshold, 300px) - 100px)\n var(--rc-fab-scroll-threshold, 300px);\n }\n\n /* Keyboard escape hatch: always visible when the button has focus */\n :host([scroll-reveal]):has(button:focus-visible) {\n animation: none;\n opacity: 1;\n visibility: visible;\n }\n\n @media (prefers-reduced-motion: reduce) {\n :host([scroll-reveal]) {\n animation-range: var(--rc-fab-scroll-threshold, 300px) var(--rc-fab-scroll-threshold, 300px);\n }\n }\n }\n\n /*\n * JS fallback for browsers without scroll-driven animation support.\n * [scroll-below-threshold] is toggled by ScrollObserverController;\n * transitions replicate the CSS animation's show/hide behavior.\n */\n @supports not (animation-timeline: scroll()) {\n /* visible state: visibility snaps immediately, opacity fades in */\n :host([scroll-reveal]) {\n transition:\n opacity 200ms linear,\n visibility 0s linear;\n }\n\n /* hidden state: opacity fades first, visibility snaps off after delay */\n :host([scroll-reveal][scroll-below-threshold]) {\n opacity: 0;\n visibility: hidden;\n transition:\n opacity 200ms linear,\n visibility 0s linear 200ms;\n }\n\n /* Keyboard escape hatch */\n :host([scroll-reveal][scroll-below-threshold]):has(button:focus-visible) {\n opacity: 1;\n visibility: visible;\n transition: none;\n }\n\n @media (prefers-reduced-motion: reduce) {\n :host([scroll-reveal]),\n :host([scroll-reveal][scroll-below-threshold]) {\n transition: none;\n }\n }\n }\n`;\n\nexport default fabStyles;\n","import { LitElement, html } from 'lit';\nimport type { PropertyValues } from 'lit';\nimport { property } from 'lit/decorators.js';\n\nimport {\n NativeChildController,\n ScrollObserverController,\n findNearestScrollAncestor,\n warnMissingDirectChild,\n} from '@rcarls/rc-common';\n\nimport fabStyles from './rc-fab.styles.js';\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'rc-fab': RCFab;\n }\n}\n\n/**\n * Sticky floating action button modeled after the Material 3 Floating action button,\n * wrapping a consumer-supplied button with scroll-aware visibility.\n *\n * Adapted from the Material Design FAB component, use this for \"back to top\",\n * sticky CTAs, chat launchers, and of course as FABs in your Material Design PWA.\n *\n * Place a native `<button>` as the direct child. The button's own accessible\n * name (text content or `aria-label`) becomes the FAB's accessible name.\n *\n * Icons go inside the button alongside or instead of visible text.\n *\n * @see {@link https://richardcarls.github.io/rc-webcomponents/components/rc-fab rc-fab docs}\n * @see {@link https://m3.material.io/components/floating-action-button/overview Material 3 Floating action button}\n *\n * @example Back to top (scroll-triggered)\n * ```html\n * <rc-fab scroll-reveal>\n * <button type=\"button\" aria-label=\"Back to top\" onclick=\"scrollTo({top:0,behavior:'smooth'})\">\n * <span aria-hidden=\"true\">↑</span>\n * </button>\n * </rc-fab>\n * ```\n *\n * @example Icon-only FAB (always visible)\n * ```html\n * <rc-fab>\n * <button type=\"button\" aria-label=\"Create\">\n * <span class=\"material-symbols-outlined\" aria-hidden=\"true\">add</span>\n * </button>\n * </rc-fab>\n * ```\n *\n * @example Extended FAB (icon + visible label)\n * ```html\n * <rc-fab>\n * <button type=\"button\">\n * <span class=\"material-symbols-outlined\" aria-hidden=\"true\">edit</span>\n * Compose\n * </button>\n * </rc-fab>\n * ```\n *\n * @slot default - The native `<button>` element. The button's own accessible\n * name (text content or `aria-label`) serves as the FAB's accessible name.\n *\n * @attr position - Viewport corner where the FAB is anchored.\n * @attr scroll-reveal - Reveal the FAB only after the page scrolls past\n * `--rc-fab-scroll-threshold`.\n * @attr [scroll-below-threshold] - Present when `scroll-reveal` is active and the page hasn't\n * scrolled past `--rc-fab-scroll-threshold` yet. Only used by the JS scroll-driven-animation\n * fallback; unused when the browser supports `animation-timeline: scroll()`.\n *\n * @cssprop [--rc-fab-position=fixed] - CSS position value. Override to `absolute` for layout-relative placement or `sticky` for scroll-snapping.\n * @cssprop [--rc-fab-inset-block=1.5rem] - Distance from the block-axis edge.\n * @cssprop [--rc-fab-inset-inline=1.5rem] - Distance from the inline-axis edge.\n * @cssprop [--rc-fab-z-index=10] - Stacking order.\n * @cssprop [--rc-fab-gap] - Gap between icon and label text. Unset by default; packaged themes\n * such as `rc-theme-material` set `0.5rem`.\n * @cssprop [--rc-fab-size] - Minimum inline size and block size of the button. Unset by\n * default (native button sizing applies); packaged themes such as `rc-theme-material` set\n * `3.5rem`.\n * @cssprop [--rc-fab-padding-block] - Button block-axis padding (defers to native button\n * padding when unset).\n * @cssprop [--rc-fab-padding-inline] - Button inline-axis padding (defers to native button\n * padding when unset).\n * @cssprop [--rc-fab-bg] - Button background (defers to native button background when unset).\n * @cssprop [--rc-fab-color] - Button foreground color (defers to native button color when\n * unset).\n * @cssprop [--rc-fab-border] - Button border (defers to native button border when unset).\n * @cssprop [--rc-fab-radius] - Button border-radius (defers to native button radius when\n * unset). Override to `9999px` for pill-shaped, `50%` for a circle (icon-only), `1rem` for\n * Material rounded-square, etc.\n * @cssprop [--rc-fab-shadow] - Elevation shadow (defers to native button shadow when unset).\n * @cssprop [--rc-fab-font-family] - Font family for label text (defers to native button font\n * when unset).\n * @cssprop [--rc-fab-font-size] - Font size for label text (defers to native button font when\n * unset).\n * @cssprop [--rc-fab-font-weight] - Font weight for label text (defers to native button font\n * when unset).\n * @cssprop [--rc-fab-letter-spacing] - Letter spacing for label text (defers to native button\n * styling when unset).\n * @cssprop [--rc-fab-transition] - Transition shorthand for hover/active state changes (defers\n * to native button transition when unset).\n * @cssprop [--rc-fab-bg-hover] - Hover background (defers to native `:hover` styling when\n * unset).\n * @cssprop [--rc-fab-shadow-hover] - Hover shadow (defers to native `:hover` styling when\n * unset).\n * @cssprop [--rc-fab-shadow-active] - Pressed shadow (defers to native `:active` styling when\n * unset).\n * @cssprop [--rc-fab-active-transform] - Transform applied while pressed, e.g. `scale(0.96)`\n * (defers to native `:active` styling when unset).\n * @cssprop [--rc-fab-focus-ring] - Focus ring style (defers to native `:focus-visible` styling\n * when unset).\n * @cssprop [--rc-fab-focus-ring-offset] - Focus ring offset (defers to native `:focus-visible`\n * styling when unset).\n * @cssprop [--rc-fab-disabled-opacity] - Opacity applied when the button is disabled (defers to\n * native `:disabled` styling when unset).\n * @cssprop [--rc-fab-disabled-shadow] - Shadow applied when the button is disabled (defers to\n * native `:disabled` styling when unset).\n * @cssprop [--rc-fab-scroll-threshold=300px] - Scroll distance at which the FAB becomes fully visible. Requires the `scroll-reveal` attribute. The JS fallback reads this value once on connect; px units only.\n * @cssprop [--rc-fab-scroll-timeline=scroll(root block)] - The `animation-timeline` value used for scroll-reveal. Override to target a different scroller, e.g. `scroll(nearest block)` for embedded contexts. CSS path only; the JS fallback discovers the nearest scrollable ancestor automatically.\n */\nexport class RCFab extends LitElement {\n static override styles = fabStyles;\n\n private _scrollCtrl?: ScrollObserverController;\n\n /** Viewport corner where the FAB is anchored. Uses logical inline/block directions. */\n @property({ type: String, reflect: true })\n position: 'bottom-end' | 'bottom-start' | 'top-end' | 'top-start' = 'bottom-end';\n\n /** Reveal the FAB only after the page scrolls past `--rc-fab-scroll-threshold` (default 300 px). Uses CSS scroll-driven animations; falls back to a passive scroll listener in unsupported browsers. */\n @property({ type: Boolean, attribute: 'scroll-reveal', reflect: true })\n scrollReveal = false;\n\n constructor() {\n super();\n\n new NativeChildController<HTMLButtonElement>(this, {\n selector: ':scope > button',\n observe: true,\n onMissing: () => {\n if (import.meta.env.DEV) {\n warnMissingDirectChild(this, {\n selector: ':scope > button',\n message:\n '[rc-fab] No direct child <button> found. Place a native <button> inside <rc-fab>.',\n });\n }\n },\n });\n }\n\n protected override updated(changed: PropertyValues): void {\n super.updated(changed);\n\n if (changed.has('scrollReveal') || (this.scrollReveal && !this._scrollCtrl)) {\n this._syncScrollFallback();\n }\n }\n\n private _syncScrollFallback(): void {\n if (this._scrollCtrl) {\n this._scrollCtrl.setOptions({ disabled: true });\n this.removeController(this._scrollCtrl);\n this._scrollCtrl = undefined;\n\n this.removeAttribute('scroll-below-threshold');\n }\n\n if (!this.scrollReveal || CSS.supports('animation-timeline: scroll()')) {\n return;\n }\n\n const threshold = this._getThreshold();\n\n this._scrollCtrl = new ScrollObserverController(this, {\n target: () => findNearestScrollAncestor(this),\n threshold,\n onScroll: (scrollTop) => {\n this.toggleAttribute('scroll-below-threshold', scrollTop < threshold);\n },\n });\n }\n\n private _getThreshold(): number {\n const raw = getComputedStyle(this).getPropertyValue('--rc-fab-scroll-threshold').trim();\n const n = parseFloat(raw);\n\n return Number.isFinite(n) ? n : 300;\n }\n\n protected override render() {\n return html`<slot></slot>`;\n }\n}\n\nexport default RCFab;\n"],"names":["fabStyles","css","_RCFab","LitElement","NativeChildController","changed","threshold","ScrollObserverController","findNearestScrollAncestor","scrollTop","raw","n","html","RCFab","__decorateClass","property"],"mappings":";;;AAEO,MAAMA,IAAYC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;;;;ACwHlB,MAAMC,IAAN,MAAMA,UAAcC,EAAW;AAAA,EAapC,cAAc;AACZ,UAAA,GAPF,KAAA,WAAoE,cAIpE,KAAA,eAAe,IAKb,IAAIC,EAAyC,MAAM;AAAA,MACjD,UAAU;AAAA,MACV,SAAS;AAAA,MACT,WAAW,MAAM;AAAA,MAQjB;AAAA,IAAA,CACD;AAAA,EACH;AAAA,EAEmB,QAAQC,GAA+B;AACxD,UAAM,QAAQA,CAAO,IAEjBA,EAAQ,IAAI,cAAc,KAAM,KAAK,gBAAgB,CAAC,KAAK,gBAC7D,KAAK,oBAAA;AAAA,EAET;AAAA,EAEQ,sBAA4B;AASlC,QARI,KAAK,gBACP,KAAK,YAAY,WAAW,EAAE,UAAU,IAAM,GAC9C,KAAK,iBAAiB,KAAK,WAAW,GACtC,KAAK,cAAc,QAEnB,KAAK,gBAAgB,wBAAwB,IAG3C,CAAC,KAAK,gBAAgB,IAAI,SAAS,8BAA8B;AACnE;AAGF,UAAMC,IAAY,KAAK,cAAA;AAEvB,SAAK,cAAc,IAAIC,EAAyB,MAAM;AAAA,MACpD,QAAQ,MAAMC,EAA0B,IAAI;AAAA,MAC5C,WAAAF;AAAA,MACA,UAAU,CAACG,MAAc;AACvB,aAAK,gBAAgB,0BAA0BA,IAAYH,CAAS;AAAA,MACtE;AAAA,IAAA,CACD;AAAA,EACH;AAAA,EAEQ,gBAAwB;AAC9B,UAAMI,IAAM,iBAAiB,IAAI,EAAE,iBAAiB,2BAA2B,EAAE,KAAA,GAC3EC,IAAI,WAAWD,CAAG;AAExB,WAAO,OAAO,SAASC,CAAC,IAAIA,IAAI;AAAA,EAClC;AAAA,EAEmB,SAAS;AAC1B,WAAOC;AAAA,EACT;AACF;AAxEEV,EAAgB,SAASF;AADpB,IAAMa,IAANX;AAOLY,EAAA;AAAA,EADCC,EAAS,EAAE,MAAM,QAAQ,SAAS,IAAM;AAAA,GAN9BF,EAOX,WAAA,UAAA;AAIAC,EAAA;AAAA,EADCC,EAAS,EAAE,MAAM,SAAS,WAAW,iBAAiB,SAAS,IAAM;AAAA,GAV3DF,EAWX,WAAA,cAAA;"}
|
package/dist/rc-fab-define.js
CHANGED
package/dist/rc-fab.js
CHANGED
|
@@ -50,31 +50,62 @@ declare global {
|
|
|
50
50
|
* @slot default - The native `<button>` element. The button's own accessible
|
|
51
51
|
* name (text content or `aria-label`) serves as the FAB's accessible name.
|
|
52
52
|
*
|
|
53
|
+
* @attr position - Viewport corner where the FAB is anchored.
|
|
54
|
+
* @attr scroll-reveal - Reveal the FAB only after the page scrolls past
|
|
55
|
+
* `--rc-fab-scroll-threshold`.
|
|
56
|
+
* @attr [scroll-below-threshold] - Present when `scroll-reveal` is active and the page hasn't
|
|
57
|
+
* scrolled past `--rc-fab-scroll-threshold` yet. Only used by the JS scroll-driven-animation
|
|
58
|
+
* fallback; unused when the browser supports `animation-timeline: scroll()`.
|
|
59
|
+
*
|
|
53
60
|
* @cssprop [--rc-fab-position=fixed] - CSS position value. Override to `absolute` for layout-relative placement or `sticky` for scroll-snapping.
|
|
54
61
|
* @cssprop [--rc-fab-inset-block=1.5rem] - Distance from the block-axis edge.
|
|
55
62
|
* @cssprop [--rc-fab-inset-inline=1.5rem] - Distance from the inline-axis edge.
|
|
56
63
|
* @cssprop [--rc-fab-z-index=10] - Stacking order.
|
|
57
|
-
* @cssprop [--rc-fab-
|
|
58
|
-
*
|
|
59
|
-
* @cssprop [--rc-fab-
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
* @cssprop [--rc-fab-
|
|
63
|
-
*
|
|
64
|
-
* @cssprop [--rc-fab-
|
|
65
|
-
*
|
|
66
|
-
* @cssprop [--rc-fab-
|
|
67
|
-
* @cssprop [--rc-fab-
|
|
68
|
-
*
|
|
69
|
-
* @cssprop [--rc-fab-
|
|
70
|
-
* @cssprop [--rc-fab-
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
* @cssprop [--rc-fab-
|
|
74
|
-
* @cssprop [--rc-fab-
|
|
64
|
+
* @cssprop [--rc-fab-gap] - Gap between icon and label text. Unset by default; packaged themes
|
|
65
|
+
* such as `rc-theme-material` set `0.5rem`.
|
|
66
|
+
* @cssprop [--rc-fab-size] - Minimum inline size and block size of the button. Unset by
|
|
67
|
+
* default (native button sizing applies); packaged themes such as `rc-theme-material` set
|
|
68
|
+
* `3.5rem`.
|
|
69
|
+
* @cssprop [--rc-fab-padding-block] - Button block-axis padding (defers to native button
|
|
70
|
+
* padding when unset).
|
|
71
|
+
* @cssprop [--rc-fab-padding-inline] - Button inline-axis padding (defers to native button
|
|
72
|
+
* padding when unset).
|
|
73
|
+
* @cssprop [--rc-fab-bg] - Button background (defers to native button background when unset).
|
|
74
|
+
* @cssprop [--rc-fab-color] - Button foreground color (defers to native button color when
|
|
75
|
+
* unset).
|
|
76
|
+
* @cssprop [--rc-fab-border] - Button border (defers to native button border when unset).
|
|
77
|
+
* @cssprop [--rc-fab-radius] - Button border-radius (defers to native button radius when
|
|
78
|
+
* unset). Override to `9999px` for pill-shaped, `50%` for a circle (icon-only), `1rem` for
|
|
79
|
+
* Material rounded-square, etc.
|
|
80
|
+
* @cssprop [--rc-fab-shadow] - Elevation shadow (defers to native button shadow when unset).
|
|
81
|
+
* @cssprop [--rc-fab-font-family] - Font family for label text (defers to native button font
|
|
82
|
+
* when unset).
|
|
83
|
+
* @cssprop [--rc-fab-font-size] - Font size for label text (defers to native button font when
|
|
84
|
+
* unset).
|
|
85
|
+
* @cssprop [--rc-fab-font-weight] - Font weight for label text (defers to native button font
|
|
86
|
+
* when unset).
|
|
87
|
+
* @cssprop [--rc-fab-letter-spacing] - Letter spacing for label text (defers to native button
|
|
88
|
+
* styling when unset).
|
|
89
|
+
* @cssprop [--rc-fab-transition] - Transition shorthand for hover/active state changes (defers
|
|
90
|
+
* to native button transition when unset).
|
|
91
|
+
* @cssprop [--rc-fab-bg-hover] - Hover background (defers to native `:hover` styling when
|
|
92
|
+
* unset).
|
|
93
|
+
* @cssprop [--rc-fab-shadow-hover] - Hover shadow (defers to native `:hover` styling when
|
|
94
|
+
* unset).
|
|
95
|
+
* @cssprop [--rc-fab-shadow-active] - Pressed shadow (defers to native `:active` styling when
|
|
96
|
+
* unset).
|
|
97
|
+
* @cssprop [--rc-fab-active-transform] - Transform applied while pressed, e.g. `scale(0.96)`
|
|
98
|
+
* (defers to native `:active` styling when unset).
|
|
99
|
+
* @cssprop [--rc-fab-focus-ring] - Focus ring style (defers to native `:focus-visible` styling
|
|
100
|
+
* when unset).
|
|
101
|
+
* @cssprop [--rc-fab-focus-ring-offset] - Focus ring offset (defers to native `:focus-visible`
|
|
102
|
+
* styling when unset).
|
|
103
|
+
* @cssprop [--rc-fab-disabled-opacity] - Opacity applied when the button is disabled (defers to
|
|
104
|
+
* native `:disabled` styling when unset).
|
|
105
|
+
* @cssprop [--rc-fab-disabled-shadow] - Shadow applied when the button is disabled (defers to
|
|
106
|
+
* native `:disabled` styling when unset).
|
|
75
107
|
* @cssprop [--rc-fab-scroll-threshold=300px] - Scroll distance at which the FAB becomes fully visible. Requires the `scroll-reveal` attribute. The JS fallback reads this value once on connect; px units only.
|
|
76
108
|
* @cssprop [--rc-fab-scroll-timeline=scroll(root block)] - The `animation-timeline` value used for scroll-reveal. Override to target a different scroller, e.g. `scroll(nearest block)` for embedded contexts. CSS path only; the JS fallback discovers the nearest scrollable ancestor automatically.
|
|
77
|
-
*
|
|
78
109
|
*/
|
|
79
110
|
export declare class RCFab extends LitElement {
|
|
80
111
|
static styles: import('lit').CSSResult;
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "0.
|
|
6
|
+
"version": "0.4.1",
|
|
7
7
|
"description": "Sticky floating action button modeled after the Material 3 Floating action button, wrapping a consumer-supplied button with scroll-aware visibility.",
|
|
8
8
|
"repository": {
|
|
9
9
|
"type": "git",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"test:browser:firefox": "vitest --run --project=firefox"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@rcarls/rc-common": "
|
|
48
|
+
"@rcarls/rc-common": "0.4.1"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"@custom-elements-manifest/analyzer": "0.11.0",
|
|
@@ -62,4 +62,4 @@
|
|
|
62
62
|
"peerDependencies": {
|
|
63
63
|
"lit": "^3.0.0"
|
|
64
64
|
}
|
|
65
|
-
}
|
|
65
|
+
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"rc-fab-CKowGgDZ.js","sources":["../src/rc-fab.styles.ts","../src/rc-fab.ts"],"sourcesContent":["import { css } from 'lit';\n\nexport const fabStyles = css`\n :host {\n position: var(--rc-fab-position, fixed);\n inset-block-end: var(--rc-fab-inset-block, 1.5rem);\n inset-inline-end: var(--rc-fab-inset-inline, 1.5rem);\n z-index: var(--rc-fab-z-index, 10);\n display: inline-flex;\n }\n\n :host([hidden]) {\n display: none;\n }\n\n :host([position='bottom-start']) {\n inset-inline-end: unset;\n inset-inline-start: var(--rc-fab-inset-inline, 1.5rem);\n }\n\n :host([position='top-end']) {\n inset-block-end: unset;\n inset-block-start: var(--rc-fab-inset-block, 1.5rem);\n }\n\n :host([position='top-start']) {\n inset-block-end: unset;\n inset-block-start: var(--rc-fab-inset-block, 1.5rem);\n inset-inline-end: unset;\n inset-inline-start: var(--rc-fab-inset-inline, 1.5rem);\n }\n\n ::slotted(button) {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--rc-fab-gap, 0.5rem);\n\n min-width: var(--rc-fab-size, 3.5rem);\n height: var(--rc-fab-size, 3.5rem);\n padding-block: 0;\n padding-inline: var(--rc-fab-padding-inline, 1rem);\n\n background: var(--rc-fab-bg, ButtonFace);\n color: var(--rc-fab-color, ButtonText);\n\n border: none;\n border-radius: var(--rc-fab-radius, 9999px);\n box-shadow: var(--rc-fab-shadow, var(--rc-shadow-level2, none));\n\n font-family: var(--rc-fab-font-family, var(--rc-font-family, inherit));\n font-size: var(--rc-fab-font-size, var(--rc-font-size, 0.875rem));\n font-weight: var(--rc-fab-font-weight, 500);\n letter-spacing: var(--rc-fab-letter-spacing, 0.00625em);\n white-space: nowrap;\n\n cursor: pointer;\n transition:\n background var(--rc-fab-transition-duration, var(--rc-motion-duration, 200ms)) ease,\n box-shadow var(--rc-fab-transition-duration, var(--rc-motion-duration, 200ms)) ease,\n transform var(--rc-fab-transition-duration, var(--rc-motion-duration, 200ms)) ease;\n }\n\n :host(:has(button:hover)) ::slotted(button) {\n background: var(--rc-fab-bg-hover, var(--rc-fab-bg, ButtonFace));\n box-shadow: var(--rc-fab-shadow-hover, var(--rc-fab-shadow, var(--rc-shadow-level3, none)));\n }\n\n :host(:has(button:active)) ::slotted(button) {\n box-shadow: var(--rc-fab-shadow-active, none);\n transform: scale(0.96);\n }\n\n :host(:has(button:focus-visible)) ::slotted(button) {\n outline: var(--rc-fab-focus-ring, var(--rc-focus-ring, 2px solid currentColor));\n outline-offset: var(--rc-fab-focus-ring-offset, var(--rc-focus-ring-offset, 2px));\n }\n\n :host(:has(button:disabled)) ::slotted(button) {\n opacity: var(--rc-fab-disabled-opacity, var(--rc-disabled-opacity, 0.38));\n pointer-events: none;\n box-shadow: none;\n }\n\n @keyframes rc-fab-scroll-reveal {\n from {\n opacity: 0;\n visibility: hidden;\n }\n 50% {\n visibility: visible;\n }\n to {\n opacity: 1;\n visibility: visible;\n }\n }\n\n @supports (animation-timeline: scroll()) {\n :host([scroll-reveal]) {\n /*\n * Override for embedded/demo contexts where root does not scroll:\n * style=\"--rc-fab-scroll-timeline: scroll(nearest block)\"\n */\n --rc-fab-scroll-timeline: scroll(root block);\n\n animation-name: rc-fab-scroll-reveal;\n animation-duration: 1ms; /* required; scroll position drives progress, not time */\n animation-timing-function: linear;\n animation-fill-mode: both;\n animation-timeline: var(--rc-fab-scroll-timeline);\n animation-range: calc(var(--rc-fab-scroll-threshold, 300px) - 100px)\n var(--rc-fab-scroll-threshold, 300px);\n }\n\n /* Keyboard escape hatch: always visible when the button has focus */\n :host([scroll-reveal]):has(button:focus-visible) {\n animation: none;\n opacity: 1;\n visibility: visible;\n }\n\n @media (prefers-reduced-motion: reduce) {\n :host([scroll-reveal]) {\n animation-range: var(--rc-fab-scroll-threshold, 300px) var(--rc-fab-scroll-threshold, 300px);\n }\n }\n }\n\n /*\n * JS fallback for browsers without scroll-driven animation support.\n * [scroll-below-threshold] is toggled by ScrollObserverController;\n * transitions replicate the CSS animation's show/hide behavior.\n */\n @supports not (animation-timeline: scroll()) {\n /* visible state: visibility snaps immediately, opacity fades in */\n :host([scroll-reveal]) {\n transition:\n opacity 200ms linear,\n visibility 0s linear;\n }\n\n /* hidden state: opacity fades first, visibility snaps off after delay */\n :host([scroll-reveal][scroll-below-threshold]) {\n opacity: 0;\n visibility: hidden;\n transition:\n opacity 200ms linear,\n visibility 0s linear 200ms;\n }\n\n /* Keyboard escape hatch */\n :host([scroll-reveal][scroll-below-threshold]):has(button:focus-visible) {\n opacity: 1;\n visibility: visible;\n transition: none;\n }\n\n @media (prefers-reduced-motion: reduce) {\n :host([scroll-reveal]),\n :host([scroll-reveal][scroll-below-threshold]) {\n transition: none;\n }\n }\n }\n`;\n\nexport default fabStyles;\n","import { LitElement, html } from 'lit';\nimport type { PropertyValues } from 'lit';\nimport { property } from 'lit/decorators.js';\n\nimport {\n NativeChildController,\n ScrollObserverController,\n findNearestScrollAncestor,\n warnMissingDirectChild,\n} from '@rcarls/rc-common';\n\nimport fabStyles from './rc-fab.styles.js';\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'rc-fab': RCFab;\n }\n}\n\n/**\n * Sticky floating action button modeled after the Material 3 Floating action button,\n * wrapping a consumer-supplied button with scroll-aware visibility.\n *\n * Adapted from the Material Design FAB component, use this for \"back to top\",\n * sticky CTAs, chat launchers, and of course as FABs in your Material Design PWA.\n *\n * Place a native `<button>` as the direct child. The button's own accessible\n * name (text content or `aria-label`) becomes the FAB's accessible name.\n *\n * Icons go inside the button alongside or instead of visible text.\n *\n * @see {@link https://richardcarls.github.io/rc-webcomponents/components/rc-fab rc-fab docs}\n * @see {@link https://m3.material.io/components/floating-action-button/overview Material 3 Floating action button}\n *\n * @example Back to top (scroll-triggered)\n * ```html\n * <rc-fab scroll-reveal>\n * <button type=\"button\" aria-label=\"Back to top\" onclick=\"scrollTo({top:0,behavior:'smooth'})\">\n * <span aria-hidden=\"true\">↑</span>\n * </button>\n * </rc-fab>\n * ```\n *\n * @example Icon-only FAB (always visible)\n * ```html\n * <rc-fab>\n * <button type=\"button\" aria-label=\"Create\">\n * <span class=\"material-symbols-outlined\" aria-hidden=\"true\">add</span>\n * </button>\n * </rc-fab>\n * ```\n *\n * @example Extended FAB (icon + visible label)\n * ```html\n * <rc-fab>\n * <button type=\"button\">\n * <span class=\"material-symbols-outlined\" aria-hidden=\"true\">edit</span>\n * Compose\n * </button>\n * </rc-fab>\n * ```\n *\n * @slot default - The native `<button>` element. The button's own accessible\n * name (text content or `aria-label`) serves as the FAB's accessible name.\n *\n * @cssprop [--rc-fab-position=fixed] - CSS position value. Override to `absolute` for layout-relative placement or `sticky` for scroll-snapping.\n * @cssprop [--rc-fab-inset-block=1.5rem] - Distance from the block-axis edge.\n * @cssprop [--rc-fab-inset-inline=1.5rem] - Distance from the inline-axis edge.\n * @cssprop [--rc-fab-z-index=10] - Stacking order.\n * @cssprop [--rc-fab-bg=ButtonFace] - Button background colour.\n * @cssprop [--rc-fab-bg-hover=var(--rc-fab-bg)] - Hover background colour.\n * @cssprop [--rc-fab-color=ButtonText] - Button foreground colour.\n * @cssprop [--rc-fab-size=3.5rem] - Height and minimum width.\n * @cssprop [--rc-fab-radius=9999px] - Border-radius. Default is pill-shaped. Override to `50%` for a circle (icon-only), `1rem` for Material rounded-square, etc.\n * @cssprop [--rc-fab-shadow=none] - Elevation shadow.\n * @cssprop [--rc-fab-shadow-hover=var(--rc-fab-shadow)] - Hover shadow.\n * @cssprop [--rc-fab-shadow-active=none] - Pressed shadow.\n * @cssprop [--rc-fab-padding-inline=1rem] - Inline padding.\n * @cssprop [--rc-fab-gap=0.5rem] - Gap between icon and label text.\n * @cssprop [--rc-fab-font-family=inherit] - Font family for label text.\n * @cssprop [--rc-fab-font-size=0.875rem] - Font size for label text.\n * @cssprop [--rc-fab-font-weight=500] - Font weight for label text.\n * @cssprop [--rc-fab-letter-spacing=0.00625em] - Letter spacing for label text.\n * @cssprop [--rc-fab-focus-ring=2px solid currentColor] - Focus ring style.\n * @cssprop [--rc-fab-focus-ring-offset=2px] - Focus ring offset.\n * @cssprop [--rc-fab-disabled-opacity=0.38] - Opacity applied when the button is disabled.\n * @cssprop [--rc-fab-transition-duration=200ms] - Transition speed for hover and active states.\n * @cssprop [--rc-fab-scroll-threshold=300px] - Scroll distance at which the FAB becomes fully visible. Requires the `scroll-reveal` attribute. The JS fallback reads this value once on connect; px units only.\n * @cssprop [--rc-fab-scroll-timeline=scroll(root block)] - The `animation-timeline` value used for scroll-reveal. Override to target a different scroller, e.g. `scroll(nearest block)` for embedded contexts. CSS path only; the JS fallback discovers the nearest scrollable ancestor automatically.\n *\n */\nexport class RCFab extends LitElement {\n static override styles = fabStyles;\n\n private _scrollCtrl?: ScrollObserverController;\n\n /** Viewport corner where the FAB is anchored. Uses logical inline/block directions. */\n @property({ type: String, reflect: true })\n position: 'bottom-end' | 'bottom-start' | 'top-end' | 'top-start' = 'bottom-end';\n\n /** Reveal the FAB only after the page scrolls past `--rc-fab-scroll-threshold` (default 300 px). Uses CSS scroll-driven animations; falls back to a passive scroll listener in unsupported browsers. */\n @property({ type: Boolean, attribute: 'scroll-reveal', reflect: true })\n scrollReveal = false;\n\n constructor() {\n super();\n\n new NativeChildController<HTMLButtonElement>(this, {\n selector: ':scope > button',\n observe: true,\n onMissing: () => {\n if (import.meta.env.DEV) {\n warnMissingDirectChild(this, {\n selector: ':scope > button',\n message:\n '[rc-fab] No direct child <button> found. Place a native <button> inside <rc-fab>.',\n });\n }\n },\n });\n }\n\n protected override updated(changed: PropertyValues): void {\n super.updated(changed);\n\n if (changed.has('scrollReveal') || (this.scrollReveal && !this._scrollCtrl)) {\n this._syncScrollFallback();\n }\n }\n\n private _syncScrollFallback(): void {\n if (this._scrollCtrl) {\n this._scrollCtrl.setOptions({ disabled: true });\n this.removeController(this._scrollCtrl);\n this._scrollCtrl = undefined;\n\n this.removeAttribute('scroll-below-threshold');\n }\n\n if (!this.scrollReveal || CSS.supports('animation-timeline: scroll()')) return;\n\n const threshold = this._getThreshold();\n\n this._scrollCtrl = new ScrollObserverController(this, {\n target: () => findNearestScrollAncestor(this),\n threshold,\n onScroll: (scrollTop) => {\n this.toggleAttribute('scroll-below-threshold', scrollTop < threshold);\n },\n });\n }\n\n private _getThreshold(): number {\n const raw = getComputedStyle(this).getPropertyValue('--rc-fab-scroll-threshold').trim();\n const n = parseFloat(raw);\n\n return Number.isFinite(n) ? n : 300;\n }\n\n protected override render() {\n return html`<slot></slot>`;\n }\n}\n\nexport default RCFab;\n"],"names":["fabStyles","css","_RCFab","LitElement","NativeChildController","changed","threshold","ScrollObserverController","findNearestScrollAncestor","scrollTop","raw","n","html","RCFab","__decorateClass","property"],"mappings":";;;AAEO,MAAMA,IAAYC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;;;;ACyFlB,MAAMC,IAAN,MAAMA,UAAcC,EAAW;AAAA,EAapC,cAAc;AACZ,UAAA,GAPF,KAAA,WAAoE,cAIpE,KAAA,eAAe,IAKb,IAAIC,EAAyC,MAAM;AAAA,MACjD,UAAU;AAAA,MACV,SAAS;AAAA,MACT,WAAW,MAAM;AAAA,MAQjB;AAAA,IAAA,CACD;AAAA,EACH;AAAA,EAEmB,QAAQC,GAA+B;AACxD,UAAM,QAAQA,CAAO,IAEjBA,EAAQ,IAAI,cAAc,KAAM,KAAK,gBAAgB,CAAC,KAAK,gBAC7D,KAAK,oBAAA;AAAA,EAET;AAAA,EAEQ,sBAA4B;AASlC,QARI,KAAK,gBACP,KAAK,YAAY,WAAW,EAAE,UAAU,IAAM,GAC9C,KAAK,iBAAiB,KAAK,WAAW,GACtC,KAAK,cAAc,QAEnB,KAAK,gBAAgB,wBAAwB,IAG3C,CAAC,KAAK,gBAAgB,IAAI,SAAS,8BAA8B,EAAG;AAExE,UAAMC,IAAY,KAAK,cAAA;AAEvB,SAAK,cAAc,IAAIC,EAAyB,MAAM;AAAA,MACpD,QAAQ,MAAMC,EAA0B,IAAI;AAAA,MAC5C,WAAAF;AAAA,MACA,UAAU,CAACG,MAAc;AACvB,aAAK,gBAAgB,0BAA0BA,IAAYH,CAAS;AAAA,MACtE;AAAA,IAAA,CACD;AAAA,EACH;AAAA,EAEQ,gBAAwB;AAC9B,UAAMI,IAAM,iBAAiB,IAAI,EAAE,iBAAiB,2BAA2B,EAAE,KAAA,GAC3EC,IAAI,WAAWD,CAAG;AAExB,WAAO,OAAO,SAASC,CAAC,IAAIA,IAAI;AAAA,EAClC;AAAA,EAEmB,SAAS;AAC1B,WAAOC;AAAA,EACT;AACF;AAtEEV,EAAgB,SAASF;AADpB,IAAMa,IAANX;AAOLY,EAAA;AAAA,EADCC,EAAS,EAAE,MAAM,QAAQ,SAAS,IAAM;AAAA,GAN9BF,EAOX,WAAA,UAAA;AAIAC,EAAA;AAAA,EADCC,EAAS,EAAE,MAAM,SAAS,WAAW,iBAAiB,SAAS,IAAM;AAAA,GAV3DF,EAWX,WAAA,cAAA;"}
|