@cocreate/clipboard 1.5.0 → 2.0.0
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/.github/FUNDING.yml +1 -1
- package/.github/workflows/automated.yml +18 -7
- package/CHANGELOG.md +44 -19
- package/CONTRIBUTING.md +6 -6
- package/CoCreate.config.js +1 -1
- package/README.md +112 -83
- package/demo/index.html +1 -1
- package/docs/index.html +4 -4
- package/package.json +8 -8
- package/src/index.js +313 -145
- package/test/clipboard.test.cjs +104 -0
package/.github/FUNDING.yml
CHANGED
|
@@ -12,16 +12,27 @@ jobs:
|
|
|
12
12
|
- name: Checkout repository
|
|
13
13
|
uses: actions/checkout@v7
|
|
14
14
|
|
|
15
|
-
- name: Sync package.json description to GitHub
|
|
15
|
+
- name: Sync package.json description and keywords to GitHub repository
|
|
16
16
|
env:
|
|
17
|
-
|
|
17
|
+
GH_TOKEN: ${{ secrets.GITHUB }}
|
|
18
18
|
run: |
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
if [ ! -f package.json ]; then
|
|
20
|
+
echo "No package.json found; skipping repository metadata sync."
|
|
21
|
+
exit 0
|
|
22
|
+
fi
|
|
23
|
+
|
|
24
|
+
DESCRIPTION=$(jq -r '.description // empty' package.json)
|
|
25
|
+
if [ -n "$DESCRIPTION" ]; then
|
|
26
|
+
gh api --method PATCH "repos/${GITHUB_REPOSITORY}" -f description="$DESCRIPTION"
|
|
27
|
+
else
|
|
28
|
+
echo "No package description found; leaving GitHub description unchanged."
|
|
29
|
+
fi
|
|
30
|
+
|
|
31
|
+
if jq -e '.keywords | type == "array"' package.json >/dev/null; then
|
|
32
|
+
TOPICS=$(jq -c '.keywords | map(ascii_downcase | gsub("[^a-z0-9-]+"; "-") | gsub("(^-+|-+$)"; "") | .[0:50]) | map(select(length > 0)) | unique | .[0:20]' package.json)
|
|
33
|
+
printf '{"names":%s}' "$TOPICS" | gh api --method PUT -H "Accept: application/vnd.github+json" "repos/${GITHUB_REPOSITORY}/topics" --input -
|
|
23
34
|
else
|
|
24
|
-
echo "No
|
|
35
|
+
echo "No package keywords found; leaving GitHub topics unchanged."
|
|
25
36
|
fi
|
|
26
37
|
|
|
27
38
|
release:
|
package/CHANGELOG.md
CHANGED
|
@@ -1,58 +1,83 @@
|
|
|
1
|
-
|
|
1
|
+
# [2.0.0](https://github.com/CoCreateJS/clipboard/compare/v1.2.2...v2.0.0) (2026-11-02)
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
### Bug Fixes
|
|
5
5
|
|
|
6
|
-
*
|
|
6
|
+
* add Deno specific entries to .gitignore ([df7ed52](https://github.com/CoCreateJS/clipboard/commit/df7ed528a533cef9158573f2293b7ae11adaa484))
|
|
7
|
+
* bump [@cocreate](https://github.com/cocreate) dependencies ([8b7a4b9](https://github.com/CoCreateJS/clipboard/commit/8b7a4b9fbc0e888eb4f95caa979ccb1ad4244dcb))
|
|
8
|
+
* bump [@cocreate](https://github.com/cocreate) package versions ([bcb7ccb](https://github.com/CoCreateJS/clipboard/commit/bcb7ccbe1123d9815142e4a781284903704f7b00))
|
|
9
|
+
* bump versions ([9745901](https://github.com/CoCreateJS/clipboard/commit/9745901c54b7a61ddb691d2bd12a690df058d474))
|
|
10
|
+
* bump versions ([791f6c9](https://github.com/CoCreateJS/clipboard/commit/791f6c992c41a4c0eaff12880fc228e6556247ec))
|
|
11
|
+
* remove trailing slash from node_modules entry in .gitignore ([e81f21a](https://github.com/CoCreateJS/clipboard/commit/e81f21ad45d395cf448a64be00023471ca155928))
|
|
12
|
+
* update git url ([2f8d441](https://github.com/CoCreateJS/clipboard/commit/2f8d441936df84a9ae6fec3516de2832a7f02fbf))
|
|
13
|
+
* update GitHub Actions to use latest versions and sync package.json description ([e5fe7ed](https://github.com/CoCreateJS/clipboard/commit/e5fe7edbd09b1af22fc466f980e4558664e2d9cb))
|
|
7
14
|
|
|
8
|
-
|
|
15
|
+
|
|
16
|
+
### Features
|
|
17
|
+
|
|
18
|
+
* align CoCreate repository and package naming ([eb6179d](https://github.com/CoCreateJS/clipboard/commit/eb6179d40d7251a1e78cd8fc40508c6355b0546a))
|
|
19
|
+
* replace uuid import and usage with utils ([05054ce](https://github.com/CoCreateJS/clipboard/commit/05054ce8454cfd26c16f10c64c83f89955d94ba4))
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
### BREAKING CHANGES
|
|
23
|
+
|
|
24
|
+
* consumers must use the renamed CoCreate package and repository identities; the previous prefixed names are no longer supported.
|
|
25
|
+
|
|
26
|
+
## [1.2.2](https://github.com/CoCreateJS/CoCreate-clipboard/compare/v1.2.1...v1.2.2) (2026-07-19)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
### Bug Fixes
|
|
30
|
+
|
|
31
|
+
* set default branch to main ([65e7f7f](https://github.com/CoCreateJS/CoCreate-clipboard/commit/65e7f7f02e62af923a1a3417730dbc408559d165))
|
|
32
|
+
|
|
33
|
+
## [1.2.1](https://github.com/CoCreateJS/CoCreate-clipboard/compare/v1.2.0...v1.2.1) (2026-07-17)
|
|
9
34
|
|
|
10
35
|
|
|
11
36
|
### Bug Fixes
|
|
12
37
|
|
|
13
|
-
* bump dependencies ([b965074](https://github.com/
|
|
38
|
+
* bump dependencies ([b965074](https://github.com/CoCreateJS/CoCreate-clipboard/commit/b96507433d68007e378ab6aa061f7a925a110e6a))
|
|
14
39
|
|
|
15
|
-
# [1.2.0](https://github.com/
|
|
40
|
+
# [1.2.0](https://github.com/CoCreateJS/CoCreate-clipboard/compare/v1.1.2...v1.2.0) (2026-07-17)
|
|
16
41
|
|
|
17
42
|
|
|
18
43
|
### Features
|
|
19
44
|
|
|
20
|
-
* update automated workflow and release configuration for improved semantic release handling ([2b573e6](https://github.com/
|
|
45
|
+
* update automated workflow and release configuration for improved semantic release handling ([2b573e6](https://github.com/CoCreateJS/CoCreate-clipboard/commit/2b573e6c36e92f8615769e0408e1ebaa0bef236b))
|
|
21
46
|
|
|
22
|
-
## [1.1.2](https://github.com/
|
|
47
|
+
## [1.1.2](https://github.com/CoCreateJS/CoCreate-clipboard/compare/v1.1.1...v1.1.2) (2026-07-11)
|
|
23
48
|
|
|
24
49
|
|
|
25
50
|
### Bug Fixes
|
|
26
51
|
|
|
27
|
-
* bump package versions ([4802ef1](https://github.com/
|
|
28
|
-
* removed post install and add add packages to apprved list ([1b2701f](https://github.com/
|
|
29
|
-
* semantic version handling ([d5f849d](https://github.com/
|
|
52
|
+
* bump package versions ([4802ef1](https://github.com/CoCreateJS/CoCreate-clipboard/commit/4802ef11961218c2ed071f2c08f39f00130b839e))
|
|
53
|
+
* removed post install and add add packages to apprved list ([1b2701f](https://github.com/CoCreateJS/CoCreate-clipboard/commit/1b2701f833e4e7b8b39d5c6078428f1585b01935))
|
|
54
|
+
* semantic version handling ([d5f849d](https://github.com/CoCreateJS/CoCreate-clipboard/commit/d5f849dc16702952e359e3a227dd7c2da6f6f2f4))
|
|
30
55
|
|
|
31
|
-
## [1.1.1](https://github.com/
|
|
56
|
+
## [1.1.1](https://github.com/CoCreateJS/CoCreate-clipboard/compare/v1.1.0...v1.1.1) (2026-01-18)
|
|
32
57
|
|
|
33
58
|
|
|
34
59
|
### Bug Fixes
|
|
35
60
|
|
|
36
|
-
* update worklow ([c54f11a](https://github.com/
|
|
61
|
+
* update worklow ([c54f11a](https://github.com/CoCreateJS/CoCreate-clipboard/commit/c54f11a9df499522afc5805d99538099d7314704))
|
|
37
62
|
|
|
38
|
-
# [1.1.0](https://github.com/
|
|
63
|
+
# [1.1.0](https://github.com/CoCreateJS/CoCreate-clipboard/compare/v1.0.0...v1.1.0) (2025-05-01)
|
|
39
64
|
|
|
40
65
|
|
|
41
66
|
### Bug Fixes
|
|
42
67
|
|
|
43
|
-
* added css-loader ([03d2c12](https://github.com/
|
|
44
|
-
* update [@cocreate](https://github.com/cocreate) dependencies ([22fa8a8](https://github.com/
|
|
45
|
-
* update query attributes ([48297b3](https://github.com/
|
|
46
|
-
* webpack.config and devdependencies ([bd741fc](https://github.com/
|
|
68
|
+
* added css-loader ([03d2c12](https://github.com/CoCreateJS/CoCreate-clipboard/commit/03d2c12b1f56ced186877939c15e81932ed6e871))
|
|
69
|
+
* update [@cocreate](https://github.com/cocreate) dependencies ([22fa8a8](https://github.com/CoCreateJS/CoCreate-clipboard/commit/22fa8a8329001ba632b2430ae564c941090a6c09))
|
|
70
|
+
* update query attributes ([48297b3](https://github.com/CoCreateJS/CoCreate-clipboard/commit/48297b37408552c5c765d812124ff510e65e7751))
|
|
71
|
+
* webpack.config and devdependencies ([bd741fc](https://github.com/CoCreateJS/CoCreate-clipboard/commit/bd741fca61bd135a811f743689d2ba5faaaa13bd))
|
|
47
72
|
|
|
48
73
|
|
|
49
74
|
### Features
|
|
50
75
|
|
|
51
|
-
* handles rich text editors and plain text editors ([3cfd47b](https://github.com/
|
|
76
|
+
* handles rich text editors and plain text editors ([3cfd47b](https://github.com/CoCreateJS/CoCreate-clipboard/commit/3cfd47ba9f3837f87aa7c46644edcfb28dc304b9))
|
|
52
77
|
|
|
53
78
|
# 1.0.0 (2024-12-14)
|
|
54
79
|
|
|
55
80
|
|
|
56
81
|
### Features
|
|
57
82
|
|
|
58
|
-
* Initial Release ([8421d97](https://github.com/
|
|
83
|
+
* Initial Release ([8421d97](https://github.com/CoCreateJS/CoCreate-clipboard/commit/8421d971b1ee2fda99a41d87de41ce767b9c49ee))
|
package/CONTRIBUTING.md
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
# Contributing to CoCreate-clipboard
|
|
2
2
|
|
|
3
|
-
This project is work of [many contributors](https://github.com/
|
|
4
|
-
You're encouraged to submit [pull requests](https://github.com/
|
|
5
|
-
[propose features and discuss issues](https://github.com/
|
|
3
|
+
This project is work of [many contributors](https://github.com/CoCreateJS/clipboard/graphs/contributors).
|
|
4
|
+
You're encouraged to submit [pull requests](https://github.com/CoCreateJS/clipboard/pulls),
|
|
5
|
+
[propose features and discuss issues](https://github.com/CoCreateJS/clipboard/issues).
|
|
6
6
|
|
|
7
7
|
In the examples below, substitute your Github username for `contributor` in URLs.
|
|
8
8
|
|
|
9
9
|
## Fork the Project
|
|
10
10
|
|
|
11
|
-
Fork the [project on Github](https://github.com/
|
|
11
|
+
Fork the [project on Github](https://github.com/CoCreateJS/clipboard) and check out your copy.
|
|
12
12
|
|
|
13
13
|
```
|
|
14
14
|
git clone https://github.com/contributor/CoCreate-clipboard.git
|
|
15
15
|
cd CoCreate-clipboard
|
|
16
|
-
git remote add upstream https://github.com/
|
|
16
|
+
git remote add upstream https://github.com/CoCreateJS/clipboard.git
|
|
17
17
|
```
|
|
18
18
|
|
|
19
19
|
## Create a Topic Branch
|
|
@@ -74,7 +74,7 @@ git push origin my-feature-branch
|
|
|
74
74
|
|
|
75
75
|
## Make a Pull Request
|
|
76
76
|
|
|
77
|
-
Go to [https://github.com/
|
|
77
|
+
Go to [https://github.com/CoCreateJS/clipboard](https://github.com/CoCreateJS/clipboard) and select your feature branch.
|
|
78
78
|
Click the 'Pull Request' button and fill out the form. Pull requests are usually reviewed within a few days.
|
|
79
79
|
|
|
80
80
|
## Rebase
|
package/CoCreate.config.js
CHANGED
|
@@ -10,7 +10,7 @@ module.exports = {
|
|
|
10
10
|
name: "index.html",
|
|
11
11
|
path: "/docs/clipboard/index.html",
|
|
12
12
|
src: "{{./docs/index.html}}",
|
|
13
|
-
host: ["
|
|
13
|
+
host: ["CoCreateJS.com", "general.CoCreateJS.com"],
|
|
14
14
|
directory: "clipboard",
|
|
15
15
|
"content-type": "{{content-type}}",
|
|
16
16
|
public: "true"
|
package/README.md
CHANGED
|
@@ -1,83 +1,112 @@
|
|
|
1
|
-
# CoCreate-clipboard
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-

|
|
11
|
-
|
|
12
|
-

|
|
4
|
+
|
|
5
|
+

|
|
6
|
+

|
|
7
|
+

|
|
8
|
+

|
|
9
|
+

|
|
10
|
+

|
|
11
|
+
|
|
12
|
+

|
|
13
|
+
|
|
14
|
+
## [Docs & Demo](https://CoCreateJS.com/docs/clipboard)
|
|
15
|
+
|
|
16
|
+
For a complete guide and working demo refer to the [doumentation](https://CoCreateJS.com/docs/clipboard)
|
|
17
|
+
|
|
18
|
+
## Declarative controls
|
|
19
|
+
|
|
20
|
+
Use `clipboard` on an element to copy its own value, or `clipboard-query` to
|
|
21
|
+
select the source. Either attribute initializes a click listener, including on
|
|
22
|
+
dynamically added elements.
|
|
23
|
+
|
|
24
|
+
```html
|
|
25
|
+
<button clipboard>Copy this text</button>
|
|
26
|
+
<button clipboard-query="#message">Copy message</button>
|
|
27
|
+
<textarea id="message">Hello</textarea>
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
The trigger's `clipboard` attribute reflects `copying`, `copied`, or `error`.
|
|
31
|
+
These are output states: changing the value does not initiate a copy. Each click
|
|
32
|
+
copies again, including when the attribute already contains a state. The state
|
|
33
|
+
remains until the next copy; no timer or CSS is required.
|
|
34
|
+
|
|
35
|
+
```css
|
|
36
|
+
[clipboard] .copy-success { display: none; }
|
|
37
|
+
[clipboard="copied"] .copy-success { display: inline; }
|
|
38
|
+
[clipboard="copied"] .copy-icon { display: none; }
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Successful copies emit the existing `clipboarded` event with `detail.clipboardItems`.
|
|
42
|
+
Failures emit `clipboard-error` with `detail.error`. Both events bubble, and state
|
|
43
|
+
is updated before the event fires. Elements with `actions` defer to the Actions
|
|
44
|
+
chain; use `actions="clipboard"` with either clipboard attribute. Disabled controls
|
|
45
|
+
and controls with `aria-disabled="true"` do not copy on click.
|
|
46
|
+
|
|
47
|
+
## CDN
|
|
48
|
+
|
|
49
|
+
```html
|
|
50
|
+
<script src="https://cdn.CoCreateJS.com/clipboard/latest/CoCreate-clipboard.min.js"></script>
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
```html
|
|
54
|
+
<script src="https://cdn.CoCreateJS.com/clipboard/latest/CoCreate-clipboard.min.css"></script>
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## NPM
|
|
58
|
+
|
|
59
|
+
```shell
|
|
60
|
+
$ npm i @cocreate/clipboard
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## yarn
|
|
64
|
+
|
|
65
|
+
```shell
|
|
66
|
+
$ yarn install @cocreate/clipboard
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
# Table of Contents
|
|
70
|
+
|
|
71
|
+
- [Table of Contents](#table-of-contents)
|
|
72
|
+
- [Announcements](#announcements)
|
|
73
|
+
- [Roadmap](#roadmap)
|
|
74
|
+
- [How to Contribute](#how-to-contribute)
|
|
75
|
+
- [About](#about)
|
|
76
|
+
- [License](#license)
|
|
77
|
+
|
|
78
|
+
<a name="announcements"></a>
|
|
79
|
+
|
|
80
|
+
# Announcements
|
|
81
|
+
|
|
82
|
+
All updates to this library are documented in our [CHANGELOG](https://github.com/CoCreateJS/clipboard/blob/master/CHANGELOG.md) and [releases](https://github.com/CoCreateJS/clipboard/releases). You may also subscribe to email for releases and breaking changes.
|
|
83
|
+
|
|
84
|
+
<a name="roadmap"></a>
|
|
85
|
+
|
|
86
|
+
# Roadmap
|
|
87
|
+
|
|
88
|
+
If you are interested in the future direction of this project, please take a look at our open [issues](https://github.com/CoCreateJS/clipboard/issues) and [pull requests](https://github.com/CoCreateJS/clipboard/pulls). We would love to hear your feedback.
|
|
89
|
+
|
|
90
|
+
<a name="about"></a>
|
|
91
|
+
|
|
92
|
+
# About
|
|
93
|
+
|
|
94
|
+
CoCreate-clipboard is guided and supported by the CoCreate Developer Experience Team.
|
|
95
|
+
|
|
96
|
+
Please Email the Developer Experience Team [here](mailto:develop@CoCreateJS.com) in case of any queries.
|
|
97
|
+
|
|
98
|
+
CoCreate-clipboard is maintained and funded by CoCreate. The names and logos for CoCreate are trademarks of CoCreate, LLC.
|
|
99
|
+
|
|
100
|
+
<a name="contribute"></a>
|
|
101
|
+
|
|
102
|
+
# How to Contribute
|
|
103
|
+
|
|
104
|
+
We encourage contribution to our libraries (you might even score some nifty swag), please see our [CONTRIBUTING](https://github.com/CoCreateJS/clipboard/blob/master/CONTRIBUTING.md) guide for details.
|
|
105
|
+
|
|
106
|
+
We want this library to be community-driven, and CoCreate led. We need your help to realize this goal. To help make sure we are building the right things in the right order, we ask that you create [issues](https://github.com/CoCreateJS/clipboard/issues) and [pull requests](https://github.com/CoCreateJS/clipboard/pulls) or merely upvote or comment on existing issues or pull requests.
|
|
107
|
+
|
|
108
|
+
We appreciate your continued support, thank you!
|
|
109
|
+
|
|
110
|
+
# License
|
|
111
|
+
|
|
112
|
+
[The MIT License (MIT)](https://github.com/CoCreateJS/clipboard/blob/master/LICENSE)
|
package/demo/index.html
CHANGED
|
@@ -37,6 +37,6 @@ test</textarea
|
|
|
37
37
|
|
|
38
38
|
<!-- <script src="../dist/CoCreate-clipboard.js"></script> -->
|
|
39
39
|
<script src="../../../CoCreateJS/dist/CoCreate.js"></script>
|
|
40
|
-
<!-- <script src="https://
|
|
40
|
+
<!-- <script src="https://CoCreateJS.com/dist/CoCreate.js"></script> -->
|
|
41
41
|
</body>
|
|
42
42
|
</html>
|
package/docs/index.html
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
rel="icon"
|
|
10
10
|
type="image/png"
|
|
11
11
|
sizes="32x32"
|
|
12
|
-
href="https://
|
|
12
|
+
href="https://CoCreateJS.com/images/favicon.ico" />
|
|
13
13
|
<meta
|
|
14
14
|
name="description"
|
|
15
15
|
content="A simple HTML5 and pure javascript component. Easy configuration using data-attributes and highly styleable." />
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
share-width="500"
|
|
37
37
|
share-media="https://via.placeholder.com/300/09f/fff.png">
|
|
38
38
|
<a
|
|
39
|
-
href="https://github.com/
|
|
39
|
+
href="https://github.com/CoCreateJS/CoCreate-clipboard"
|
|
40
40
|
target="_blank"
|
|
41
41
|
class="margin-right:15px"
|
|
42
42
|
><i src="/assets/svg/github.svg"></i
|
|
@@ -80,7 +80,7 @@
|
|
|
80
80
|
<pre
|
|
81
81
|
class="margin-top:15px"><code class="language-javascript">npm install cocreate-clipboard</code></pre>
|
|
82
82
|
<p class="padding:10px_0px">Or you can use cdn link:</p>
|
|
83
|
-
<pre><code class="language-javascript">https://cdn.
|
|
83
|
+
<pre><code class="language-javascript">https://cdn.CoCreateJS.com/js/CoCreate-clipboard.min.js</code></pre>
|
|
84
84
|
|
|
85
85
|
<h2
|
|
86
86
|
class="border-bottom:1px_solid_lightgrey margin-top:20px padding:5px_0px">
|
|
@@ -225,6 +225,6 @@
|
|
|
225
225
|
</div>
|
|
226
226
|
|
|
227
227
|
<!--CoCreateJS-->
|
|
228
|
-
<script src="https://
|
|
228
|
+
<script src="https://CoCreateJS.com/dist/CoCreate.js"></script>
|
|
229
229
|
</body>
|
|
230
230
|
</html>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cocreate/clipboard",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "A simple clipboard component in vanilla javascript. Easily configured using HTML5 data-attributes and/or JavaScript API.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"clipboard",
|
|
@@ -22,27 +22,27 @@
|
|
|
22
22
|
},
|
|
23
23
|
"repository": {
|
|
24
24
|
"type": "git",
|
|
25
|
-
"url": "git+https://github.com/
|
|
25
|
+
"url": "git+https://github.com/CoCreateJS/clipboard.git"
|
|
26
26
|
},
|
|
27
27
|
"author": "CoCreate LLC",
|
|
28
28
|
"license": "MIT",
|
|
29
29
|
"bugs": {
|
|
30
|
-
"url": "https://github.com/
|
|
30
|
+
"url": "https://github.com/CoCreateJS/clipboard/issues"
|
|
31
31
|
},
|
|
32
|
-
"homepage": "https://
|
|
32
|
+
"homepage": "https://CoCreateJS.com/docs/clipboard",
|
|
33
33
|
"funding": {
|
|
34
34
|
"type": "GitHub Sponsors ❤",
|
|
35
|
-
"url": "https://github.com/sponsors/
|
|
35
|
+
"url": "https://github.com/sponsors/CoCreateJS"
|
|
36
36
|
},
|
|
37
37
|
"main": "./src/index.js",
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"@cocreate/actions": "^1.24.0",
|
|
40
40
|
"@cocreate/element-prototype": "^1.35.0",
|
|
41
|
-
"@cocreate/observer": "^1.
|
|
42
|
-
"@cocreate/utils": "^1.
|
|
41
|
+
"@cocreate/observer": "^1.23.0",
|
|
42
|
+
"@cocreate/utils": "^1.54.0"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
|
-
"@cocreate/webpack": "^1.
|
|
45
|
+
"@cocreate/webpack": "^1.9.0"
|
|
46
46
|
},
|
|
47
47
|
"allowScripts": {
|
|
48
48
|
"@cocreate/actions@1.21.4": true,
|
package/src/index.js
CHANGED
|
@@ -1,145 +1,313 @@
|
|
|
1
|
-
/********************************************************************************
|
|
2
|
-
* Copyright (C) 2020 CoCreate LLC and others.
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* SPDX-License-Identifier: MIT
|
|
6
|
-
********************************************************************************/
|
|
7
|
-
|
|
8
|
-
import "@cocreate/element-prototype";
|
|
9
|
-
import { queryElements } from "@cocreate/utils";
|
|
10
|
-
import Actions from "@cocreate/actions";
|
|
11
|
-
import Observer from "@cocreate/observer";
|
|
12
|
-
|
|
13
|
-
const selector = "[clipboard-query]";
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
)
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
})
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
1
|
+
/********************************************************************************
|
|
2
|
+
* Copyright (C) 2020 CoCreate LLC and others.
|
|
3
|
+
*
|
|
4
|
+
*
|
|
5
|
+
* SPDX-License-Identifier: MIT
|
|
6
|
+
********************************************************************************/
|
|
7
|
+
|
|
8
|
+
import "@cocreate/element-prototype";
|
|
9
|
+
import { queryElements } from "@cocreate/utils";
|
|
10
|
+
import Actions from "@cocreate/actions";
|
|
11
|
+
import Observer from "@cocreate/observer";
|
|
12
|
+
|
|
13
|
+
const selector = "[clipboard], [clipboard-query]";
|
|
14
|
+
const initialized = new WeakSet();
|
|
15
|
+
const pending = new WeakMap();
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Initializes matching elements. With no argument, scans the document; accepts an element, collection, NodeList, or array.
|
|
19
|
+
*
|
|
20
|
+
* @public
|
|
21
|
+
* @environment client
|
|
22
|
+
*/
|
|
23
|
+
function init(element) {
|
|
24
|
+
if (!element) {
|
|
25
|
+
element = document.querySelectorAll(selector);
|
|
26
|
+
} else if (
|
|
27
|
+
!(element instanceof HTMLCollection) &&
|
|
28
|
+
!(element instanceof NodeList) &&
|
|
29
|
+
!Array.isArray(element)
|
|
30
|
+
) {
|
|
31
|
+
element = [element];
|
|
32
|
+
}
|
|
33
|
+
for (let i = 0; i < element.length; i++) {
|
|
34
|
+
if (element[i].matches(selector)) {
|
|
35
|
+
initElement(element[i]);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function initElement(element) {
|
|
41
|
+
if (initialized.has(element)) return;
|
|
42
|
+
initialized.add(element);
|
|
43
|
+
element.addEventListener("click", () => {
|
|
44
|
+
if (
|
|
45
|
+
element.matches(selector) && !element.hasAttribute("actions") &&
|
|
46
|
+
!element.disabled && element.getAttribute("aria-disabled") !== "true"
|
|
47
|
+
) queryClipboardElement(element);
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function queryClipboardElement(element) {
|
|
52
|
+
let elements = [];
|
|
53
|
+
if (element.hasAttribute("clipboard-query")) {
|
|
54
|
+
elements = queryElements({ element, prefix: "clipboard" });
|
|
55
|
+
} else {
|
|
56
|
+
elements = [element];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return clipboard(element, elements);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Copies values from the supplied source elements to the system clipboard and returns a promise for the result.
|
|
64
|
+
*
|
|
65
|
+
* @public
|
|
66
|
+
* @environment client
|
|
67
|
+
*/
|
|
68
|
+
function clipboard(element, elements = [element]) {
|
|
69
|
+
if (pending.has(element)) return pending.get(element);
|
|
70
|
+
const operation = copy(element, elements).finally(() => pending.delete(element));
|
|
71
|
+
pending.set(element, operation);
|
|
72
|
+
return operation;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async function copy(element, elements) {
|
|
76
|
+
if (
|
|
77
|
+
!(elements instanceof HTMLCollection) &&
|
|
78
|
+
!(elements instanceof NodeList) &&
|
|
79
|
+
!Array.isArray(elements)
|
|
80
|
+
) {
|
|
81
|
+
elements = [elements];
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
try {
|
|
85
|
+
element.setAttribute("clipboard", "copying");
|
|
86
|
+
const clipboardItems = [];
|
|
87
|
+
|
|
88
|
+
for (const target of elements) {
|
|
89
|
+
const value = await target.getValue();
|
|
90
|
+
if (typeof value === "string") {
|
|
91
|
+
// Handle string content as plain text
|
|
92
|
+
clipboardItems.push(
|
|
93
|
+
new ClipboardItem({
|
|
94
|
+
"text/plain": new Blob([value], { type: "text/plain" }),
|
|
95
|
+
"text/html": new Blob([value], {
|
|
96
|
+
type: "text/html"
|
|
97
|
+
}) // Render as code block in rich editors
|
|
98
|
+
})
|
|
99
|
+
);
|
|
100
|
+
} else if (value instanceof Blob) {
|
|
101
|
+
// Handle Blob content directly
|
|
102
|
+
clipboardItems.push(
|
|
103
|
+
new ClipboardItem({
|
|
104
|
+
[value.type || "application/octet-stream"]: value
|
|
105
|
+
})
|
|
106
|
+
);
|
|
107
|
+
} else if (value !== undefined) {
|
|
108
|
+
const content = JSON.stringify(value, null, 2);
|
|
109
|
+
clipboardItems.push(
|
|
110
|
+
new ClipboardItem({
|
|
111
|
+
"text/plain": new Blob([content], {
|
|
112
|
+
type: "text/plain"
|
|
113
|
+
})
|
|
114
|
+
})
|
|
115
|
+
);
|
|
116
|
+
} else {
|
|
117
|
+
console.warn("Unsupported value type for clipboard:", value);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (clipboardItems.length > 0) {
|
|
122
|
+
await navigator.clipboard.write(clipboardItems);
|
|
123
|
+
} else {
|
|
124
|
+
throw new Error("No valid content to copy to clipboard.");
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
element.setAttribute("clipboard", "copied");
|
|
128
|
+
element.dispatchEvent(
|
|
129
|
+
new CustomEvent("clipboarded", {
|
|
130
|
+
bubbles: true,
|
|
131
|
+
detail: { clipboardItems }
|
|
132
|
+
})
|
|
133
|
+
);
|
|
134
|
+
return { clipboardItems };
|
|
135
|
+
} catch (error) {
|
|
136
|
+
element.setAttribute("clipboard", "error");
|
|
137
|
+
element.dispatchEvent(new CustomEvent("clipboard-error", {
|
|
138
|
+
bubbles: true, detail: { error }
|
|
139
|
+
}));
|
|
140
|
+
return { error };
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
init();
|
|
145
|
+
|
|
146
|
+
Actions.init([
|
|
147
|
+
{
|
|
148
|
+
name: "clipboard",
|
|
149
|
+
endEvent: "clipboarded",
|
|
150
|
+
callback: async (action) => {
|
|
151
|
+
const result = await queryClipboardElement(action.element || action);
|
|
152
|
+
if (result.error) action.abort(result.error);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
]);
|
|
156
|
+
|
|
157
|
+
Observer.init({
|
|
158
|
+
name: "CoCreateClipboardAddedNodes",
|
|
159
|
+
types: ["addedNodes"],
|
|
160
|
+
selector,
|
|
161
|
+
callback(mutation) {
|
|
162
|
+
init(mutation.target);
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
Observer.init({
|
|
167
|
+
name: "CoCreateClipboardObserver",
|
|
168
|
+
types: ["attributes"],
|
|
169
|
+
attributeFilter: ["clipboard", "clipboard-query"],
|
|
170
|
+
selector,
|
|
171
|
+
callback: function (mutation) {
|
|
172
|
+
init(mutation.target);
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
export default { init, clipboard };
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Bubbles after a successful copy; detail contains clipboardItems.
|
|
180
|
+
*
|
|
181
|
+
* @name clipboarded
|
|
182
|
+
* @kind member
|
|
183
|
+
* @public
|
|
184
|
+
* @environment client
|
|
185
|
+
*/
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Bubbles when copying fails; detail contains the error.
|
|
189
|
+
*
|
|
190
|
+
* @name clipboard-error
|
|
191
|
+
* @kind member
|
|
192
|
+
* @public
|
|
193
|
+
* @environment client
|
|
194
|
+
*/
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Copies the element's own value when used as a declarative control; also reflects the current operation state.
|
|
198
|
+
*
|
|
199
|
+
* @name clipboard
|
|
200
|
+
* @kind member
|
|
201
|
+
* @attribute
|
|
202
|
+
* @valueType string
|
|
203
|
+
* @acceptedValues copying, copied, error
|
|
204
|
+
* @environment client
|
|
205
|
+
*/
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Selects source elements relative to the control using CoCreate queryElements with the clipboard prefix.
|
|
209
|
+
*
|
|
210
|
+
* @name clipboard-query
|
|
211
|
+
* @kind member
|
|
212
|
+
* @attribute
|
|
213
|
+
* @valueType string
|
|
214
|
+
* @environment client
|
|
215
|
+
*/
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Declarative actions supported or registered by this module.
|
|
219
|
+
*
|
|
220
|
+
* @name declarative-actions
|
|
221
|
+
* @kind member
|
|
222
|
+
* @actions
|
|
223
|
+
* @environment client
|
|
224
|
+
* @example
|
|
225
|
+
* ```json
|
|
226
|
+
* [
|
|
227
|
+
* {
|
|
228
|
+
* "name": "clipboard",
|
|
229
|
+
* "description": "Copies the value of the action element or the elements selected by its clipboard-query attribute.",
|
|
230
|
+
* "endEvent": "clipboarded",
|
|
231
|
+
* "failure": "Aborts the action chain with the copy error."
|
|
232
|
+
* }
|
|
233
|
+
* ]
|
|
234
|
+
* ```
|
|
235
|
+
*/
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Declarative binding behavior for this module.
|
|
239
|
+
*
|
|
240
|
+
* @name declarative-binding
|
|
241
|
+
* @kind member
|
|
242
|
+
* @binding
|
|
243
|
+
* @environment client
|
|
244
|
+
* @example
|
|
245
|
+
* ```json
|
|
246
|
+
* {
|
|
247
|
+
* "selector": "[clipboard], [clipboard-query]",
|
|
248
|
+
* "trigger": "click",
|
|
249
|
+
* "initializesExistingElements": true,
|
|
250
|
+
* "observesAddedNodes": true,
|
|
251
|
+
* "actionsIntegration": "When an element has an actions attribute, direct click handling is skipped."
|
|
252
|
+
* }
|
|
253
|
+
* ```
|
|
254
|
+
*/
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Value conversion and handling behavior for this module.
|
|
258
|
+
*
|
|
259
|
+
* @name declarative-valueHandling
|
|
260
|
+
* @kind member
|
|
261
|
+
* @valueHandling
|
|
262
|
+
* @environment client
|
|
263
|
+
* @example
|
|
264
|
+
* ```json
|
|
265
|
+
* {
|
|
266
|
+
* "string": [
|
|
267
|
+
* "text/plain",
|
|
268
|
+
* "text/html"
|
|
269
|
+
* ],
|
|
270
|
+
* "blob": "Copied using its MIME type, or application/octet-stream when the type is empty.",
|
|
271
|
+
* "otherDefinedValues": "JSON-stringified and copied as text/plain.",
|
|
272
|
+
* "undefined": "Ignored; if no valid ClipboardItems remain, the operation fails."
|
|
273
|
+
* }
|
|
274
|
+
* ```
|
|
275
|
+
*/
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Result shape and state metadata for this API.
|
|
279
|
+
*
|
|
280
|
+
* @name api-result
|
|
281
|
+
* @kind member
|
|
282
|
+
* @result
|
|
283
|
+
* @environment client
|
|
284
|
+
* @example
|
|
285
|
+
* ```json
|
|
286
|
+
* {
|
|
287
|
+
* "success": "{ clipboardItems }",
|
|
288
|
+
* "failure": "{ error }",
|
|
289
|
+
* "stateAttribute": "clipboard",
|
|
290
|
+
* "stateValues": [
|
|
291
|
+
* "copying",
|
|
292
|
+
* "copied",
|
|
293
|
+
* "error"
|
|
294
|
+
* ]
|
|
295
|
+
* }
|
|
296
|
+
* ```
|
|
297
|
+
*/
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Usage constraints for this API.
|
|
301
|
+
*
|
|
302
|
+
* @name api-constraints
|
|
303
|
+
* @kind member
|
|
304
|
+
* @constraints
|
|
305
|
+
* @environment client
|
|
306
|
+
* @example
|
|
307
|
+
* ```json
|
|
308
|
+
* [
|
|
309
|
+
* "Direct click handling skips disabled controls and controls with aria-disabled=\"true\".",
|
|
310
|
+
* "Copying requires browser support for navigator.clipboard.write and ClipboardItem, plus the browser's applicable clipboard permissions and context requirements."
|
|
311
|
+
* ]
|
|
312
|
+
* ```
|
|
313
|
+
*/
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
const { test } = require('node:test');
|
|
2
|
+
const assert = require('node:assert/strict');
|
|
3
|
+
const fs = require('node:fs');
|
|
4
|
+
const vm = require('node:vm');
|
|
5
|
+
|
|
6
|
+
function setup() {
|
|
7
|
+
const observers = [], actions = [], writes = [];
|
|
8
|
+
class Element extends EventTarget {
|
|
9
|
+
constructor(attributes = {}, value = 'Hello') {
|
|
10
|
+
super(); this.attributes = new Map(Object.entries(attributes)); this.value = value;
|
|
11
|
+
}
|
|
12
|
+
hasAttribute(name) { return this.attributes.has(name); }
|
|
13
|
+
getAttribute(name) { return this.attributes.get(name) ?? null; }
|
|
14
|
+
setAttribute(name, value) { this.attributes.set(name, value); }
|
|
15
|
+
matches() { return this.hasAttribute('clipboard') || this.hasAttribute('clipboard-query'); }
|
|
16
|
+
getValue() { return this.value; }
|
|
17
|
+
}
|
|
18
|
+
const context = vm.createContext({
|
|
19
|
+
Event, EventTarget, Blob, console,
|
|
20
|
+
CustomEvent: class extends Event { constructor(name, options) { super(name, options); this.detail = options.detail; } },
|
|
21
|
+
ClipboardItem: class { constructor(data) { this.data = data; } },
|
|
22
|
+
HTMLCollection: class {}, NodeList: class {},
|
|
23
|
+
document: { querySelectorAll: () => [] },
|
|
24
|
+
queryElements: ({ element }) => element.targets || [],
|
|
25
|
+
navigator: { clipboard: { write: async items => { writes.push(items); } } },
|
|
26
|
+
Actions: { init: items => actions.push(...items) },
|
|
27
|
+
Observer: { init: config => observers.push(config) }
|
|
28
|
+
});
|
|
29
|
+
const source = fs.readFileSync('src/index.js', 'utf8')
|
|
30
|
+
.replace(/^import .*;\r?\n/gm, '')
|
|
31
|
+
.replace('export default { init, clipboard };', 'globalThis.api = { init, clipboard };');
|
|
32
|
+
vm.runInContext(source, context);
|
|
33
|
+
return { ...context, Element, observers, actions, writes };
|
|
34
|
+
}
|
|
35
|
+
const flush = () => new Promise(resolve => setImmediate(resolve));
|
|
36
|
+
|
|
37
|
+
test('bare attribute copies itself and reflects state before its success event', async () => {
|
|
38
|
+
const s = setup(), element = new s.Element({ clipboard: '' });
|
|
39
|
+
s.api.init(element);
|
|
40
|
+
let state, bubbles;
|
|
41
|
+
element.addEventListener('clipboarded', event => { state = element.getAttribute('clipboard'); bubbles = event.bubbles; });
|
|
42
|
+
element.dispatchEvent(new Event('click'));
|
|
43
|
+
assert.equal(element.getAttribute('clipboard'), 'copying');
|
|
44
|
+
await flush();
|
|
45
|
+
assert.equal(state, 'copied');
|
|
46
|
+
assert.equal(bubbles, true);
|
|
47
|
+
assert.equal(await s.writes[0][0].data['text/plain'].text(), 'Hello');
|
|
48
|
+
element.dispatchEvent(new Event('click'));
|
|
49
|
+
await flush();
|
|
50
|
+
assert.equal(s.writes.length, 2);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test('query-only dynamic controls bind once across state and query mutations', async () => {
|
|
54
|
+
const s = setup(), element = new s.Element({ 'clipboard-query': '#source' });
|
|
55
|
+
element.targets = [new s.Element({}, 'Target')];
|
|
56
|
+
s.observers[0].callback({ target: element });
|
|
57
|
+
for (let i = 0; i < 3; i++) s.observers[1].callback({ target: element });
|
|
58
|
+
element.dispatchEvent(new Event('click'));
|
|
59
|
+
await flush();
|
|
60
|
+
assert.equal(s.writes.length, 1);
|
|
61
|
+
assert.equal(await s.writes[0][0].data['text/plain'].text(), 'Target');
|
|
62
|
+
assert.equal(element.getAttribute('clipboard'), 'copied');
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test('actions own the click and disabled controls do not copy', async () => {
|
|
66
|
+
const s = setup();
|
|
67
|
+
for (const attrs of [{ clipboard: '', actions: 'clipboard' }, { clipboard: '', 'aria-disabled': 'true' }]) {
|
|
68
|
+
const element = new s.Element(attrs);
|
|
69
|
+
s.api.init(element);
|
|
70
|
+
element.dispatchEvent(new Event('click'));
|
|
71
|
+
}
|
|
72
|
+
await flush();
|
|
73
|
+
assert.equal(s.writes.length, 0);
|
|
74
|
+
const element = new s.Element({ clipboard: '', actions: 'clipboard' });
|
|
75
|
+
await s.actions[0].callback({ element });
|
|
76
|
+
assert.equal(s.writes.length, 1);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test('failed writes expose error state and event, abort actions and allow retry', async () => {
|
|
80
|
+
const s = setup(), element = new s.Element({ clipboard: '' });
|
|
81
|
+
const error = new Error('Permission denied');
|
|
82
|
+
s.navigator.clipboard.write = async () => { throw error; };
|
|
83
|
+
let detail, aborted;
|
|
84
|
+
element.addEventListener('clipboard-error', event => { detail = event.detail; });
|
|
85
|
+
await s.actions[0].callback({ element, abort: value => { aborted = value; } });
|
|
86
|
+
assert.equal(element.getAttribute('clipboard'), 'error');
|
|
87
|
+
assert.equal(detail.error, error);
|
|
88
|
+
assert.equal(aborted, error);
|
|
89
|
+
s.navigator.clipboard.write = async () => {};
|
|
90
|
+
await s.api.clipboard(element);
|
|
91
|
+
assert.equal(element.getAttribute('clipboard'), 'copied');
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test('empty targets fail and overlapping copies share a single write', async () => {
|
|
95
|
+
const s = setup(), element = new s.Element({ clipboard: '' });
|
|
96
|
+
const result = await s.api.clipboard(element, []);
|
|
97
|
+
assert.ok(result.error);
|
|
98
|
+
assert.equal(element.getAttribute('clipboard'), 'error');
|
|
99
|
+
const first = s.api.clipboard(element);
|
|
100
|
+
const second = s.api.clipboard(element);
|
|
101
|
+
assert.equal(first, second);
|
|
102
|
+
await first;
|
|
103
|
+
assert.equal(s.writes.length, 1);
|
|
104
|
+
});
|