@navikt/ds-react 3.1.3 → 3.2.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/_docs.json +269 -0
- package/cjs/copybutton/CopyButton.js +77 -0
- package/cjs/copybutton/index.js +8 -0
- package/cjs/copybutton/package.json +6 -0
- package/cjs/index.js +2 -1
- package/cjs/util/copy.js +73 -0
- package/esm/copybutton/CopyButton.d.ts +55 -0
- package/esm/copybutton/CopyButton.js +49 -0
- package/esm/copybutton/CopyButton.js.map +1 -0
- package/esm/copybutton/index.d.ts +1 -0
- package/esm/copybutton/index.js +2 -0
- package/esm/copybutton/index.js.map +1 -0
- package/esm/index.d.ts +2 -1
- package/esm/index.js +2 -1
- package/esm/index.js.map +1 -1
- package/esm/util/copy.d.ts +1 -0
- package/esm/util/copy.js +71 -0
- package/esm/util/copy.js.map +1 -0
- package/package.json +2 -2
- package/src/copybutton/CopyButton.tsx +174 -0
- package/src/copybutton/copy-button.stories.tsx +168 -0
- package/src/copybutton/index.ts +1 -0
- package/src/index.ts +2 -1
- package/src/util/copy.ts +81 -0
package/_docs.json
CHANGED
|
@@ -1256,6 +1256,275 @@
|
|
|
1256
1256
|
}
|
|
1257
1257
|
}
|
|
1258
1258
|
},
|
|
1259
|
+
{
|
|
1260
|
+
"filePath": "src/copybutton/CopyButton.tsx",
|
|
1261
|
+
"displayName": "CopyButton",
|
|
1262
|
+
"props": {
|
|
1263
|
+
"size": {
|
|
1264
|
+
"defaultValue": {
|
|
1265
|
+
"value": "medium"
|
|
1266
|
+
},
|
|
1267
|
+
"description": "",
|
|
1268
|
+
"name": "size",
|
|
1269
|
+
"parent": {
|
|
1270
|
+
"fileName": "src/copybutton/CopyButton.tsx",
|
|
1271
|
+
"name": "CopyButtonProps"
|
|
1272
|
+
},
|
|
1273
|
+
"declarations": [
|
|
1274
|
+
{
|
|
1275
|
+
"fileName": "src/copybutton/CopyButton.tsx",
|
|
1276
|
+
"name": "CopyButtonProps"
|
|
1277
|
+
}
|
|
1278
|
+
],
|
|
1279
|
+
"required": false,
|
|
1280
|
+
"type": {
|
|
1281
|
+
"name": "\"medium\" | \"small\""
|
|
1282
|
+
}
|
|
1283
|
+
},
|
|
1284
|
+
"variant": {
|
|
1285
|
+
"defaultValue": {
|
|
1286
|
+
"value": "neutral"
|
|
1287
|
+
},
|
|
1288
|
+
"description": "",
|
|
1289
|
+
"name": "variant",
|
|
1290
|
+
"parent": {
|
|
1291
|
+
"fileName": "src/copybutton/CopyButton.tsx",
|
|
1292
|
+
"name": "CopyButtonProps"
|
|
1293
|
+
},
|
|
1294
|
+
"declarations": [
|
|
1295
|
+
{
|
|
1296
|
+
"fileName": "src/copybutton/CopyButton.tsx",
|
|
1297
|
+
"name": "CopyButtonProps"
|
|
1298
|
+
}
|
|
1299
|
+
],
|
|
1300
|
+
"required": false,
|
|
1301
|
+
"type": {
|
|
1302
|
+
"name": "\"action\" | \"neutral\""
|
|
1303
|
+
}
|
|
1304
|
+
},
|
|
1305
|
+
"copyText": {
|
|
1306
|
+
"defaultValue": null,
|
|
1307
|
+
"description": "Text to copy to clipboard",
|
|
1308
|
+
"name": "copyText",
|
|
1309
|
+
"parent": {
|
|
1310
|
+
"fileName": "src/copybutton/CopyButton.tsx",
|
|
1311
|
+
"name": "CopyButtonProps"
|
|
1312
|
+
},
|
|
1313
|
+
"declarations": [
|
|
1314
|
+
{
|
|
1315
|
+
"fileName": "src/copybutton/CopyButton.tsx",
|
|
1316
|
+
"name": "CopyButtonProps"
|
|
1317
|
+
}
|
|
1318
|
+
],
|
|
1319
|
+
"required": true,
|
|
1320
|
+
"type": {
|
|
1321
|
+
"name": "string"
|
|
1322
|
+
}
|
|
1323
|
+
},
|
|
1324
|
+
"text": {
|
|
1325
|
+
"defaultValue": null,
|
|
1326
|
+
"description": "Optional text in button",
|
|
1327
|
+
"name": "text",
|
|
1328
|
+
"parent": {
|
|
1329
|
+
"fileName": "src/copybutton/CopyButton.tsx",
|
|
1330
|
+
"name": "CopyButtonProps"
|
|
1331
|
+
},
|
|
1332
|
+
"declarations": [
|
|
1333
|
+
{
|
|
1334
|
+
"fileName": "src/copybutton/CopyButton.tsx",
|
|
1335
|
+
"name": "CopyButtonProps"
|
|
1336
|
+
}
|
|
1337
|
+
],
|
|
1338
|
+
"required": false,
|
|
1339
|
+
"type": {
|
|
1340
|
+
"name": "string"
|
|
1341
|
+
}
|
|
1342
|
+
},
|
|
1343
|
+
"activeText": {
|
|
1344
|
+
"defaultValue": {
|
|
1345
|
+
"value": "Kopiert!"
|
|
1346
|
+
},
|
|
1347
|
+
"description": "Text shown when button is clicked\nOnly set if used with 'text'-prop",
|
|
1348
|
+
"name": "activeText",
|
|
1349
|
+
"parent": {
|
|
1350
|
+
"fileName": "src/copybutton/CopyButton.tsx",
|
|
1351
|
+
"name": "CopyButtonProps"
|
|
1352
|
+
},
|
|
1353
|
+
"declarations": [
|
|
1354
|
+
{
|
|
1355
|
+
"fileName": "src/copybutton/CopyButton.tsx",
|
|
1356
|
+
"name": "CopyButtonProps"
|
|
1357
|
+
}
|
|
1358
|
+
],
|
|
1359
|
+
"required": false,
|
|
1360
|
+
"type": {
|
|
1361
|
+
"name": "string"
|
|
1362
|
+
}
|
|
1363
|
+
},
|
|
1364
|
+
"onActiveChange": {
|
|
1365
|
+
"defaultValue": null,
|
|
1366
|
+
"description": "Callback when 'copied'-state is active",
|
|
1367
|
+
"name": "onActiveChange",
|
|
1368
|
+
"parent": {
|
|
1369
|
+
"fileName": "src/copybutton/CopyButton.tsx",
|
|
1370
|
+
"name": "CopyButtonProps"
|
|
1371
|
+
},
|
|
1372
|
+
"declarations": [
|
|
1373
|
+
{
|
|
1374
|
+
"fileName": "src/copybutton/CopyButton.tsx",
|
|
1375
|
+
"name": "CopyButtonProps"
|
|
1376
|
+
}
|
|
1377
|
+
],
|
|
1378
|
+
"required": false,
|
|
1379
|
+
"type": {
|
|
1380
|
+
"name": "((state: boolean) => void)"
|
|
1381
|
+
}
|
|
1382
|
+
},
|
|
1383
|
+
"icon": {
|
|
1384
|
+
"defaultValue": {
|
|
1385
|
+
"value": "<FilesIcon />"
|
|
1386
|
+
},
|
|
1387
|
+
"description": "Icon shown when button is not clicked",
|
|
1388
|
+
"name": "icon",
|
|
1389
|
+
"parent": {
|
|
1390
|
+
"fileName": "src/copybutton/CopyButton.tsx",
|
|
1391
|
+
"name": "CopyButtonProps"
|
|
1392
|
+
},
|
|
1393
|
+
"declarations": [
|
|
1394
|
+
{
|
|
1395
|
+
"fileName": "src/copybutton/CopyButton.tsx",
|
|
1396
|
+
"name": "CopyButtonProps"
|
|
1397
|
+
}
|
|
1398
|
+
],
|
|
1399
|
+
"required": false,
|
|
1400
|
+
"type": {
|
|
1401
|
+
"name": "ReactNode"
|
|
1402
|
+
}
|
|
1403
|
+
},
|
|
1404
|
+
"activeIcon": {
|
|
1405
|
+
"defaultValue": {
|
|
1406
|
+
"value": "<CheckmarkIcon />"
|
|
1407
|
+
},
|
|
1408
|
+
"description": "Icon shown when active",
|
|
1409
|
+
"name": "activeIcon",
|
|
1410
|
+
"parent": {
|
|
1411
|
+
"fileName": "src/copybutton/CopyButton.tsx",
|
|
1412
|
+
"name": "CopyButtonProps"
|
|
1413
|
+
},
|
|
1414
|
+
"declarations": [
|
|
1415
|
+
{
|
|
1416
|
+
"fileName": "src/copybutton/CopyButton.tsx",
|
|
1417
|
+
"name": "CopyButtonProps"
|
|
1418
|
+
}
|
|
1419
|
+
],
|
|
1420
|
+
"required": false,
|
|
1421
|
+
"type": {
|
|
1422
|
+
"name": "ReactNode"
|
|
1423
|
+
}
|
|
1424
|
+
},
|
|
1425
|
+
"activeDuration": {
|
|
1426
|
+
"defaultValue": {
|
|
1427
|
+
"value": "2000"
|
|
1428
|
+
},
|
|
1429
|
+
"description": "Timeout duration in milliseconds",
|
|
1430
|
+
"name": "activeDuration",
|
|
1431
|
+
"parent": {
|
|
1432
|
+
"fileName": "src/copybutton/CopyButton.tsx",
|
|
1433
|
+
"name": "CopyButtonProps"
|
|
1434
|
+
},
|
|
1435
|
+
"declarations": [
|
|
1436
|
+
{
|
|
1437
|
+
"fileName": "src/copybutton/CopyButton.tsx",
|
|
1438
|
+
"name": "CopyButtonProps"
|
|
1439
|
+
}
|
|
1440
|
+
],
|
|
1441
|
+
"required": false,
|
|
1442
|
+
"type": {
|
|
1443
|
+
"name": "number"
|
|
1444
|
+
}
|
|
1445
|
+
},
|
|
1446
|
+
"title": {
|
|
1447
|
+
"defaultValue": {
|
|
1448
|
+
"value": "Kopier"
|
|
1449
|
+
},
|
|
1450
|
+
"description": "* accessible label for icon (ignored if text is set)",
|
|
1451
|
+
"name": "title",
|
|
1452
|
+
"parent": {
|
|
1453
|
+
"fileName": "src/copybutton/CopyButton.tsx",
|
|
1454
|
+
"name": "CopyButtonProps"
|
|
1455
|
+
},
|
|
1456
|
+
"declarations": [
|
|
1457
|
+
{
|
|
1458
|
+
"fileName": "src/copybutton/CopyButton.tsx",
|
|
1459
|
+
"name": "CopyButtonProps"
|
|
1460
|
+
}
|
|
1461
|
+
],
|
|
1462
|
+
"required": false,
|
|
1463
|
+
"type": {
|
|
1464
|
+
"name": "string"
|
|
1465
|
+
}
|
|
1466
|
+
},
|
|
1467
|
+
"activeTitle": {
|
|
1468
|
+
"defaultValue": {
|
|
1469
|
+
"value": "Kopiert"
|
|
1470
|
+
},
|
|
1471
|
+
"description": "accessible label for icon in active-state (ignored if text is set)",
|
|
1472
|
+
"name": "activeTitle",
|
|
1473
|
+
"parent": {
|
|
1474
|
+
"fileName": "src/copybutton/CopyButton.tsx",
|
|
1475
|
+
"name": "CopyButtonProps"
|
|
1476
|
+
},
|
|
1477
|
+
"declarations": [
|
|
1478
|
+
{
|
|
1479
|
+
"fileName": "src/copybutton/CopyButton.tsx",
|
|
1480
|
+
"name": "CopyButtonProps"
|
|
1481
|
+
}
|
|
1482
|
+
],
|
|
1483
|
+
"required": false,
|
|
1484
|
+
"type": {
|
|
1485
|
+
"name": "string"
|
|
1486
|
+
}
|
|
1487
|
+
},
|
|
1488
|
+
"className": {
|
|
1489
|
+
"defaultValue": null,
|
|
1490
|
+
"description": "",
|
|
1491
|
+
"name": "className",
|
|
1492
|
+
"parent": {
|
|
1493
|
+
"fileName": "aksel/node_modules/@types/react/index.d.ts",
|
|
1494
|
+
"name": "HTMLAttributes"
|
|
1495
|
+
},
|
|
1496
|
+
"declarations": [
|
|
1497
|
+
{
|
|
1498
|
+
"fileName": "aksel/node_modules/@types/react/index.d.ts",
|
|
1499
|
+
"name": "HTMLAttributes"
|
|
1500
|
+
}
|
|
1501
|
+
],
|
|
1502
|
+
"required": false,
|
|
1503
|
+
"type": {
|
|
1504
|
+
"name": "string"
|
|
1505
|
+
}
|
|
1506
|
+
},
|
|
1507
|
+
"ref": {
|
|
1508
|
+
"defaultValue": null,
|
|
1509
|
+
"description": "",
|
|
1510
|
+
"name": "ref",
|
|
1511
|
+
"parent": {
|
|
1512
|
+
"fileName": "aksel/node_modules/@types/react/index.d.ts",
|
|
1513
|
+
"name": "RefAttributes"
|
|
1514
|
+
},
|
|
1515
|
+
"declarations": [
|
|
1516
|
+
{
|
|
1517
|
+
"fileName": "aksel/node_modules/@types/react/index.d.ts",
|
|
1518
|
+
"name": "RefAttributes"
|
|
1519
|
+
}
|
|
1520
|
+
],
|
|
1521
|
+
"required": false,
|
|
1522
|
+
"type": {
|
|
1523
|
+
"name": "Ref<HTMLButtonElement>"
|
|
1524
|
+
}
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1527
|
+
},
|
|
1259
1528
|
{
|
|
1260
1529
|
"filePath": "src/date/DateInput.tsx",
|
|
1261
1530
|
"displayName": "DatePickerInput",
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
"use client";
|
|
3
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
+
if (k2 === undefined) k2 = k;
|
|
5
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
+
}
|
|
9
|
+
Object.defineProperty(o, k2, desc);
|
|
10
|
+
}) : (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
o[k2] = m[k];
|
|
13
|
+
}));
|
|
14
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
+
}) : function(o, v) {
|
|
17
|
+
o["default"] = v;
|
|
18
|
+
});
|
|
19
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
20
|
+
if (mod && mod.__esModule) return mod;
|
|
21
|
+
var result = {};
|
|
22
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
23
|
+
__setModuleDefault(result, mod);
|
|
24
|
+
return result;
|
|
25
|
+
};
|
|
26
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
27
|
+
var t = {};
|
|
28
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
29
|
+
t[p] = s[p];
|
|
30
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
31
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
32
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
33
|
+
t[p[i]] = s[p[i]];
|
|
34
|
+
}
|
|
35
|
+
return t;
|
|
36
|
+
};
|
|
37
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
38
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
39
|
+
};
|
|
40
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
41
|
+
exports.CopyButton = void 0;
|
|
42
|
+
const aksel_icons_1 = require("@navikt/aksel-icons");
|
|
43
|
+
const clsx_1 = __importDefault(require("clsx"));
|
|
44
|
+
const react_1 = __importStar(require("react"));
|
|
45
|
+
const copy_1 = __importDefault(require("../util/copy"));
|
|
46
|
+
const Label_1 = __importDefault(require("../typography/Label"));
|
|
47
|
+
exports.CopyButton = (0, react_1.forwardRef)((_a, ref) => {
|
|
48
|
+
var { className, copyText, text, activeText = "Kopiert!", variant = "neutral", size = "medium", onActiveChange, icon, activeIcon, activeDuration = 2000, title = "Kopier", activeTitle = "Kopiert" } = _a, rest = __rest(_a, ["className", "copyText", "text", "activeText", "variant", "size", "onActiveChange", "icon", "activeIcon", "activeDuration", "title", "activeTitle"]);
|
|
49
|
+
const [active, setActive] = (0, react_1.useState)(false);
|
|
50
|
+
const timeoutRef = (0, react_1.useRef)();
|
|
51
|
+
(0, react_1.useEffect)(() => {
|
|
52
|
+
return () => {
|
|
53
|
+
timeoutRef.current && clearTimeout(timeoutRef.current);
|
|
54
|
+
};
|
|
55
|
+
}, []);
|
|
56
|
+
const handleClick = (event) => {
|
|
57
|
+
var _a;
|
|
58
|
+
timeoutRef.current && clearTimeout(timeoutRef.current);
|
|
59
|
+
(0, copy_1.default)(copyText);
|
|
60
|
+
setActive(true);
|
|
61
|
+
onActiveChange === null || onActiveChange === void 0 ? void 0 : onActiveChange(true);
|
|
62
|
+
(_a = rest.onClick) === null || _a === void 0 ? void 0 : _a.call(rest, event);
|
|
63
|
+
timeoutRef.current = window.setTimeout(() => {
|
|
64
|
+
setActive(false);
|
|
65
|
+
onActiveChange === null || onActiveChange === void 0 ? void 0 : onActiveChange(false);
|
|
66
|
+
}, activeDuration);
|
|
67
|
+
};
|
|
68
|
+
return (react_1.default.createElement("button", Object.assign({ ref: ref, type: "button" }, rest, { "aria-live": "polite", className: (0, clsx_1.default)("navds-copybutton", className, `navds-copybutton--${size}`, `navds-copybutton--${variant}`, {
|
|
69
|
+
"navds-copybutton--icon-only": !text,
|
|
70
|
+
"navds-copybutton--active": active,
|
|
71
|
+
}), onClick: handleClick }),
|
|
72
|
+
react_1.default.createElement("span", { className: "navds-copybutton__content" },
|
|
73
|
+
active ? (react_1.default.createElement("span", { className: "navds-copybutton__icon" }, activeIcon !== null && activeIcon !== void 0 ? activeIcon : (react_1.default.createElement(aksel_icons_1.CheckmarkIcon, { "aria-hidden": !!text, title: text ? undefined : activeTitle })))) : (react_1.default.createElement("span", { className: "navds-copybutton__icon" }, icon !== null && icon !== void 0 ? icon : (react_1.default.createElement(aksel_icons_1.FilesIcon, { "aria-hidden": !!text, title: text ? undefined : title })))),
|
|
74
|
+
text &&
|
|
75
|
+
(active ? (react_1.default.createElement(Label_1.default, { as: "span", size: size === "medium" ? "medium" : "small", "aria-live": "polite" }, activeText)) : (react_1.default.createElement(Label_1.default, { as: "span", size: size === "medium" ? "medium" : "small", "aria-live": "polite" }, text))))));
|
|
76
|
+
});
|
|
77
|
+
exports.default = exports.CopyButton;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.CopyButton = void 0;
|
|
7
|
+
var CopyButton_1 = require("./CopyButton");
|
|
8
|
+
Object.defineProperty(exports, "CopyButton", { enumerable: true, get: function () { return __importDefault(CopyButton_1).default; } });
|
package/cjs/index.js
CHANGED
|
@@ -19,9 +19,10 @@ __exportStar(require("./alert"), exports);
|
|
|
19
19
|
__exportStar(require("./button"), exports);
|
|
20
20
|
__exportStar(require("./chat"), exports);
|
|
21
21
|
__exportStar(require("./chips"), exports);
|
|
22
|
+
__exportStar(require("./copybutton"), exports);
|
|
22
23
|
__exportStar(require("./date"), exports);
|
|
23
|
-
__exportStar(require("./form"), exports);
|
|
24
24
|
__exportStar(require("./expansion-card"), exports);
|
|
25
|
+
__exportStar(require("./form"), exports);
|
|
25
26
|
__exportStar(require("./grid"), exports);
|
|
26
27
|
__exportStar(require("./guide-panel"), exports);
|
|
27
28
|
__exportStar(require("./help-text"), exports);
|
package/cjs/util/copy.js
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// https://github.com/sudodoki/copy-to-clipboard/blob/main/index.js
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
const defaultMessage = "Kopier til utklippstavle: #{key}, Enter";
|
|
5
|
+
function format(message) {
|
|
6
|
+
const copyKey = (/mac os x/i.test(navigator.userAgent) ? "⌘" : "Ctrl") + "+C";
|
|
7
|
+
return message.replace(/#{\s*key\s*}/g, copyKey);
|
|
8
|
+
}
|
|
9
|
+
function copy(text) {
|
|
10
|
+
let debug, message, range, selection, mark, success = false;
|
|
11
|
+
debug = process.env.NODE_ENV !== "production";
|
|
12
|
+
try {
|
|
13
|
+
range = document.createRange();
|
|
14
|
+
selection = document.getSelection();
|
|
15
|
+
mark = document.createElement("span");
|
|
16
|
+
mark.textContent = text;
|
|
17
|
+
// avoid screen readers from reading out loud the text
|
|
18
|
+
mark.ariaHidden = "true";
|
|
19
|
+
// reset user styles for span element
|
|
20
|
+
mark.style.all = "unset";
|
|
21
|
+
// prevents scrolling to the end of the page
|
|
22
|
+
mark.style.position = "fixed";
|
|
23
|
+
mark.style.top = 0;
|
|
24
|
+
mark.style.clip = "rect(0, 0, 0, 0)";
|
|
25
|
+
// used to preserve spaces and line breaks
|
|
26
|
+
mark.style.whiteSpace = "pre";
|
|
27
|
+
// do not inherit user-select (it may be `none`)
|
|
28
|
+
mark.style.webkitUserSelect = "text";
|
|
29
|
+
mark.style.MozUserSelect = "text";
|
|
30
|
+
mark.style.msUserSelect = "text";
|
|
31
|
+
mark.style.userSelect = "text";
|
|
32
|
+
mark.addEventListener("copy", function (e) {
|
|
33
|
+
e.stopPropagation();
|
|
34
|
+
});
|
|
35
|
+
document.body.appendChild(mark);
|
|
36
|
+
range.selectNodeContents(mark);
|
|
37
|
+
selection.addRange(range);
|
|
38
|
+
const successful = document.execCommand("copy");
|
|
39
|
+
if (!successful) {
|
|
40
|
+
throw new Error("copy command was unsuccessful");
|
|
41
|
+
}
|
|
42
|
+
success = true;
|
|
43
|
+
}
|
|
44
|
+
catch (err) {
|
|
45
|
+
debug && console.error("unable to copy using execCommand: ", err);
|
|
46
|
+
debug && console.warn("trying IE specific stuff");
|
|
47
|
+
try {
|
|
48
|
+
window.clipboardData.setData("text", text);
|
|
49
|
+
success = true;
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
debug && console.error("unable to copy using clipboardData: ", err);
|
|
53
|
+
debug && console.error("falling back to prompt");
|
|
54
|
+
message = format(defaultMessage);
|
|
55
|
+
window.prompt(message, text);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
finally {
|
|
59
|
+
if (selection) {
|
|
60
|
+
if (typeof selection.removeRange == "function") {
|
|
61
|
+
selection.removeRange(range);
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
selection.removeAllRanges();
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
if (mark) {
|
|
68
|
+
document.body.removeChild(mark);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return success;
|
|
72
|
+
}
|
|
73
|
+
exports.default = copy;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import React, { ButtonHTMLAttributes } from "react";
|
|
2
|
+
export interface CopyButtonProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, "children"> {
|
|
3
|
+
/**
|
|
4
|
+
* @default "medium"
|
|
5
|
+
*/
|
|
6
|
+
size?: "medium" | "small";
|
|
7
|
+
/**
|
|
8
|
+
* @default "neutral"
|
|
9
|
+
*/
|
|
10
|
+
variant?: "action" | "neutral";
|
|
11
|
+
/**
|
|
12
|
+
* Text to copy to clipboard
|
|
13
|
+
*/
|
|
14
|
+
copyText: string;
|
|
15
|
+
/**
|
|
16
|
+
* Optional text in button
|
|
17
|
+
*/
|
|
18
|
+
text?: string;
|
|
19
|
+
/**
|
|
20
|
+
* Text shown when button is clicked
|
|
21
|
+
* Only set if used with 'text'-prop
|
|
22
|
+
*/
|
|
23
|
+
activeText?: string;
|
|
24
|
+
/**
|
|
25
|
+
* Callback when 'copied'-state is active
|
|
26
|
+
*/
|
|
27
|
+
onActiveChange?: (state: boolean) => void;
|
|
28
|
+
/**
|
|
29
|
+
* Icon shown when button is not clicked
|
|
30
|
+
* @default <FilesIcon />
|
|
31
|
+
*/
|
|
32
|
+
icon?: React.ReactNode;
|
|
33
|
+
/**
|
|
34
|
+
* Icon shown when active
|
|
35
|
+
* @default <CheckmarkIcon />
|
|
36
|
+
*/
|
|
37
|
+
activeIcon?: React.ReactNode;
|
|
38
|
+
/**
|
|
39
|
+
* Timeout duration in milliseconds
|
|
40
|
+
* @default 2000
|
|
41
|
+
*/
|
|
42
|
+
activeDuration?: number;
|
|
43
|
+
/**
|
|
44
|
+
* * accessible label for icon (ignored if text is set)
|
|
45
|
+
* @default 'Kopier'
|
|
46
|
+
*/
|
|
47
|
+
title?: string;
|
|
48
|
+
/**
|
|
49
|
+
* accessible label for icon in active-state (ignored if text is set)
|
|
50
|
+
* @default 'Kopiert'
|
|
51
|
+
*/
|
|
52
|
+
activeTitle?: string;
|
|
53
|
+
}
|
|
54
|
+
export declare const CopyButton: React.ForwardRefExoticComponent<CopyButtonProps & React.RefAttributes<HTMLButtonElement>>;
|
|
55
|
+
export default CopyButton;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
3
|
+
var t = {};
|
|
4
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
5
|
+
t[p] = s[p];
|
|
6
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
7
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
8
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
9
|
+
t[p[i]] = s[p[i]];
|
|
10
|
+
}
|
|
11
|
+
return t;
|
|
12
|
+
};
|
|
13
|
+
import { CheckmarkIcon, FilesIcon } from "@navikt/aksel-icons";
|
|
14
|
+
import cl from "clsx";
|
|
15
|
+
import React, { forwardRef, useEffect, useRef, useState, } from "react";
|
|
16
|
+
import copy from "../util/copy";
|
|
17
|
+
import Label from "../typography/Label";
|
|
18
|
+
export const CopyButton = forwardRef((_a, ref) => {
|
|
19
|
+
var { className, copyText, text, activeText = "Kopiert!", variant = "neutral", size = "medium", onActiveChange, icon, activeIcon, activeDuration = 2000, title = "Kopier", activeTitle = "Kopiert" } = _a, rest = __rest(_a, ["className", "copyText", "text", "activeText", "variant", "size", "onActiveChange", "icon", "activeIcon", "activeDuration", "title", "activeTitle"]);
|
|
20
|
+
const [active, setActive] = useState(false);
|
|
21
|
+
const timeoutRef = useRef();
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
return () => {
|
|
24
|
+
timeoutRef.current && clearTimeout(timeoutRef.current);
|
|
25
|
+
};
|
|
26
|
+
}, []);
|
|
27
|
+
const handleClick = (event) => {
|
|
28
|
+
var _a;
|
|
29
|
+
timeoutRef.current && clearTimeout(timeoutRef.current);
|
|
30
|
+
copy(copyText);
|
|
31
|
+
setActive(true);
|
|
32
|
+
onActiveChange === null || onActiveChange === void 0 ? void 0 : onActiveChange(true);
|
|
33
|
+
(_a = rest.onClick) === null || _a === void 0 ? void 0 : _a.call(rest, event);
|
|
34
|
+
timeoutRef.current = window.setTimeout(() => {
|
|
35
|
+
setActive(false);
|
|
36
|
+
onActiveChange === null || onActiveChange === void 0 ? void 0 : onActiveChange(false);
|
|
37
|
+
}, activeDuration);
|
|
38
|
+
};
|
|
39
|
+
return (React.createElement("button", Object.assign({ ref: ref, type: "button" }, rest, { "aria-live": "polite", className: cl("navds-copybutton", className, `navds-copybutton--${size}`, `navds-copybutton--${variant}`, {
|
|
40
|
+
"navds-copybutton--icon-only": !text,
|
|
41
|
+
"navds-copybutton--active": active,
|
|
42
|
+
}), onClick: handleClick }),
|
|
43
|
+
React.createElement("span", { className: "navds-copybutton__content" },
|
|
44
|
+
active ? (React.createElement("span", { className: "navds-copybutton__icon" }, activeIcon !== null && activeIcon !== void 0 ? activeIcon : (React.createElement(CheckmarkIcon, { "aria-hidden": !!text, title: text ? undefined : activeTitle })))) : (React.createElement("span", { className: "navds-copybutton__icon" }, icon !== null && icon !== void 0 ? icon : (React.createElement(FilesIcon, { "aria-hidden": !!text, title: text ? undefined : title })))),
|
|
45
|
+
text &&
|
|
46
|
+
(active ? (React.createElement(Label, { as: "span", size: size === "medium" ? "medium" : "small", "aria-live": "polite" }, activeText)) : (React.createElement(Label, { as: "span", size: size === "medium" ? "medium" : "small", "aria-live": "polite" }, text))))));
|
|
47
|
+
});
|
|
48
|
+
export default CopyButton;
|
|
49
|
+
//# sourceMappingURL=CopyButton.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CopyButton.js","sourceRoot":"","sources":["../../src/copybutton/CopyButton.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as CopyButton, type CopyButtonProps } from "./CopyButton";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/copybutton/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,UAAU,EAAwB,MAAM,cAAc,CAAC"}
|
package/esm/index.d.ts
CHANGED
|
@@ -3,9 +3,10 @@ export * from "./alert";
|
|
|
3
3
|
export * from "./button";
|
|
4
4
|
export * from "./chat";
|
|
5
5
|
export * from "./chips";
|
|
6
|
+
export * from "./copybutton";
|
|
6
7
|
export * from "./date";
|
|
7
|
-
export * from "./form";
|
|
8
8
|
export * from "./expansion-card";
|
|
9
|
+
export * from "./form";
|
|
9
10
|
export * from "./grid";
|
|
10
11
|
export * from "./guide-panel";
|
|
11
12
|
export * from "./help-text";
|
package/esm/index.js
CHANGED
|
@@ -3,9 +3,10 @@ export * from "./alert";
|
|
|
3
3
|
export * from "./button";
|
|
4
4
|
export * from "./chat";
|
|
5
5
|
export * from "./chips";
|
|
6
|
+
export * from "./copybutton";
|
|
6
7
|
export * from "./date";
|
|
7
|
-
export * from "./form";
|
|
8
8
|
export * from "./expansion-card";
|
|
9
|
+
export * from "./form";
|
|
9
10
|
export * from "./grid";
|
|
10
11
|
export * from "./guide-panel";
|
|
11
12
|
export * from "./help-text";
|
package/esm/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AACzB,cAAc,QAAQ,CAAC;AACvB,cAAc,SAAS,CAAC;AACxB,cAAc,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AACzB,cAAc,QAAQ,CAAC;AACvB,cAAc,SAAS,CAAC;AACxB,cAAc,cAAc,CAAC;AAC7B,cAAc,QAAQ,CAAC;AACvB,cAAc,kBAAkB,CAAC;AACjC,cAAc,QAAQ,CAAC;AACvB,cAAc,QAAQ,CAAC;AACvB,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,QAAQ,CAAC;AACvB,cAAc,cAAc,CAAC;AAC7B,cAAc,QAAQ,CAAC;AACvB,cAAc,UAAU,CAAC;AACzB,cAAc,SAAS,CAAC;AACxB,cAAc,cAAc,CAAC;AAC7B,cAAc,SAAS,CAAC;AACxB,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,SAAS,CAAC;AACxB,cAAc,QAAQ,CAAC;AACvB,cAAc,OAAO,CAAC;AACtB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,WAAW,CAAC;AAC1B,cAAc,cAAc,CAAC;AAC7B,cAAc,QAAQ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default function copy(text: any): boolean;
|
package/esm/util/copy.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// https://github.com/sudodoki/copy-to-clipboard/blob/main/index.js
|
|
2
|
+
const defaultMessage = "Kopier til utklippstavle: #{key}, Enter";
|
|
3
|
+
function format(message) {
|
|
4
|
+
const copyKey = (/mac os x/i.test(navigator.userAgent) ? "⌘" : "Ctrl") + "+C";
|
|
5
|
+
return message.replace(/#{\s*key\s*}/g, copyKey);
|
|
6
|
+
}
|
|
7
|
+
export default function copy(text) {
|
|
8
|
+
let debug, message, range, selection, mark, success = false;
|
|
9
|
+
debug = process.env.NODE_ENV !== "production";
|
|
10
|
+
try {
|
|
11
|
+
range = document.createRange();
|
|
12
|
+
selection = document.getSelection();
|
|
13
|
+
mark = document.createElement("span");
|
|
14
|
+
mark.textContent = text;
|
|
15
|
+
// avoid screen readers from reading out loud the text
|
|
16
|
+
mark.ariaHidden = "true";
|
|
17
|
+
// reset user styles for span element
|
|
18
|
+
mark.style.all = "unset";
|
|
19
|
+
// prevents scrolling to the end of the page
|
|
20
|
+
mark.style.position = "fixed";
|
|
21
|
+
mark.style.top = 0;
|
|
22
|
+
mark.style.clip = "rect(0, 0, 0, 0)";
|
|
23
|
+
// used to preserve spaces and line breaks
|
|
24
|
+
mark.style.whiteSpace = "pre";
|
|
25
|
+
// do not inherit user-select (it may be `none`)
|
|
26
|
+
mark.style.webkitUserSelect = "text";
|
|
27
|
+
mark.style.MozUserSelect = "text";
|
|
28
|
+
mark.style.msUserSelect = "text";
|
|
29
|
+
mark.style.userSelect = "text";
|
|
30
|
+
mark.addEventListener("copy", function (e) {
|
|
31
|
+
e.stopPropagation();
|
|
32
|
+
});
|
|
33
|
+
document.body.appendChild(mark);
|
|
34
|
+
range.selectNodeContents(mark);
|
|
35
|
+
selection.addRange(range);
|
|
36
|
+
const successful = document.execCommand("copy");
|
|
37
|
+
if (!successful) {
|
|
38
|
+
throw new Error("copy command was unsuccessful");
|
|
39
|
+
}
|
|
40
|
+
success = true;
|
|
41
|
+
}
|
|
42
|
+
catch (err) {
|
|
43
|
+
debug && console.error("unable to copy using execCommand: ", err);
|
|
44
|
+
debug && console.warn("trying IE specific stuff");
|
|
45
|
+
try {
|
|
46
|
+
window.clipboardData.setData("text", text);
|
|
47
|
+
success = true;
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
debug && console.error("unable to copy using clipboardData: ", err);
|
|
51
|
+
debug && console.error("falling back to prompt");
|
|
52
|
+
message = format(defaultMessage);
|
|
53
|
+
window.prompt(message, text);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
finally {
|
|
57
|
+
if (selection) {
|
|
58
|
+
if (typeof selection.removeRange == "function") {
|
|
59
|
+
selection.removeRange(range);
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
selection.removeAllRanges();
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
if (mark) {
|
|
66
|
+
document.body.removeChild(mark);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return success;
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=copy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"copy.js","sourceRoot":"","sources":["../../src/util/copy.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@navikt/ds-react",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.2.0",
|
|
4
4
|
"description": "Aksel react-components for NAV designsystem",
|
|
5
5
|
"author": "Aksel | NAV designsystem team",
|
|
6
6
|
"license": "MIT",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
40
|
"@floating-ui/react": "0.17.0",
|
|
41
|
-
"@navikt/aksel-icons": "^3.
|
|
41
|
+
"@navikt/aksel-icons": "^3.2.0",
|
|
42
42
|
"@radix-ui/react-tabs": "1.0.0",
|
|
43
43
|
"@radix-ui/react-toggle-group": "1.0.0",
|
|
44
44
|
"clsx": "^1.2.1",
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { CheckmarkIcon, FilesIcon } from "@navikt/aksel-icons";
|
|
3
|
+
import cl from "clsx";
|
|
4
|
+
import React, {
|
|
5
|
+
ButtonHTMLAttributes,
|
|
6
|
+
forwardRef,
|
|
7
|
+
useEffect,
|
|
8
|
+
useRef,
|
|
9
|
+
useState,
|
|
10
|
+
} from "react";
|
|
11
|
+
import copy from "../util/copy";
|
|
12
|
+
import Label from "../typography/Label";
|
|
13
|
+
|
|
14
|
+
export interface CopyButtonProps
|
|
15
|
+
extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, "children"> {
|
|
16
|
+
/**
|
|
17
|
+
* @default "medium"
|
|
18
|
+
*/
|
|
19
|
+
size?: "medium" | "small";
|
|
20
|
+
/**
|
|
21
|
+
* @default "neutral"
|
|
22
|
+
*/
|
|
23
|
+
variant?: "action" | "neutral";
|
|
24
|
+
/**
|
|
25
|
+
* Text to copy to clipboard
|
|
26
|
+
*/
|
|
27
|
+
copyText: string;
|
|
28
|
+
/**
|
|
29
|
+
* Optional text in button
|
|
30
|
+
*/
|
|
31
|
+
text?: string;
|
|
32
|
+
/**
|
|
33
|
+
* Text shown when button is clicked
|
|
34
|
+
* Only set if used with 'text'-prop
|
|
35
|
+
*/
|
|
36
|
+
activeText?: string;
|
|
37
|
+
/**
|
|
38
|
+
* Callback when 'copied'-state is active
|
|
39
|
+
*/
|
|
40
|
+
onActiveChange?: (state: boolean) => void;
|
|
41
|
+
/**
|
|
42
|
+
* Icon shown when button is not clicked
|
|
43
|
+
* @default <FilesIcon />
|
|
44
|
+
*/
|
|
45
|
+
icon?: React.ReactNode;
|
|
46
|
+
/**
|
|
47
|
+
* Icon shown when active
|
|
48
|
+
* @default <CheckmarkIcon />
|
|
49
|
+
*/
|
|
50
|
+
activeIcon?: React.ReactNode;
|
|
51
|
+
/**
|
|
52
|
+
* Timeout duration in milliseconds
|
|
53
|
+
* @default 2000
|
|
54
|
+
*/
|
|
55
|
+
activeDuration?: number;
|
|
56
|
+
/**
|
|
57
|
+
* * accessible label for icon (ignored if text is set)
|
|
58
|
+
* @default 'Kopier'
|
|
59
|
+
*/
|
|
60
|
+
title?: string;
|
|
61
|
+
/**
|
|
62
|
+
* accessible label for icon in active-state (ignored if text is set)
|
|
63
|
+
* @default 'Kopiert'
|
|
64
|
+
*/
|
|
65
|
+
activeTitle?: string;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export const CopyButton = forwardRef<HTMLButtonElement, CopyButtonProps>(
|
|
69
|
+
(
|
|
70
|
+
{
|
|
71
|
+
className,
|
|
72
|
+
copyText,
|
|
73
|
+
text,
|
|
74
|
+
activeText = "Kopiert!",
|
|
75
|
+
variant = "neutral",
|
|
76
|
+
size = "medium",
|
|
77
|
+
onActiveChange,
|
|
78
|
+
icon,
|
|
79
|
+
activeIcon,
|
|
80
|
+
activeDuration = 2000,
|
|
81
|
+
title = "Kopier",
|
|
82
|
+
activeTitle = "Kopiert",
|
|
83
|
+
...rest
|
|
84
|
+
},
|
|
85
|
+
ref
|
|
86
|
+
) => {
|
|
87
|
+
const [active, setActive] = useState(false);
|
|
88
|
+
const timeoutRef = useRef<number>();
|
|
89
|
+
|
|
90
|
+
useEffect(() => {
|
|
91
|
+
return () => {
|
|
92
|
+
timeoutRef.current && clearTimeout(timeoutRef.current);
|
|
93
|
+
};
|
|
94
|
+
}, []);
|
|
95
|
+
|
|
96
|
+
const handleClick = (
|
|
97
|
+
event: React.MouseEvent<HTMLButtonElement, MouseEvent>
|
|
98
|
+
) => {
|
|
99
|
+
timeoutRef.current && clearTimeout(timeoutRef.current);
|
|
100
|
+
copy(copyText);
|
|
101
|
+
setActive(true);
|
|
102
|
+
onActiveChange?.(true);
|
|
103
|
+
rest.onClick?.(event);
|
|
104
|
+
|
|
105
|
+
timeoutRef.current = window.setTimeout(() => {
|
|
106
|
+
setActive(false);
|
|
107
|
+
onActiveChange?.(false);
|
|
108
|
+
}, activeDuration);
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
return (
|
|
112
|
+
<button
|
|
113
|
+
ref={ref}
|
|
114
|
+
type="button"
|
|
115
|
+
{...rest}
|
|
116
|
+
aria-live="polite"
|
|
117
|
+
className={cl(
|
|
118
|
+
"navds-copybutton",
|
|
119
|
+
className,
|
|
120
|
+
`navds-copybutton--${size}`,
|
|
121
|
+
`navds-copybutton--${variant}`,
|
|
122
|
+
{
|
|
123
|
+
"navds-copybutton--icon-only": !text,
|
|
124
|
+
"navds-copybutton--active": active,
|
|
125
|
+
}
|
|
126
|
+
)}
|
|
127
|
+
onClick={handleClick}
|
|
128
|
+
>
|
|
129
|
+
<span className="navds-copybutton__content">
|
|
130
|
+
{active ? (
|
|
131
|
+
<span className="navds-copybutton__icon">
|
|
132
|
+
{activeIcon ?? (
|
|
133
|
+
<CheckmarkIcon
|
|
134
|
+
aria-hidden={!!text}
|
|
135
|
+
title={text ? undefined : activeTitle}
|
|
136
|
+
/>
|
|
137
|
+
)}
|
|
138
|
+
</span>
|
|
139
|
+
) : (
|
|
140
|
+
<span className="navds-copybutton__icon">
|
|
141
|
+
{icon ?? (
|
|
142
|
+
<FilesIcon
|
|
143
|
+
aria-hidden={!!text}
|
|
144
|
+
title={text ? undefined : title}
|
|
145
|
+
/>
|
|
146
|
+
)}
|
|
147
|
+
</span>
|
|
148
|
+
)}
|
|
149
|
+
|
|
150
|
+
{text &&
|
|
151
|
+
(active ? (
|
|
152
|
+
<Label
|
|
153
|
+
as="span"
|
|
154
|
+
size={size === "medium" ? "medium" : "small"}
|
|
155
|
+
aria-live="polite"
|
|
156
|
+
>
|
|
157
|
+
{activeText}
|
|
158
|
+
</Label>
|
|
159
|
+
) : (
|
|
160
|
+
<Label
|
|
161
|
+
as="span"
|
|
162
|
+
size={size === "medium" ? "medium" : "small"}
|
|
163
|
+
aria-live="polite"
|
|
164
|
+
>
|
|
165
|
+
{text}
|
|
166
|
+
</Label>
|
|
167
|
+
))}
|
|
168
|
+
</span>
|
|
169
|
+
</button>
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
export default CopyButton;
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { LinkIcon, ThumbUpIcon } from "@navikt/aksel-icons";
|
|
2
|
+
import { userEvent, within } from "@storybook/testing-library";
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { CopyButton } from ".";
|
|
5
|
+
import { Tooltip } from "../tooltip";
|
|
6
|
+
|
|
7
|
+
export default {
|
|
8
|
+
title: "ds-react/CopyButton",
|
|
9
|
+
component: CopyButton,
|
|
10
|
+
argTypes: {
|
|
11
|
+
size: {
|
|
12
|
+
defaultValue: "medium",
|
|
13
|
+
control: { type: "radio" },
|
|
14
|
+
options: ["small", "medium"],
|
|
15
|
+
},
|
|
16
|
+
variant: {
|
|
17
|
+
defaultValue: undefined,
|
|
18
|
+
control: { type: "radio" },
|
|
19
|
+
options: ["neutral", "action"],
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export const Default = {
|
|
25
|
+
render: (args) => <CopyButton {...args} />,
|
|
26
|
+
args: {
|
|
27
|
+
size: "medium",
|
|
28
|
+
duration: 2000,
|
|
29
|
+
copyText: "3.14",
|
|
30
|
+
text: "",
|
|
31
|
+
activeText: "",
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export const Interaction = {
|
|
36
|
+
render: () => (
|
|
37
|
+
<CopyButton
|
|
38
|
+
copyText="3.14"
|
|
39
|
+
variant="action"
|
|
40
|
+
text="Kopier"
|
|
41
|
+
data-testid="copy-button"
|
|
42
|
+
/>
|
|
43
|
+
),
|
|
44
|
+
play: async ({ canvasElement }) => {
|
|
45
|
+
const canvas = within(canvasElement);
|
|
46
|
+
|
|
47
|
+
const button = canvas.getByTestId("copy-button");
|
|
48
|
+
|
|
49
|
+
userEvent.click(button);
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export const Variants = {
|
|
54
|
+
render: () => (
|
|
55
|
+
<div className="colgap">
|
|
56
|
+
<CopyButton copyText="3.14" variant="action" text="Kopier" />
|
|
57
|
+
|
|
58
|
+
<CopyButton copyText="3.14" variant="neutral" text="Kopier" />
|
|
59
|
+
</div>
|
|
60
|
+
),
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export const Sizes = {
|
|
64
|
+
render: () => (
|
|
65
|
+
<div className="colgap">
|
|
66
|
+
<div className="rowgap">
|
|
67
|
+
<CopyButton copyText="3.14" variant="action" />
|
|
68
|
+
<CopyButton copyText="3.14" variant="neutral" />
|
|
69
|
+
<CopyButton copyText="3.14" variant="action" text="Kopier" />
|
|
70
|
+
<CopyButton copyText="3.14" variant="neutral" text="Kopier" />
|
|
71
|
+
</div>
|
|
72
|
+
<div className="rowgap">
|
|
73
|
+
<CopyButton size="small" copyText="3.14" variant="action" />
|
|
74
|
+
<CopyButton size="small" copyText="3.14" variant="neutral" />
|
|
75
|
+
<CopyButton
|
|
76
|
+
size="small"
|
|
77
|
+
copyText="3.14"
|
|
78
|
+
variant="action"
|
|
79
|
+
text="Kopier"
|
|
80
|
+
/>
|
|
81
|
+
<CopyButton
|
|
82
|
+
size="small"
|
|
83
|
+
copyText="3.14"
|
|
84
|
+
variant="neutral"
|
|
85
|
+
text="Kopier"
|
|
86
|
+
/>
|
|
87
|
+
</div>
|
|
88
|
+
</div>
|
|
89
|
+
),
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
export const Texts = {
|
|
93
|
+
render: () => (
|
|
94
|
+
<div className="colgap">
|
|
95
|
+
<div>
|
|
96
|
+
<CopyButton
|
|
97
|
+
copyText="3.14"
|
|
98
|
+
text="Kopier XYZ"
|
|
99
|
+
activeText="Kopierte XYZ"
|
|
100
|
+
/>
|
|
101
|
+
</div>
|
|
102
|
+
<div>
|
|
103
|
+
<CopyButton
|
|
104
|
+
copyText="3.14"
|
|
105
|
+
size="small"
|
|
106
|
+
text="Kopier XYZ"
|
|
107
|
+
activeText="Kopierte XYZ"
|
|
108
|
+
/>
|
|
109
|
+
</div>
|
|
110
|
+
</div>
|
|
111
|
+
),
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
export const Icons = {
|
|
115
|
+
render: () => (
|
|
116
|
+
<div className="rowgap">
|
|
117
|
+
<div>
|
|
118
|
+
<CopyButton
|
|
119
|
+
copyText="3.14"
|
|
120
|
+
icon={<LinkIcon title="Kopier" />}
|
|
121
|
+
activeIcon={<ThumbUpIcon title="Kopiert" />}
|
|
122
|
+
/>
|
|
123
|
+
</div>
|
|
124
|
+
<div>
|
|
125
|
+
<CopyButton
|
|
126
|
+
copyText="3.14"
|
|
127
|
+
size="small"
|
|
128
|
+
icon={<LinkIcon title="Kopier" />}
|
|
129
|
+
activeIcon={<ThumbUpIcon title="Kopiert" />}
|
|
130
|
+
/>
|
|
131
|
+
</div>
|
|
132
|
+
</div>
|
|
133
|
+
),
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
export const InlineDemo = {
|
|
137
|
+
render: () => (
|
|
138
|
+
<div style={{ display: "flex", gap: "0.5rem", alignItems: "center" }}>
|
|
139
|
+
<CopyButton size="small" copyText="3.14" /> Kopier dette feltet
|
|
140
|
+
</div>
|
|
141
|
+
),
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
export const WithTooltip = {
|
|
145
|
+
render: () => {
|
|
146
|
+
return (
|
|
147
|
+
<div>
|
|
148
|
+
<Tooltip content="Kopier fødselsnummer">
|
|
149
|
+
<CopyButton copyText="3.14" />
|
|
150
|
+
</Tooltip>
|
|
151
|
+
</div>
|
|
152
|
+
);
|
|
153
|
+
},
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
export const Duration = {
|
|
157
|
+
render: () => <CopyButton copyText="3.14" activeDuration={300} />,
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
export const Disabled = {
|
|
161
|
+
render: () => (
|
|
162
|
+
<div className="colgap">
|
|
163
|
+
<CopyButton copyText="3.14" disabled />
|
|
164
|
+
<CopyButton copyText="3.14" size="small" disabled />
|
|
165
|
+
<CopyButton copyText="3.14" disabled variant="action" />
|
|
166
|
+
</div>
|
|
167
|
+
),
|
|
168
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as CopyButton, type CopyButtonProps } from "./CopyButton";
|
package/src/index.ts
CHANGED
|
@@ -3,9 +3,10 @@ export * from "./alert";
|
|
|
3
3
|
export * from "./button";
|
|
4
4
|
export * from "./chat";
|
|
5
5
|
export * from "./chips";
|
|
6
|
+
export * from "./copybutton";
|
|
6
7
|
export * from "./date";
|
|
7
|
-
export * from "./form";
|
|
8
8
|
export * from "./expansion-card";
|
|
9
|
+
export * from "./form";
|
|
9
10
|
export * from "./grid";
|
|
10
11
|
export * from "./guide-panel";
|
|
11
12
|
export * from "./help-text";
|
package/src/util/copy.ts
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// https://github.com/sudodoki/copy-to-clipboard/blob/main/index.js
|
|
2
|
+
|
|
3
|
+
const defaultMessage = "Kopier til utklippstavle: #{key}, Enter";
|
|
4
|
+
|
|
5
|
+
function format(message) {
|
|
6
|
+
const copyKey = (/mac os x/i.test(navigator.userAgent) ? "⌘" : "Ctrl") + "+C";
|
|
7
|
+
return message.replace(/#{\s*key\s*}/g, copyKey);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export default function copy(text) {
|
|
11
|
+
let debug,
|
|
12
|
+
message,
|
|
13
|
+
range,
|
|
14
|
+
selection,
|
|
15
|
+
mark,
|
|
16
|
+
success = false;
|
|
17
|
+
debug = process.env.NODE_ENV !== "production";
|
|
18
|
+
try {
|
|
19
|
+
range = document.createRange();
|
|
20
|
+
selection = document.getSelection();
|
|
21
|
+
|
|
22
|
+
mark = document.createElement("span");
|
|
23
|
+
mark.textContent = text;
|
|
24
|
+
// avoid screen readers from reading out loud the text
|
|
25
|
+
mark.ariaHidden = "true";
|
|
26
|
+
// reset user styles for span element
|
|
27
|
+
mark.style.all = "unset";
|
|
28
|
+
// prevents scrolling to the end of the page
|
|
29
|
+
mark.style.position = "fixed";
|
|
30
|
+
mark.style.top = 0;
|
|
31
|
+
mark.style.clip = "rect(0, 0, 0, 0)";
|
|
32
|
+
// used to preserve spaces and line breaks
|
|
33
|
+
mark.style.whiteSpace = "pre";
|
|
34
|
+
// do not inherit user-select (it may be `none`)
|
|
35
|
+
mark.style.webkitUserSelect = "text";
|
|
36
|
+
mark.style.MozUserSelect = "text";
|
|
37
|
+
mark.style.msUserSelect = "text";
|
|
38
|
+
mark.style.userSelect = "text";
|
|
39
|
+
mark.addEventListener("copy", function (e) {
|
|
40
|
+
e.stopPropagation();
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
document.body.appendChild(mark);
|
|
44
|
+
|
|
45
|
+
range.selectNodeContents(mark);
|
|
46
|
+
selection.addRange(range);
|
|
47
|
+
|
|
48
|
+
const successful = document.execCommand("copy");
|
|
49
|
+
if (!successful) {
|
|
50
|
+
throw new Error("copy command was unsuccessful");
|
|
51
|
+
}
|
|
52
|
+
success = true;
|
|
53
|
+
} catch (err) {
|
|
54
|
+
debug && console.error("unable to copy using execCommand: ", err);
|
|
55
|
+
debug && console.warn("trying IE specific stuff");
|
|
56
|
+
try {
|
|
57
|
+
(window as any).clipboardData.setData("text", text);
|
|
58
|
+
|
|
59
|
+
success = true;
|
|
60
|
+
} catch (err) {
|
|
61
|
+
debug && console.error("unable to copy using clipboardData: ", err);
|
|
62
|
+
debug && console.error("falling back to prompt");
|
|
63
|
+
message = format(defaultMessage);
|
|
64
|
+
window.prompt(message, text);
|
|
65
|
+
}
|
|
66
|
+
} finally {
|
|
67
|
+
if (selection) {
|
|
68
|
+
if (typeof selection.removeRange == "function") {
|
|
69
|
+
selection.removeRange(range);
|
|
70
|
+
} else {
|
|
71
|
+
selection.removeAllRanges();
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (mark) {
|
|
76
|
+
document.body.removeChild(mark);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return success;
|
|
81
|
+
}
|