Added sample "react-timeline"

This commit is contained in:
Nanddeep Nachan 2020-07-08 07:13:15 +00:00
parent 084e745567
commit daa67c1fa7
45 changed files with 19452 additions and 144 deletions

View File

@ -5,35 +5,53 @@
"requires": true,
"dependencies": {
"@babel/code-frame": {
"version": "7.10.1",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.1.tgz",
"integrity": "sha512-IGhtTmpjGbYzcEDOw7DcQtbQSXcG9ftmAXtWTu9V936vDye4xjjekktFAtgZsWpzTj/X01jocB46mTywm/4SZw==",
"version": "7.10.3",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.3.tgz",
"integrity": "sha512-fDx9eNW0qz0WkUeqL6tXEXzVlPh6Y5aCDEZesl0xBGA8ndRukX91Uk44ZqnkECp01NAZUdCAl+aiQNGi0k88Eg==",
"dev": true,
"requires": {
"@babel/highlight": "^7.10.1"
"@babel/highlight": "^7.10.3"
}
},
"@babel/helper-validator-identifier": {
"version": "7.10.1",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.1.tgz",
"integrity": "sha512-5vW/JXLALhczRCWP0PnFDMCJAchlBvM7f4uk/jXritBnIa6E1KmqmtrS3yn1LAnxFBypQ3eneLuXjsnfQsgILw==",
"version": "7.10.3",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.3.tgz",
"integrity": "sha512-bU8JvtlYpJSBPuj1VUmKpFGaDZuLxASky3LhaKj3bmpSTY6VWooSM8msk+Z0CZoErFye2tlABF6yDkT3FOPAXw==",
"dev": true
},
"@babel/highlight": {
"version": "7.10.1",
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.1.tgz",
"integrity": "sha512-8rMof+gVP8mxYZApLF/JgNDAkdKa+aJt3ZYxF8z6+j/hpeXL7iMsKCPHa2jNMHu/qqBwzQF4OHNoYi8dMA/rYg==",
"version": "7.10.3",
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.3.tgz",
"integrity": "sha512-Ih9B/u7AtgEnySE2L2F0Xm0GaM729XqqLfHkalTsbjXGyqmf/6M0Cu0WpvqueUlW+xk88BHw9Nkpj49naU+vWw==",
"dev": true,
"requires": {
"@babel/helper-validator-identifier": "^7.10.1",
"@babel/helper-validator-identifier": "^7.10.3",
"chalk": "^2.0.0",
"js-tokens": "^4.0.0"
}
},
"@fluentui/date-time-utilities": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/@fluentui/date-time-utilities/-/date-time-utilities-7.1.1.tgz",
"integrity": "sha512-TcUzz/WY1e7petcahB5a5999vmZHnPOZsRWiZ2Igbo+Os5BnJxGeLkHVHaest6iYBsYmbcdNeE02+aUF7YDfGA==",
"dev": true,
"requires": {
"@uifabric/set-version": "^7.0.14",
"tslib": "^1.10.0"
},
"dependencies": {
"tslib": {
"version": "1.13.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz",
"integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==",
"dev": true
}
}
},
"@fluentui/keyboard-key": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/@fluentui/keyboard-key/-/keyboard-key-0.2.1.tgz",
"integrity": "sha512-s2CYcspWWdqzwXNOvkNURifuRRiZun/5CQ3gcvRw9+S9/ONvPtedRkppNeTyj2wbW6Ctzf218bu2eJqu0aVK/Q==",
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/@fluentui/keyboard-key/-/keyboard-key-0.2.2.tgz",
"integrity": "sha512-lUacGG22XRuf8YJPMIrwOxYjdL3cGxSK/PjzCt+LMC4dE+Lc5hi1wWFgLc950/OQsF+V54aVTIWuAGhtpuZOqw==",
"dev": true,
"requires": {
"tslib": "^1.10.0"
@ -48,16 +66,16 @@
}
},
"@fluentui/react-focus": {
"version": "7.12.7",
"resolved": "https://registry.npmjs.org/@fluentui/react-focus/-/react-focus-7.12.7.tgz",
"integrity": "sha512-L7FTkpJAQO/ceoRtwVnjnUjlKokunFT16cLIeqpKLoNvjMNMmW9xanfW21HTp8zuI4K1HjQl8BV22WP2mRow8Q==",
"version": "7.12.11",
"resolved": "https://registry.npmjs.org/@fluentui/react-focus/-/react-focus-7.12.11.tgz",
"integrity": "sha512-47tMEuTMVxtwIV+OVmdrHEneQiM1uVk6LRrtN4xZfQXWggWMbwhY9jaXkB3FKQZUcQJE/1xc9ucsmKqpsGbe8g==",
"dev": true,
"requires": {
"@fluentui/keyboard-key": "^0.2.1",
"@uifabric/merge-styles": "^7.14.1",
"@uifabric/set-version": "^7.0.13",
"@uifabric/styling": "^7.12.17",
"@uifabric/utilities": "^7.20.3",
"@fluentui/keyboard-key": "^0.2.2",
"@uifabric/merge-styles": "^7.15.0",
"@uifabric/set-version": "^7.0.14",
"@uifabric/styling": "^7.13.2",
"@uifabric/utilities": "^7.21.2",
"tslib": "^1.10.0"
},
"dependencies": {
@ -70,14 +88,14 @@
}
},
"@fluentui/react-icons": {
"version": "0.1.27",
"resolved": "https://registry.npmjs.org/@fluentui/react-icons/-/react-icons-0.1.27.tgz",
"integrity": "sha512-ixasJn4M9wyyzd8HDGDb9pXKAzKRM7wM4azpRdjaZ2oTQnI1VT/h7dfNmVDDt6GFGIJoNXl59krJWwZEpUD+hA==",
"version": "0.1.30",
"resolved": "https://registry.npmjs.org/@fluentui/react-icons/-/react-icons-0.1.30.tgz",
"integrity": "sha512-kouS3lJTPUQUI/jBAtt4Rz3pn27lY93txmQJr03JFMC7e+BF+EaXr4rVUnObPPFv0jKYjj/VFH2GxG68PbOYWQ==",
"dev": true,
"requires": {
"@microsoft/load-themed-styles": "^1.10.26",
"@uifabric/set-version": "^7.0.13",
"@uifabric/utilities": "^7.20.3",
"@uifabric/set-version": "^7.0.14",
"@uifabric/utilities": "^7.21.2",
"tslib": "^1.10.0"
},
"dependencies": {
@ -1446,9 +1464,9 @@
}
},
"@microsoft/load-themed-styles": {
"version": "1.10.57",
"resolved": "https://registry.npmjs.org/@microsoft/load-themed-styles/-/load-themed-styles-1.10.57.tgz",
"integrity": "sha512-Q/F2X9pxJzfolYdaZHxKrzrorXeEdORCkLqHmqjD83ASD7W8tKQta3eK5neqzcOGxbbH7R0gpb71JokgWnYxsQ=="
"version": "1.10.61",
"resolved": "https://registry.npmjs.org/@microsoft/load-themed-styles/-/load-themed-styles-1.10.61.tgz",
"integrity": "sha512-mP7OWOxs63AxS0XKNibYLF46KMaREqTaoL1sgHYJR8yMOCte/zPC7aPeM1d8hwA3Xukr3QLXXp7psmgRvGRztA=="
},
"@microsoft/loader-cased-file": {
"version": "1.10.0",
@ -2617,9 +2635,9 @@
"integrity": "sha512-HKJFVLCGrWQ/1unEw8JdaTxu6n3EUxmwTxJ6D0O1x0gD8joCsgoTWxEgevb7fp2XIogNjof3KEd+3bJoGne/nw=="
},
"@types/express-serve-static-core": {
"version": "4.17.7",
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.7.tgz",
"integrity": "sha512-EMgTj/DF9qpgLXyc+Btimg+XoH7A2liE8uKul8qSmMTHCeNYzydDKFdsJskDvw42UsesCnhO63dO0Grbj8J4Dw==",
"version": "4.17.8",
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.8.tgz",
"integrity": "sha512-1SJZ+R3Q/7mLkOD9ewCBDYD2k0WyZQtWYqF/2VvoNN2/uhI49J9CDN4OAm+wGMA0DbArA4ef27xl4+JwMtGggw==",
"dev": true,
"requires": {
"@types/node": "*",
@ -2860,9 +2878,9 @@
"dev": true
},
"@types/tapable": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/@types/tapable/-/tapable-1.0.5.tgz",
"integrity": "sha512-/gG2M/Imw7cQFp8PGvz/SwocNrmKFjFsm5Pb8HdbHkZ1K8pmuPzOX4VeVoiEecFCVf4CsN1r3/BRvx+6sNqwtQ==",
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/@types/tapable/-/tapable-1.0.6.tgz",
"integrity": "sha512-W+bw9ds02rAQaMvaLYxAbJ6cvguW/iJXNT6lTssS1ps6QdrMKttqEAMEG/b5CR8TZl3/L7/lH0ZV5nNR1LXikA==",
"dev": true
},
"@types/terser-webpack-plugin": {
@ -3023,14 +3041,14 @@
"integrity": "sha1-LrHQCl5Ow/pYx2r94S4YK2bcXBw="
},
"@uifabric/foundation": {
"version": "7.7.24",
"resolved": "https://registry.npmjs.org/@uifabric/foundation/-/foundation-7.7.24.tgz",
"integrity": "sha512-N4MUx9g9c973J8QiT3OOzjNlqck/BTZ1lwbRraQnQBqYO/6Ffb9/ikjMwLADvZ5dVzTxKQcB1p05muctcEurYw==",
"version": "7.7.28",
"resolved": "https://registry.npmjs.org/@uifabric/foundation/-/foundation-7.7.28.tgz",
"integrity": "sha512-yjR4ImrIBPE8ZaJ+/GHIPABXywLaFiYHLLX0HC+7f517f/cEt/xP8mdeRcAf+lznK+onyNfKUAdLZamjxXNrHQ==",
"requires": {
"@uifabric/merge-styles": "^7.14.1",
"@uifabric/set-version": "^7.0.13",
"@uifabric/styling": "^7.12.17",
"@uifabric/utilities": "^7.20.3",
"@uifabric/merge-styles": "^7.15.0",
"@uifabric/set-version": "^7.0.14",
"@uifabric/styling": "^7.13.2",
"@uifabric/utilities": "^7.21.2",
"tslib": "^1.10.0"
},
"dependencies": {
@ -3052,11 +3070,11 @@
}
},
"@uifabric/merge-styles": {
"version": "7.14.1",
"resolved": "https://registry.npmjs.org/@uifabric/merge-styles/-/merge-styles-7.14.1.tgz",
"integrity": "sha512-nKkk0o9XyVh8HL174ZSDqw3IUnN2qb+kO73vg/rwioKPEQyuPGoEfij8jrb+CGpcCGnMYi53IeB1tm8ySlNatg==",
"version": "7.15.0",
"resolved": "https://registry.npmjs.org/@uifabric/merge-styles/-/merge-styles-7.15.0.tgz",
"integrity": "sha512-dB2Pa0LSOmma+mZwpKEyc2l2WFUw3XfvXF599osuVfUAtgJHUYsCEOxJN2olt67apDYL2GYyMcBX5tlSId+rIQ==",
"requires": {
"@uifabric/set-version": "^7.0.13",
"@uifabric/set-version": "^7.0.14",
"tslib": "^1.10.0"
},
"dependencies": {
@ -3068,12 +3086,12 @@
}
},
"@uifabric/react-hooks": {
"version": "7.4.5",
"resolved": "https://registry.npmjs.org/@uifabric/react-hooks/-/react-hooks-7.4.5.tgz",
"integrity": "sha512-OLEBII+7x4rlTWjQ6hvMWS+CuWRJgvEfCsUcFMu5D5teXTr0bZQThyW8oVvkqKsNmF2JdwwqT6dSwhwgarlFzA==",
"version": "7.4.8",
"resolved": "https://registry.npmjs.org/@uifabric/react-hooks/-/react-hooks-7.4.8.tgz",
"integrity": "sha512-ZIST1D3c8JkUrBWy/jZDRpoE2DDfty3LgJudfzK0IwSiltt7+VRuzRoKkkhckvOcDqZsmf7nIboig4+967c1AA==",
"requires": {
"@uifabric/set-version": "^7.0.13",
"@uifabric/utilities": "^7.20.3",
"@uifabric/set-version": "^7.0.14",
"@uifabric/utilities": "^7.21.2",
"tslib": "^1.10.0"
},
"dependencies": {
@ -3085,9 +3103,9 @@
}
},
"@uifabric/set-version": {
"version": "7.0.13",
"resolved": "https://registry.npmjs.org/@uifabric/set-version/-/set-version-7.0.13.tgz",
"integrity": "sha512-SRsYaacvNykS9lRwKNJgrJuhPV4ytblthFNg0+Wi6+zvIf/w50k/nBlmXVetV5U9dAuX4njSkd+/3iOpgevkyw==",
"version": "7.0.14",
"resolved": "https://registry.npmjs.org/@uifabric/set-version/-/set-version-7.0.14.tgz",
"integrity": "sha512-Bkrkpbs896hCsDFyt0yNES4tfUd9Wjg4Idi+kg36kLMuImxNU7EGXo8hnu8tgVwMvvIKdNvwD+L+7n8YTLsMnQ==",
"requires": {
"tslib": "^1.10.0"
},
@ -3100,14 +3118,14 @@
}
},
"@uifabric/styling": {
"version": "7.12.17",
"resolved": "https://registry.npmjs.org/@uifabric/styling/-/styling-7.12.17.tgz",
"integrity": "sha512-dUytYJje0dvRVhBirQeXmFoI84Oegef4Y9545Vy1fqfNvx2ipMQPkSg1xwFBsgDrbwGaYH7ZPukqyMO6H/2ZdQ==",
"version": "7.13.2",
"resolved": "https://registry.npmjs.org/@uifabric/styling/-/styling-7.13.2.tgz",
"integrity": "sha512-jspuHuKPKRaNV+hb+Cdd70JPVDH9qaeTEdTgewEOrjVBVuc2GQoq6Qq0lwFCZP11oDdNpoFKTOeATSszj8kDVw==",
"requires": {
"@microsoft/load-themed-styles": "^1.10.26",
"@uifabric/merge-styles": "^7.14.1",
"@uifabric/set-version": "^7.0.13",
"@uifabric/utilities": "^7.20.3",
"@uifabric/merge-styles": "^7.15.0",
"@uifabric/set-version": "^7.0.14",
"@uifabric/utilities": "^7.21.2",
"tslib": "^1.10.0"
},
"dependencies": {
@ -3119,12 +3137,12 @@
}
},
"@uifabric/utilities": {
"version": "7.20.3",
"resolved": "https://registry.npmjs.org/@uifabric/utilities/-/utilities-7.20.3.tgz",
"integrity": "sha512-Amg+qdnNKx0yxjoEFHanM2jTCYfCZAlHPDHcS+BCiSwzeQrEPhlOrtZVPJtagNVhXLFiC09uDsmE/BF6dHb+ww==",
"version": "7.21.2",
"resolved": "https://registry.npmjs.org/@uifabric/utilities/-/utilities-7.21.2.tgz",
"integrity": "sha512-fc0gTWiuo46A6TnjSgTL9XdnMesWoMdIc2srGDQGPWGhCEh6DWOhyzNxghPvf/MfQMqUlankLzosG7OzSU0Nog==",
"requires": {
"@uifabric/merge-styles": "^7.14.1",
"@uifabric/set-version": "^7.0.13",
"@uifabric/merge-styles": "^7.15.0",
"@uifabric/set-version": "^7.0.14",
"prop-types": "^15.7.2",
"tslib": "^1.10.0"
},
@ -3148,13 +3166,13 @@
},
"dependencies": {
"@uifabric/icons": {
"version": "7.3.50",
"resolved": "https://registry.npmjs.org/@uifabric/icons/-/icons-7.3.50.tgz",
"integrity": "sha512-1xW423TRy50FDukquNjWWIrIlpz3a4beH6HK0J1PgvsTl2KrQZ+fWbVZL8ylbKLwJutaCb/5ouHJIT1VcIGWvg==",
"version": "7.3.54",
"resolved": "https://registry.npmjs.org/@uifabric/icons/-/icons-7.3.54.tgz",
"integrity": "sha512-c4949Bd1zw/AvEA8WFbzb+7Hlp2CYPIgTt02Af0OpWLajF0VDAQK3Sl3paCjbZ1QVyZCzdKwyN3UTtZiZhzlzw==",
"dev": true,
"requires": {
"@uifabric/set-version": "^7.0.13",
"@uifabric/styling": "^7.12.17",
"@uifabric/set-version": "^7.0.14",
"@uifabric/styling": "^7.13.2",
"tslib": "^1.10.0"
},
"dependencies": {
@ -3167,21 +3185,22 @@
}
},
"office-ui-fabric-react": {
"version": "7.120.0",
"resolved": "https://registry.npmjs.org/office-ui-fabric-react/-/office-ui-fabric-react-7.120.0.tgz",
"integrity": "sha512-8/GyokmByHmb1aIQ74337lpYLL70fED9XjQxVpHCxNk52ynicjVQ4Vf12BW3tzv8ACVj4h7iLb3+ohpel1CEAw==",
"version": "7.121.3",
"resolved": "https://registry.npmjs.org/office-ui-fabric-react/-/office-ui-fabric-react-7.121.3.tgz",
"integrity": "sha512-AsiGPfGAIWdUgB8x+sio4QINbVoipxsUlAsG+T7h+DT8kg6rEcaRsjgj19tp2qMLfl/lnafqiNs02ng21l6ivQ==",
"dev": true,
"requires": {
"@fluentui/react-focus": "^7.12.7",
"@fluentui/react-icons": "^0.1.27",
"@fluentui/date-time-utilities": "^7.1.1",
"@fluentui/react-focus": "^7.12.11",
"@fluentui/react-icons": "^0.1.30",
"@microsoft/load-themed-styles": "^1.10.26",
"@uifabric/foundation": "^7.7.24",
"@uifabric/icons": "^7.3.50",
"@uifabric/merge-styles": "^7.14.1",
"@uifabric/react-hooks": "^7.4.5",
"@uifabric/set-version": "^7.0.13",
"@uifabric/styling": "^7.12.17",
"@uifabric/utilities": "^7.20.3",
"@uifabric/foundation": "^7.7.28",
"@uifabric/icons": "^7.3.54",
"@uifabric/merge-styles": "^7.15.0",
"@uifabric/react-hooks": "^7.4.8",
"@uifabric/set-version": "^7.0.14",
"@uifabric/styling": "^7.13.2",
"@uifabric/utilities": "^7.21.2",
"prop-types": "^15.7.2",
"tslib": "^1.10.0"
},
@ -3522,9 +3541,9 @@
"dev": true
},
"ajv-keywords": {
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.1.tgz",
"integrity": "sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ==",
"version": "3.5.0",
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.0.tgz",
"integrity": "sha512-eyoaac3btgU8eJlvh01En8OCKzRqlLe2G5jDsCr3RiE2uLGMEEB1aaGwVVpwR8M95956tGH6R+9edC++OvzaVw==",
"dev": true
},
"align-text": {
@ -4660,15 +4679,15 @@
}
},
"browserslist": {
"version": "4.12.0",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.12.0.tgz",
"integrity": "sha512-UH2GkcEDSI0k/lRkuDSzFl9ZZ87skSy9w2XAn1MsZnL+4c4rqbBd3e82UWHbYDpztABrPBhZsTEeuxVfHppqDg==",
"version": "4.12.1",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.12.1.tgz",
"integrity": "sha512-WMjXwFtPskSW1pQUDJRxvRKRkeCr7usN0O/Za76N+F4oadaTdQHotSGcX9jT/Hs7mSKPkyMFNvqawB/1HzYDKQ==",
"dev": true,
"requires": {
"caniuse-lite": "^1.0.30001043",
"electron-to-chromium": "^1.3.413",
"node-releases": "^1.1.53",
"pkg-up": "^2.0.0"
"caniuse-lite": "^1.0.30001088",
"electron-to-chromium": "^1.3.481",
"escalade": "^3.0.1",
"node-releases": "^1.1.58"
}
},
"bser": {
@ -4866,9 +4885,9 @@
}
},
"caniuse-lite": {
"version": "1.0.30001083",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001083.tgz",
"integrity": "sha512-CnYJ27awX4h7yj5glfK7r1TOI13LBytpLzEgfj0s4mY75/F8pnQcYjL+oVpmS38FB59+vU0gscQ9D8tc+lIXvA==",
"version": "1.0.30001088",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001088.tgz",
"integrity": "sha512-6eYUrlShRYveyqKG58HcyOfPgh3zb2xqs7NvT2VVtP3hEUeeWvc3lqhpeMTxYWBBeeaT9A4bKsrtjATm66BTHg==",
"dev": true
},
"capture-exit": {
@ -6336,15 +6355,15 @@
"dev": true
},
"electron-to-chromium": {
"version": "1.3.473",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.473.tgz",
"integrity": "sha512-smevlzzMNz3vMz6OLeeCq5HRWEj2AcgccNPYnAx4Usx0IOciq9DU36RJcICcS09hXoY7t7deRfVYKD14IrGb9A==",
"version": "1.3.483",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.483.tgz",
"integrity": "sha512-+05RF8S9rk8S0G8eBCqBRBaRq7+UN3lDs2DAvnG8SBSgQO3hjy0+qt4CmRk5eiuGbTcaicgXfPmBi31a+BD3lg==",
"dev": true
},
"elliptic": {
"version": "6.5.2",
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.2.tgz",
"integrity": "sha512-f4x70okzZbIQl/NSRLkI/+tteV/9WqL98zx+SQ69KbXxmVrmjwsNUPn/gYJJ0sHvEak24cZgHIPegRePAtA/xw==",
"version": "6.5.3",
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz",
"integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==",
"dev": true,
"requires": {
"bn.js": "^4.4.0",
@ -6581,6 +6600,12 @@
"es6-symbol": "^3.1.1"
}
},
"escalade": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.0.1.tgz",
"integrity": "sha512-DR6NO3h9niOT+MZs7bjxlj2a1k+POu5RN8CLTPX2+i78bRi9eLe7+0zXgUHMnGXWybYcL61E9hGhPKqedy8tQA==",
"dev": true
},
"escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
@ -6594,9 +6619,9 @@
"dev": true
},
"escodegen": {
"version": "1.14.2",
"resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.2.tgz",
"integrity": "sha512-InuOIiKk8wwuOFg6x9BQXbzjrQhtyXh46K9bqVTPzSo2FnyMBaYGBMC6PhQy7yxxil9vIedFBweQBMK74/7o8A==",
"version": "1.14.3",
"resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz",
"integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==",
"dev": true,
"requires": {
"esprima": "^4.0.1",
@ -10827,9 +10852,9 @@
"dev": true
},
"js-base64": {
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.5.2.tgz",
"integrity": "sha512-Vg8czh0Q7sFBSUMWWArX/miJeBWYBPpdU/3M/DKSaekLMqrqVPaedp+5mZhie/r0lgrcaYBfwXatEew6gwgiQQ==",
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.6.2.tgz",
"integrity": "sha512-1hgLrLIrmCgZG+ID3VoLNLOSwjGnoZa8tyrUdEteMeIzsT6PH7PMLyUvbDwzNE56P3PNxyvuIOx4Uh2E5rzQIw==",
"dev": true
},
"js-tokens": {
@ -12289,9 +12314,9 @@
}
},
"object-inspect": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz",
"integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==",
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz",
"integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==",
"dev": true
},
"object-keys": {
@ -12971,26 +12996,6 @@
}
}
},
"pkg-up": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-2.0.0.tgz",
"integrity": "sha1-yBmscoBZpGHKscOImivjxJoATX8=",
"dev": true,
"requires": {
"find-up": "^2.1.0"
},
"dependencies": {
"find-up": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
"integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=",
"dev": true,
"requires": {
"locate-path": "^2.0.0"
}
}
}
},
"plugin-error": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz",
@ -15596,9 +15601,9 @@
"dev": true
},
"thenify": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.0.tgz",
"integrity": "sha1-5p44obq+lpsBCCB5eLn2K4hgSDk=",
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
"integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==",
"dev": true,
"requires": {
"any-promise": "^1.0.0"
@ -16022,14 +16027,11 @@
"dev": true
},
"uglify-js": {
"version": "3.9.4",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.9.4.tgz",
"integrity": "sha512-8RZBJq5smLOa7KslsNsVcSH+KOXf1uDU8yqLeNuVKwmT0T3FA0ZoXlinQfRad7SDcbZZRZE4ov+2v71EnxNyCA==",
"version": "3.10.0",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.10.0.tgz",
"integrity": "sha512-Esj5HG5WAyrLIdYU74Z3JdG2PxdIusvj6IWHMtlyESxc7kcDz7zYlYjpnSokn1UbpV0d/QX9fan7gkCNd/9BQA==",
"dev": true,
"optional": true,
"requires": {
"commander": "~2.20.3"
}
"optional": true
},
"uglify-to-browserify": {
"version": "1.0.2",
@ -16729,9 +16731,9 @@
}
},
"enhanced-resolve": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.1.tgz",
"integrity": "sha512-98p2zE+rL7/g/DzMHMTF4zZlCgeVdJ7yr6xzEpJRYwFYrGi9ANdn5DnJURg6RpBkyk60XYDnWIv51VfIhfNGuA==",
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.2.0.tgz",
"integrity": "sha512-S7eiFb/erugyd1rLb6mQ3Vuq+EXHv5cpCkNqqIkYkBgN2QdFnyCZzFBleqwGEx4lgNGYij81BWnCrFNK7vxvjQ==",
"dev": true,
"requires": {
"graceful-fs": "^4.1.2",

View File

@ -0,0 +1,25 @@
# EditorConfig helps developers define and maintain consistent
# coding styles between different editors and IDEs
# editorconfig.org
root = true
[*]
# change these settings to your own preference
indent_style = space
indent_size = 2
# we recommend you to keep these unchanged
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
[{package,bower}.json]
indent_style = space
indent_size = 2

32
samples/react-timeline/.gitignore vendored Normal file
View File

@ -0,0 +1,32 @@
# Logs
logs
*.log
npm-debug.log*
# Dependency directories
node_modules
# Build generated files
dist
lib
solution
temp
*.sppkg
# Coverage directory used by tools like istanbul
coverage
# OSX
.DS_Store
# Visual Studio files
.ntvs_analysis.dat
.vs
bin
obj
# Resx Generated Code
*.resx.ts
# Styles Generated Code
*.scss.ts

View File

@ -0,0 +1,12 @@
{
"@microsoft/generator-sharepoint": {
"isCreatingSolution": true,
"environment": "spo",
"version": "1.10.0",
"libraryName": "react-timeline",
"libraryId": "6b0d9bb7-2d49-4b62-9256-10f57306407c",
"packageManager": "npm",
"isDomainIsolated": false,
"componentType": "webpart"
}
}

View File

@ -0,0 +1,123 @@
---
page_type: sample
products:
- office-sp
languages:
- javascript
- typescript
extensions:
contentType: samples
technologies:
- SharePoint Framework
platforms:
- React
createdDate: 07/08/2020 12:00:00 AM
---
# Timeline
## Summary
This sample displays list of events in chronological order. It is typically a graphic design showing a long bar labelled with dates paralleling it, and coexisting events. This web part helps to draw the timeline based from SharePoint list with pre-defined schema.
![Web part preview][figure1]
When added to SharePoint site, the source list containing timeline information, layout, date format, and sort direction can be configured from web part properties.
![SharePoint list schema][figure2]
Users with "Manage Web" permissions will be able to add, edit, and delete events from web part.
![Event CRUD][figure3]
### Layouts
The sample helps to represent the timeline in 2 layouts:
**Vertical layout**
Represents the events vertically one below other.
![Vertical layout][figure4]
**Horizontal layout**
Represents the events horizontally one after other.
![Horizontal layout][figure5]
### SharePoint Asset
A SharePoint list (named "Timeline") is provisioned to store the timeline information, along with supported site columns and content type. The schema of the list is as below.
![List Schema][figure6]
- The "Title" column stores the event title.
- The "Event Date" column represents the date of event.
- The "Picture" column stores the url of image to be displayed for the event.
- The "Link" column represents the url to event.
- The "Description" column represents the more information about the event.
The sample also provisions sample data to the "Timeline" list, which can be used as an example to start using the web part.
![List Sample Data][figure7]
### NPM Packages Used
Below NPM package is used to develop this sample.
1. @pnp/sp (https://www.npmjs.com/package/@pnp/sp)
## Used SharePoint Framework Version
![1.10.0](https://img.shields.io/badge/drop-1.10-green.svg)
## Applies to
* [SharePoint Framework Developer Preview](https://docs.microsoft.com/sharepoint/dev/spfx/sharepoint-framework-overview)
* [Office 365 developer tenant](https://docs.microsoft.com/sharepoint/dev/spfx/set-up-your-developer-tenant)
## Solution
Solution|Author(s)
--------|---------
react-timeline|[Nanddeep Nachan](https://www.linkedin.com/in/nanddeepnachan/) (SharePoint Consultant, [@NanddeepNachan](https://http://twitter.com/NanddeepNachan) )
 |[Ravi Kulkarni](https://www.linkedin.com/in/ravi-kulkarni-a5381723/) (SharePoint Consultant)
## Version history
Version|Date|Comments
-------|----|--------
1.0.0|July 08, 2020|Initial release
## Disclaimer
**THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.**
---
## Prerequisites
- SharePoint Online tenant
- Site Collection created under the **/sites/** or **/**
## Minimal Path to Awesome
- Clone this repo
- `npm i`
- `gulp serve --nobrowser`
- Open workbench on your tenant, i.e. https://contoso.sharepoint.com/sites/salesteam/_layouts/15/workbench.aspx
- Search and add web part "Timeline"
## Features
This sample web part displays list of events in chronological order with data stored in a SharePoint list.
- SharePoint assets provisioning
- Creating extensible services
- Using @pnp/sp
[figure1]: ./assets/webpart-preview.gif
[figure2]: ./assets/wepart-propertypane.png
[figure3]: ./assets/event-crud.gif
[figure4]: ./assets/layout-vertical.png
[figure5]: ./assets/layout-horizontal.png
[figure6]: ./assets/list-schema.png
[figure7]: ./assets/list-sample-data.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 856 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -0,0 +1,18 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/config.2.0.schema.json",
"version": "2.0",
"bundles": {
"timeline-web-part": {
"components": [
{
"entrypoint": "./lib/webparts/timeline/TimelineWebPart.js",
"manifest": "./src/webparts/timeline/TimelineWebPart.manifest.json"
}
]
}
},
"externals": {},
"localizedResources": {
"TimelineWebPartStrings": "lib/webparts/timeline/loc/{locale}.js"
}
}

View File

@ -0,0 +1,4 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/copy-assets.schema.json",
"deployCdnPath": "temp/deploy"
}

View File

@ -0,0 +1,7 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/deploy-azure-storage.schema.json",
"workingDir": "./temp/deploy/",
"account": "<!-- STORAGE ACCOUNT NAME -->",
"container": "react-timeline",
"accessKey": "<!-- ACCESS KEY -->"
}

View File

@ -0,0 +1,29 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/package-solution.schema.json",
"solution": {
"name": "react-timeline-client-side-solution",
"id": "6b0d9bb7-2d49-4b62-9256-10f57306407c",
"version": "1.0.0.0",
"features": [
{
"title": "asset-deployment-react-timeline-client-side-solution",
"description": "asset-deployment-react-timeline-client-side-solution",
"id": "51deb4ec-9fb4-4bf6-b440-69085be0437e",
"version": "1.0.0.0",
"assets": {
"elementManifests": [
"elements.xml"
],
"elementFiles": [
"schema.xml"
]
}
}
],
"includeClientSideAssets": true,
"isDomainIsolated": false
},
"paths": {
"zippedPackage": "solution/react-timeline.sppkg"
}
}

View File

@ -0,0 +1,10 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/core-build/serve.schema.json",
"port": 4321,
"https": true,
"initialPage": "https://localhost:5432/workbench",
"api": {
"port": 5432,
"entryPath": "node_modules/@microsoft/sp-webpart-workbench/lib/api/"
}
}

View File

@ -0,0 +1,4 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/write-manifests.schema.json",
"cdnBasePath": "<!-- PATH TO CDN -->"
}

7
samples/react-timeline/gulpfile.js vendored Normal file
View File

@ -0,0 +1,7 @@
'use strict';
const build = require('@microsoft/sp-build-web');
build.addSuppression(`Warning - [sass] The local CSS class 'ms-Grid' is not camelCase and will not be type-safe.`);
build.initialize(require('gulp'));

17270
samples/react-timeline/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,45 @@
{
"name": "react-timeline",
"version": "0.0.1",
"private": true,
"main": "lib/index.js",
"engines": {
"node": ">=0.10.0"
},
"scripts": {
"build": "gulp bundle",
"clean": "gulp clean",
"test": "gulp test"
},
"dependencies": {
"@microsoft/sp-core-library": "1.10.0",
"@microsoft/sp-lodash-subset": "1.10.0",
"@microsoft/sp-office-ui-fabric-core": "1.10.0",
"@microsoft/sp-property-pane": "1.10.0",
"@microsoft/sp-webpart-base": "1.10.0",
"@pnp/sp": "^2.0.6",
"@types/es6-promise": "0.0.33",
"@types/react": "16.8.8",
"@types/react-dom": "16.8.3",
"@types/webpack-env": "1.13.1",
"@uifabric/react-cards": "^0.109.104",
"moment": "^2.27.0",
"office-ui-fabric-react": "6.189.2",
"react": "16.8.5",
"react-dom": "16.8.5"
},
"resolutions": {
"@types/react": "16.8.8"
},
"devDependencies": {
"@microsoft/sp-build-web": "1.10.0",
"@microsoft/sp-tslint-rules": "1.10.0",
"@microsoft/sp-module-interfaces": "1.10.0",
"@microsoft/sp-webpart-workbench": "1.10.0",
"@microsoft/rush-stack-compiler-3.3": "0.3.5",
"gulp": "~3.9.1",
"@types/chai": "3.4.34",
"@types/mocha": "2.2.38",
"ajv": "~5.2.2"
}
}

View File

@ -0,0 +1,102 @@
<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<!-- SPFxTimelineLink Site Column -->
<Field ID="{D1DD9D2D-6FBB-4419-823C-124524BB8043}"
Name="SPFxTimelineLink"
DisplayName="Link"
StaticName="SPFxTimelineLink"
Group="SPFx Site Columns"
Type="URL"
Format="Hyperlink"
Required="FALSE"
EnforceUniqueValues="FALSE"
Indexed="FALSE"
/>
<!-- SPFxTimelineDate Site Column -->
<Field ID="{CCCFD821-A9D6-4485-ADC5-B5DE16DDC265}"
Name="SPFxTimelineDate"
DisplayName="Event Date"
StaticName="SPFxTimelineDate"
Group="SPFx Site Columns"
Type="DateTime"
Format="DateOnly"
Required="TRUE"
EnforceUniqueValues="FALSE"
Indexed="FALSE"
/>
<!-- SPFxTimelinePicture Site Column -->
<Field ID="{FAF70121-54EC-47DC-B454-50402EB30345}"
Name="SPFxTimelinePicture"
DisplayName="Picture"
StaticName="SPFxTimelinePicture"
Group="SPFx Site Columns"
Type="URL"
Format="Image"
Height="150"
Width="224"
Required="FALSE"
Indexed="FALSE"
/>
<!-- SPFxTimelineDescription Site Column -->
<Field ID="{D1A0FFDC-1DF3-463E-8948-CC2855AC917E}"
Name="SPFxTimelineDescription"
DisplayName="Description"
StaticName="SPFxTimelineDescription"
Description="Description of item"
Group="SPFx Site Columns"
Type="Note"
RichText="FALSE"
NumLines="6"
Required="FALSE"
Indexed="FALSE"
/>
<!-- Parent ContentType: Item (0x0100) -->
<ContentType ID="0x0100945DE9A91BAB454FB867B17618D15F05"
Name="Timeline"
Group="SPFx Content Types"
Description="Content type for Timeline"
Inherits="FALSE"
Version="0"
Hidden="FALSE"
Sealed="FALSE">
<FieldRefs>
<!-- OOB Title Column -->
<FieldRef ID="{fa564e0f-0c70-4ab9-b863-0177e6ddd247}" Required="TRUE" ShowInNewForm="TRUE" ShowInEditForm="TRUE" />
<!-- SPFxTimelineLink Site Column -->
<FieldRef ID="{D1DD9D2D-6FBB-4419-823C-124524BB8043}" Required="FALSE" ShowInNewForm="TRUE" ShowInEditForm="TRUE" />
<!-- SPFxTimelineDate Site Column -->
<FieldRef ID="{CCCFD821-A9D6-4485-ADC5-B5DE16DDC265}" Required="TRUE" ShowInNewForm="TRUE" ShowInEditForm="TRUE" />
<!-- SPFxTimelinePicture Site Column -->
<FieldRef ID="{FAF70121-54EC-47DC-B454-50402EB30345}" Required="FALSE" ShowInNewForm="TRUE" ShowInEditForm="TRUE" />
<!-- SPFxTimelineDescription Site Column -->
<FieldRef ID="{D1A0FFDC-1DF3-463E-8948-CC2855AC917E}" Required="FALSE" ShowInNewForm="TRUE" ShowInEditForm="TRUE" />
</FieldRefs>
</ContentType>
<ListInstance
CustomSchema="schema.xml"
FeatureId="00bfea71-de22-43b2-a848-c05709900100"
Title="Timeline"
Description="List with timeline information"
TemplateType="100"
Url="Lists/Timeline">
<Data>
<Rows>
<Row>
<Field Name="Title">Timeline rolled out</Field>
<Field Name="SPFxTimelineDate">2020-07-15T21:00:00Z</Field>
</Row>
<Row>
<Field Name="Title">Add your first event</Field>
<Field Name="SPFxTimelineDate">2020-07-22T21:00:00Z</Field>
</Row>
</Rows>
</Data>
</ListInstance>
</Elements>

View File

@ -0,0 +1,49 @@
<List xmlns:ows="Microsoft SharePoint" Title="Timeline" EnableContentTypes="TRUE" FolderCreation="FALSE" Direction="$Resources:Direction;" Url="Lists/Timeline" BaseType="0" xmlns="http://schemas.microsoft.com/sharepoint/">
<MetaData>
<ContentTypes>
<ContentType ID="0x0100945DE9A91BAB454FB867B17618D15F05"
Name="Timeline"
Group="SPFx Content Types"
Description="">
<FieldRefs>
<!--Title Site Column -->
<FieldRef ID="{fa564e0f-0c70-4ab9-b863-0177e6ddd247}" Required="TRUE" ShowInNewForm="TRUE" ShowInEditForm="TRUE" />
<!-- SPFxTimelineLink Site Column -->
<FieldRef ID="{D1DD9D2D-6FBB-4419-823C-124524BB8043}" Required="FALSE" ShowInNewForm="TRUE" ShowInEditForm="TRUE" />
<!-- SPFxTimelineDate Site Column -->
<FieldRef ID="{CCCFD821-A9D6-4485-ADC5-B5DE16DDC265}" Required="TRUE" ShowInNewForm="TRUE" ShowInEditForm="TRUE" />
<!-- SPFxTimelinePicture Site Column -->
<FieldRef ID="{FAF70121-54EC-47DC-B454-50402EB30345}" Required="FALSE" ShowInNewForm="TRUE" ShowInEditForm="TRUE" />
<!-- SPFxTimelineDescription Site Column -->
<FieldRef ID="{D1A0FFDC-1DF3-463E-8948-CC2855AC917E}" Required="FALSE" ShowInNewForm="TRUE" ShowInEditForm="TRUE" />
</FieldRefs>
</ContentType>
</ContentTypes>
<Fields></Fields>
<Views>
<View BaseViewID="1" Type="HTML" WebPartZoneID="Main" DisplayName="$Resources:core,objectiv_schema_mwsidcamlidC24;" DefaultView="TRUE" MobileView="TRUE" MobileDefaultView="TRUE" SetupPath="pages\viewpage.aspx" ImageUrl="/_layouts/images/generic.png" Url="AllItems.aspx">
<XslLink Default="TRUE">main.xsl</XslLink>
<JSLink>clienttemplates.js</JSLink>
<RowLimit Paged="TRUE">30</RowLimit>
<Toolbar Type="Standard" />
<ViewFields>
<FieldRef Name="LinkTitle"></FieldRef>
<FieldRef Name="SPFxTimelineLink"></FieldRef>
<FieldRef Name="SPFxTimelineDate"></FieldRef>
<FieldRef Name="SPFxTimelinePicture"></FieldRef>
<FieldRef Name="SPFxTimelineDescription"></FieldRef>
</ViewFields>
<Query>
<OrderBy>
<FieldRef Name="SPFxTimelineDate" />
</OrderBy>
</Query>
</View>
</Views>
<Forms>
<Form Type="DisplayForm" Url="DispForm.aspx" SetupPath="pages\form.aspx" WebPartZoneID="Main" />
<Form Type="EditForm" Url="EditForm.aspx" SetupPath="pages\form.aspx" WebPartZoneID="Main" />
<Form Type="NewForm" Url="NewForm.aspx" SetupPath="pages\form.aspx" WebPartZoneID="Main" />
</Forms>
</MetaData>
</List>

View File

@ -0,0 +1 @@
// A file is required to be in the root of the /src directory by the TypeScript compiler

View File

@ -0,0 +1,9 @@
// Represents attributes of timeline activity
export interface ITimelineActivity {
id: number;
activityTitle: string;
activityLink: string;
acivityDate: Date;
activityPictureUrl: string;
activityDescription: string;
}

View File

@ -0,0 +1 @@
export * from './ITimelineActivity';

View File

@ -0,0 +1,179 @@
import { WebPartContext } from "@microsoft/sp-webpart-base";
import { sp } from "@pnp/sp/presets/all";
import { ITypedHash } from "@pnp/common";
import { ITimelineActivity } from "../models";
export default class TimelineService {
/**
* Constructor
* @param context
*/
constructor(private context: WebPartContext) {
// Setup context to PnPjs
sp.setup({
spfxContext: this.context
});
}
/**
* Get timeline activity by id.
* @param listTitle
* @param id
*/
public async getTimelineActivity(listTitle: string, id: number): Promise<ITimelineActivity> {
let returnTimelineActivity: ITimelineActivity = undefined;
try {
let activity: any = await sp.web.lists.getByTitle(listTitle).items.usingCaching().getById(id)
.select("Id", "Title", "SPFxTimelineLink", "SPFxTimelineDate", "SPFxTimelinePicture", "SPFxTimelineDescription")
.get();
returnTimelineActivity = {
id: activity.ID,
activityTitle: activity.Title,
activityLink: activity.SPFxTimelineLink,
acivityDate: activity.SPFxTimelineDate,
activityPictureUrl: activity.SPFxTimelinePicture,
activityDescription: activity.SPFxTimelineDescription,
};
}
catch (error) {
return Promise.reject(error);
}
return returnTimelineActivity;
}
/**
* Get all timeline activities
* @param listTitle
* @param sortOrder
*/
public async getTimelineActivities(listTitle: string, sortOrder: string): Promise<ITimelineActivity[]> {
let returnTimelineActivities: ITimelineActivity[] = [];
let sortOrderAsc: boolean = (sortOrder === "asc");
try {
let activities: any[] = await sp.web.lists.getByTitle(listTitle).items
.select("Id", "Title", "SPFxTimelineLink", "SPFxTimelineDate", "SPFxTimelinePicture", "SPFxTimelineDescription")
.orderBy("SPFxTimelineDate", sortOrderAsc)
.get();
activities.forEach(activity => {
let timelineActivity = {
id: activity.ID,
activityTitle: activity.Title,
activityLink: activity.SPFxTimelineLink,
acivityDate: activity.SPFxTimelineDate,
activityPictureUrl: activity.SPFxTimelinePicture,
activityDescription: activity.SPFxTimelineDescription,
};
returnTimelineActivities.push(timelineActivity);
});
}
catch (error) {
return Promise.reject(error);
}
return returnTimelineActivities;
}
/**
* Adds timeline activity to SP list.
* @param listTitle
* @param newTimelineActivity
*/
public async addTimelineActivity(listTitle: string, newTimelineActivity: ITimelineActivity) {
try {
let addData: ITypedHash<any> = {
Title: newTimelineActivity.activityTitle,
SPFxTimelineDate: newTimelineActivity.acivityDate,
SPFxTimelineDescription: newTimelineActivity.activityDescription,
SPFxTimelineLink: {},
SPFxTimelinePicture: {}
};
if (newTimelineActivity.activityLink) {
addData.SPFxTimelineLink = {
"__metadata": { type: "SP.FieldUrlValue" },
Description: newTimelineActivity.activityTitle,
Url: newTimelineActivity.activityLink,
};
}
if (newTimelineActivity.activityPictureUrl) {
addData.SPFxTimelinePicture = {
"__metadata": { type: "SP.FieldUrlValue" },
Description: newTimelineActivity.activityTitle,
Url: newTimelineActivity.activityPictureUrl,
};
}
await sp.web.lists.getByTitle(listTitle).items.add(addData);
}
catch (error) {
console.log(error);
return Promise.reject(error);
}
}
/**
* Updates timeline activity to SP list by id.
* @param listTitle
* @param updateTimelineActivity
*/
public async updateTimelineActivity(listTitle: string, updateTimelineActivity: ITimelineActivity) {
try {
let updateItem: ITypedHash<any> = {
Title: updateTimelineActivity.activityTitle,
SPFxTimelineDate: updateTimelineActivity.acivityDate,
SPFxTimelineDescription: updateTimelineActivity.activityDescription,
SPFxTimelineLink: {},
SPFxTimelinePicture: {}
};
if (updateTimelineActivity.activityLink) {
let linkUrl = updateTimelineActivity.activityLink["Url"] ? updateTimelineActivity.activityLink["Url"] : updateTimelineActivity.activityLink;
updateItem.SPFxTimelineLink = {
"__metadata": { type: "SP.FieldUrlValue" },
Description: updateTimelineActivity.activityTitle,
Url: linkUrl,
};
}
if (updateTimelineActivity.activityPictureUrl) {
let picUrl = updateTimelineActivity.activityPictureUrl["Url"] ? updateTimelineActivity.activityPictureUrl["Url"] : updateTimelineActivity.activityPictureUrl;
updateItem.SPFxTimelinePicture = {
"__metadata": { type: "SP.FieldUrlValue" },
Description: updateTimelineActivity.activityTitle,
Url: picUrl,
};
}
await sp.web.lists.getByTitle(listTitle).items.getById(updateTimelineActivity.id).update(updateItem).then((value: any) => {
console.log(value);
});
}
catch (error) {
console.log(error);
return Promise.reject(error);
}
}
/**
* Deletes timeline activity from SP list.
* @param listTitle
* @param deleteTimelineActivity
*/
public async deleteTimelineActivity(listTitle: string, deleteTimelineActivity: ITimelineActivity) {
try {
await sp.web.lists.getByTitle(listTitle).items.getById(deleteTimelineActivity.id).delete();
}
catch (error) {
return Promise.reject(error);
}
}
}

View File

@ -0,0 +1,33 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx/client-side-web-part-manifest.schema.json",
"id": "f6cf705a-2e4b-40d2-9fdd-942883c8f826",
"alias": "TimelineWebPart",
"componentType": "WebPart",
// The "*" signifies that the version should be taken from the package.json
"version": "*",
"manifestVersion": 2,
// If true, the component can only be installed on sites where Custom Script is allowed.
// Components that allow authors to embed arbitrary script code should set this to true.
// https://support.office.com/en-us/article/Turn-scripting-capabilities-on-or-off-1f2c515f-5d7e-448a-9fd7-835da935584f
"requiresCustomScript": false,
"supportedHosts": ["SharePointWebPart"],
"preconfiguredEntries": [{
"groupId": "5c03119e-3074-46fd-976b-c60198311f70", // Other
"group": { "default": "Other" },
"title": { "default": "Timeline" },
"description": { "default": "Displays a list of events in chronological order." },
"officeFabricIconFontName": "TimelineProgress",
"properties": {
"description": "Timeline Events",
"listName": "Timeline",
"layout": "Vertical",
"showImage": true,
"showDescription": true,
"dateFormat": "MM/DD/yyyy",
"sortOrder": "asc"
}
}]
}

View File

@ -0,0 +1,107 @@
import * as React from 'react';
import * as ReactDom from 'react-dom';
import { Version } from '@microsoft/sp-core-library';
import {
IPropertyPaneConfiguration,
PropertyPaneTextField, PropertyPaneDropdown, PropertyPaneToggle
} from '@microsoft/sp-property-pane';
import { BaseClientSideWebPart } from '@microsoft/sp-webpart-base';
import * as strings from 'TimelineWebPartStrings';
import Timeline from './components/Timeline';
import { ITimelineProps } from './components/ITimelineProps';
import TimelineService from '../../services/TimelineService';
import { IDropdownOption } from 'office-ui-fabric-react/lib/components/Dropdown';
export interface ITimelineWebPartProps {
description: string;
listName: string;
layout: string;
showImage: boolean;
showDescription: boolean;
dateFormat : string;
sortOrder: string;
}
export default class TimelineWebPart extends BaseClientSideWebPart <ITimelineWebPartProps> {
private TimelineService: TimelineService = null;
protected onInit(): Promise<void> {
this.TimelineService = new TimelineService(this.context);
return Promise.resolve();
}
public render(): void {
const element: React.ReactElement<ITimelineProps> = React.createElement(
Timeline,
{
context: this.context,
description: this.properties.description,
listName: this.properties.listName,
layout: this.properties.layout,
showImage: this.properties.showImage,
showDescription: this.properties.showDescription,
dateFormat: this.properties.dateFormat,
sortOrder: this.properties.sortOrder
}
);
ReactDom.render(element, this.domElement);
}
protected onDispose(): void {
ReactDom.unmountComponentAtNode(this.domElement);
}
protected get dataVersion(): Version {
return Version.parse('1.0');
}
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
return {
pages: [
{
header: {
description: strings.PropertyPaneDescription
},
groups: [
{
groupName: strings.BasicGroupName,
groupFields: [
PropertyPaneTextField('description', {
label: strings.DescriptionFieldLabel
}),
PropertyPaneTextField('listName', {
label: strings.ListNameFieldLabel
}),
PropertyPaneDropdown('layout', {
label: strings.LayoutFieldLabel,
options: [
{ key: 'Vertical', text: 'Vertical' },
{ key: 'Horizontal', text: 'Horizontal' }
]
}),
PropertyPaneToggle('showImage', {
label: strings.ShowImageFieldLabel,checked:true
}),
PropertyPaneToggle('showDescription', {
label: strings.ShowDescriptionFieldLabel, checked: true
}),
PropertyPaneTextField('dateFormat', {
label: strings.DateFormatFieldLabel
}),
PropertyPaneDropdown('sortOrder', {
label: strings.SortOrderFieldLabel,
options: [
{ key: 'asc', text: 'Ascending' },
{ key: 'desc', text: 'Descending' }
]
})
]
}
]
}
]
};
}
}

View File

@ -0,0 +1,12 @@
import { WebPartContext } from "@microsoft/sp-webpart-base";
export interface ITimelineProps {
context: WebPartContext;
description: string;
listName: string;
layout: string;
showImage: boolean;
showDescription: boolean;
dateFormat: string;
sortOrder: string;
}

View File

@ -0,0 +1,6 @@
import { ITimelineActivity } from "../../../models";
export interface ITimelineState {
timelineActivities: ITimelineActivity[];
isloading: boolean;
}

View File

@ -0,0 +1,5 @@
export enum IPanelModelEnum {
add=1,
edit=2,
delete=3
}

View File

@ -0,0 +1,12 @@
import { ITimelineActivity } from "../../../../models/ITimelineActivity";
import { IPanelModelEnum } from './IPanelModeEnum';
import { WebPartContext } from "@microsoft/sp-webpart-base";
export interface IEventProps {
event: ITimelineActivity;
panelMode: IPanelModelEnum;
onDissmissPanel: (refresh: boolean) => void;
showPanel: boolean;
context: WebPartContext;
listName: string;
}

View File

@ -0,0 +1,21 @@
import { ITimelineActivity } from '../../../../models/ITimelineActivity';
import { DayOfWeek} from 'office-ui-fabric-react/lib/DatePicker';
import { IDropdownOption } from 'office-ui-fabric-react/';
export interface IEventState {
showPanel: boolean;
eventData: ITimelineActivity;
startSelectedHour: IDropdownOption ;
startSelectedMin: IDropdownOption ;
activityTitle: string;
activityLink: string;
acivityDate: Date;
activityPictureUrl: string;
activityDescription: string;
errorMessage?:string;
hasError?:boolean;
disableButton?: boolean;
isSaving?:boolean;
isDeleting?:boolean;
displayDialog:boolean;
isloading:boolean;
}

View File

@ -0,0 +1,379 @@
import * as React from 'react';
import styles from './timelineEvent.module.scss';
import { IEventProps } from './ITimeLineEventProps';
import { IEventState } from './ITimeLineEventState';
import { ITimelineActivity } from '../../../../models/ITimelineActivity';
import {
TextField,
Label,
DatePicker,
IDatePickerStrings,
Dropdown,
IDropdownOption,
DefaultButton,
PrimaryButton,
Dialog,
DialogType,
DialogFooter
} from 'office-ui-fabric-react';
import * as moment from 'moment';
import { mergeStyleSets } from 'office-ui-fabric-react/lib/Styling';
import { IPanelModelEnum } from './IPanelModeEnum';
import TimelineService from "../../../../services/TimelineService";
const DayPickerStrings: IDatePickerStrings = {
months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
shortMonths: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
shortDays: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
goToToday: 'Go to today',
prevMonthAriaLabel: 'Go to previous month',
nextMonthAriaLabel: 'Go to next month',
prevYearAriaLabel: 'Go to previous year',
nextYearAriaLabel: 'Go to next year',
closeButtonAriaLabel: 'Close date picker',
isRequiredErrorMessage: 'Start date is required.',
invalidInputErrorMessage: 'Invalid date format.'
};
const controlClass = mergeStyleSets({
control: {
margin: '0 0 15px 0',
maxWidth: '160px'
},
});
export class TimelineEvent extends React.Component<IEventProps, IEventState> {
private TimelineService: TimelineService = null;
public constructor(props) {
super(props);
this.state = {
showPanel: false,
eventData: this.props.event,
startSelectedHour: { key: '00', text: '00' },
startSelectedMin: { key: '00', text: '00' },
activityTitle: null,
activityLink: null,
acivityDate: new Date(),
activityPictureUrl: null,
activityDescription: null,
hasError: false,
errorMessage: '',
disableButton: false,
isSaving: false,
displayDialog: false,
isloading: false,
};
this.TimelineService = new TimelineService(this.props.context);
this.onStartChangeHour = this.onStartChangeHour.bind(this);
this.onStartChangeMin = this.onStartChangeMin.bind(this);
this.onDescriptionChange = this.onDescriptionChange.bind(this);
this.onSave = this.onSave.bind(this);
this.onSelectDateStart = this.onSelectDateStart.bind(this);
this.onGetErrorMessageTitle = this.onGetErrorMessageTitle.bind(this);
this.hidePanel = this.hidePanel.bind(this);
this.onDelete = this.onDelete.bind(this);
this.closeDialog = this.closeDialog.bind(this);
this._onEventTitleChange = this._onEventTitleChange.bind(this);
this._onActivityPictureURLChange = this._onActivityPictureURLChange.bind(this);
}
private hidePanel() {
this.props.onDissmissPanel(true);
}
private async onSave() {
let eventData: ITimelineActivity = this.state.eventData;
let panelMode = this.props.panelMode;
let startDate: string = null;
startDate = `${moment(this.state.acivityDate).format('YYYY/MM/DD')}`;
const startTime = `${this.state.startSelectedHour.key}:${this.state.startSelectedMin.key}`;
const startDateTime = `${startDate} ${startTime}`;
const start = moment(startDateTime, 'YYYY/MM/DD HH:mm').toLocaleString();
eventData.acivityDate = new Date(start);
eventData.activityDescription = this.state.activityDescription;
eventData.activityLink = this.state.eventData.activityLink;
try {
this.setState({ isSaving: true });
switch (panelMode) {
case IPanelModelEnum.edit:
await this.TimelineService.updateTimelineActivity(
this.props.listName,
eventData
).then((value: any) => { this.props.onDissmissPanel(true); });
break;
case IPanelModelEnum.add:
await this.TimelineService.addTimelineActivity(this.props.listName, eventData).then((value: any) => { this.props.onDissmissPanel(true); });
break;
default:
break;
}
this.setState({ isSaving: false });
}
catch (error) {
this.setState({ hasError: true, errorMessage: error.message, isSaving: false });
}
}
public componentDidCatch(error: any, errorInfo: any) {
this.setState({ hasError: true, errorMessage: errorInfo.message });
}
private async renderEventData(eventId?: number) {
this.setState({ isloading: true });
const event: ITimelineActivity = !eventId ? this.props.event : await this.TimelineService.getTimelineActivity('Timeline', eventId);
if (this.props.panelMode == IPanelModelEnum.edit && event) {
// Get hours of event
const startHour = moment(event.acivityDate).format('HH').toString();
const startMin = moment(event.acivityDate).format("mm").toString();
let timeLineDate: Date = moment(event.acivityDate).toDate();
// Update Component Data
this.setState({
eventData: event,
acivityDate: timeLineDate,
startSelectedHour: { key: startHour, text: startHour },
startSelectedMin: { key: startMin, text: startMin },
activityDescription: event.activityDescription,
activityTitle: event.activityTitle,
activityLink: event.activityLink,
activityPictureUrl: event.activityPictureUrl,
isloading: false
});
}
else {
this.setState({
acivityDate: new Date(),
activityDescription: '',
activityTitle: '',
activityLink: '',
activityPictureUrl: '',
isloading: false,
eventData: { ...event },
});
}
}
public async componentDidMount() {
await this.renderEventData();
}
private onStartChangeHour = (ev: React.FormEvent<HTMLDivElement>, item: IDropdownOption): void => {
this.setState({ startSelectedHour: item });
}
private _onEventTitleChange = (ev: any, newText: string): void => {
this.setState({ eventData: { ...this.state.eventData, activityTitle: newText } });
}
private _onActivityPictureURLChange = (ev: any, newText: string): void => {
this.setState({ eventData: { ...this.state.eventData, activityPictureUrl: newText } });
}
private _onActivityLinkURLChange = (ev: any, newText: string): void => {
this.setState({ eventData: { ...this.state.eventData, activityLink: newText } });
}
private onStartChangeMin = (ev: React.FormEvent<HTMLDivElement>, item: IDropdownOption): void => {
this.setState({ startSelectedMin: item });
}
private onDescriptionChange = (e): void => {
this.setState({ activityDescription: e.target.value });
}
private onGetErrorMessageTitle(value: string): string {
let returnMessage: string = '';
if (value.length === 0) {
returnMessage = "Error";
}
else {
this.setState({ eventData: { ...this.state.eventData, activityTitle: value }, disableButton: false, errorMessage: '' });
}
return returnMessage;
}
private onDelete(ev: React.MouseEvent<HTMLDivElement>) {
ev.preventDefault();
this.setState({ displayDialog: true });
}
private closeDialog = (): void => {
this.setState({ displayDialog: false });
}
private onSelectDateStart(newDate: Date) {
this.setState({ acivityDate: newDate });
}
public render(): React.ReactElement<IEventProps> {
return (
<div>
<Dialog
isOpen={this.props.showPanel}
closeButtonAriaLabel="Close"
dialogContentProps={{
type: DialogType.normal,
title:
this.props.panelMode == 2
? "Edit Timeline Event"
: "Create Timeline Event",
showCloseButton: true,
}}
onDismiss={this.hidePanel}
modalProps={{
className: styles.dialogOverride
}}
>
<div>
<TextField
label="Title"
required
value={
this.state.eventData
? this.state.eventData.activityTitle
: ""
}
deferredValidationTime={500}
onChange={this._onEventTitleChange}
/>
</div>
<Label>
Date
</Label>
<div
style={{
display: "inline-block",
verticalAlign: "top",
paddingRight: 10,
}}
>
<DatePicker
isRequired={true}
className={controlClass.control}
strings={DayPickerStrings}
allowTextInput={true}
value={this.state.acivityDate}
onSelectDate={this.onSelectDateStart}
showMonthPickerAsOverlay={false}
isMonthPickerVisible={false}
showGoToToday={false}
/>
</div>
<React.Fragment>
<div
style={{
display: "inline-block",
verticalAlign: "top",
paddingRight: 10,
}}
>
<Dropdown
selectedKey={this.state.startSelectedHour.key}
onChange={this.onStartChangeHour}
dropdownWidth={75}
options={[
{ key: "00", text: "00" },
{ key: "01", text: "01" },
{ key: "02", text: "02" },
{ key: "03", text: "03" },
{ key: "04", text: "04" },
{ key: "05", text: "05" },
{ key: "06", text: "06" },
{ key: "07", text: "07" },
{ key: "08", text: "08" },
{ key: "09", text: "09" },
{ key: "10", text: "10" },
{ key: "11", text: "11" },
{ key: "12", text: "12" },
{ key: "13", text: "13" },
{ key: "14", text: "14" },
{ key: "15", text: "15" },
{ key: "16", text: "16" },
{ key: "17", text: "17" },
{ key: "18", text: "18" },
{ key: "19", text: "19" },
{ key: "20", text: "20" },
{ key: "21", text: "21" },
{ key: "22", text: "22" },
{ key: "23", text: "23" },
]}
/>
</div>
<div
style={{
display: "inline-block",
verticalAlign: "top",
paddingRight: 10,
}}
>
<Dropdown
selectedKey={this.state.startSelectedMin.key}
onChange={this.onStartChangeMin}
options={[
{ key: "00", text: "00" },
{ key: "05", text: "05" },
{ key: "10", text: "10" },
{ key: "15", text: "15" },
{ key: "20", text: "20" },
{ key: "25", text: "25" },
{ key: "30", text: "30" },
{ key: "35", text: "35" },
{ key: "40", text: "40" },
{ key: "45", text: "45" },
{ key: "50", text: "50" },
{ key: "55", text: "55" },
]}
/>
</div>
</React.Fragment>
<div>
<TextField
label="Description"
value={this.state.activityDescription} onChange={this.onDescriptionChange}
multiline
/>
</div>
<div>
<TextField
label="Picture URL"
value={
this.state.eventData
? this.state.eventData.activityPictureUrl ? this.state.eventData.activityPictureUrl["Url"] : ''
: ""
}
deferredValidationTime={500}
onChange={this._onActivityPictureURLChange}
/>
</div>
<div>
<TextField
label="Link URL"
value={
this.state.eventData
? this.state.eventData.activityLink ? this.state.eventData.activityLink["Url"] : ''
: ""
}
deferredValidationTime={500}
onChange={this._onActivityLinkURLChange}
/>
</div>
<DialogFooter>
<PrimaryButton onClick={this.onSave} text={this.props.panelMode == 2 ? "Update Event" : "Create Event"} />
<DefaultButton onClick={this.hidePanel} text="Cancel" />
</DialogFooter>
</Dialog>
</div>
);
}
}

View File

@ -0,0 +1,55 @@
@import "~@microsoft/sp-office-ui-fabric-core/dist/sass/SPFabricCore.scss";
:global(.ms-Dialog-inner) {
padding-left: 50px !important;
padding-right: 50px !important;
}
:global(.ms-Dialog-title) {
color: white !important;
background-color: $ms-color-themePrimary !important;
font-size: 16px;
font-weight: bold;
padding: 18px !important;
}
:global(i.ms-Button-icon[data-icon-name="Cancel"]) {
width: 25px;
height: 25px;
background-repeat: no-repeat;
background-size: 23px 23px;
color: white !important;
}
.dialogOverride {
@media (max-width: $ms-screen-max-md) {
// need to remove justify center when on a small screen so dialog can use full width
:global(.ms-Dialog) {
justify-content: initial;
}
}
:global(.ms-Dialog-main) {
// override built-in max-width so dialog can grow wider
max-width: 760px !important;
// 100% width on medium and below
@media (max-width: $ms-screen-max-md) {
& {
min-width: 100%;
}
}
// 80% width on medium to xx-large
@media (min-width: $ms-screen-min-md) and (max-width: $ms-screen-max-xl) {
& {
min-width: 80%;
}
}
// 800px width on x-large and above
@media (min-width: $ms-screen-min-xl) {
& {
min-width: 760px;
}
}
}
}

View File

@ -0,0 +1,256 @@
@import "~office-ui-fabric-react/dist/sass/References.scss";
@import "~@microsoft/sp-office-ui-fabric-core/dist/sass/SPFabricCore.scss";
:global(#workbenchPageContent) {
max-width: initial;
}
.timeline {
overflow-x: auto;
.timelineContainerVertical {
display: block;
width: 100%;
max-width: 1040px;
position: relative;
padding: 25px 15px;
margin: 0 auto;
&:before {
content: "";
display: block;
width: 3px;
background: $ms-color-themePrimary;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto;
}
}
.timelineContentVertical {
position: relative;
&:before {
display: block;
content: "";
width: 20px;
height: 20px;
background: $ms-color-themePrimary;
border-radius: 50%;
position: absolute;
left: 0;
right: 0;
top: 40px;
margin: auto;
z-index: 1;
}
}
.timelineRowVertical {
display: flex;
-ms-flex-align: start;
align-items: flex-start;
-webkit-align-items: flex-start;
-ms-flex-pack: start;
justify-content: flex-start;
-webkit-justify-content: flex-start;
padding: 20px 0;
position: relative;
z-index: 2;
width: 100%;
}
.timelineColumnVertical {
width: 50%;
float: left;
&:nth-child(odd) {
padding: 0 15px 0 0;
}
&:nth-child(even) {
padding: 0 0 0 30px;
}
}
.timelineAddVertical {
display: block;
content: " ";
width: 32px;
height: 22px;
left: 0;
right: 0;
z-index: 3;
margin: auto;
}
.timelineContainerHorizontal {
position: relative;
list-style: none;
display: inline-flex;
flex-wrap: nowrap;
margin: 0;
padding: 0;
&:before {
content: "";
position: absolute;
top: calc(50% - -10px);
width: 100%;
height: 2px;
background: $ms-color-themePrimary;
}
}
.timelineContentHorizontal {
align-self: flex-start;
&:before {
content: "";
position: absolute;
top: calc(50% - -10px);
margin-left: 100px;
transform: translate(-50%, -50%);
border: 1px $ms-color-themePrimary solid;
width: 10px;
height: 10px;
border-radius: 50%;
background: $ms-color-themePrimary;
}
}
.timelineRowHorizontal {
display: block;
}
.timelineDateHorizontalTop {
padding-top: 70px !important;
padding-bottom: 0px !important;
width: 100% !important;
.timelineDate {
text-align: center;
margin-top: 0px !important;
}
}
.timelineDateHorizontal {
padding-top: 0px !important;
padding-bottom: 0px !important;
width: 100% !important;
.timelineDate {
text-align: center;
margin-top: 0px !important;
}
}
.timelineColumnHorizontal {
float: left;
position: relative;
&:nth-child(odd) {
padding: 0 0 50px 0;
}
&:nth-child(even) {
padding: 50px 0 0 0;
}
}
.timelineAddHorizontal {
display: block;
content: " ";
width: 32px;
height: 22px;
left: 0;
right: 0;
z-index: 3;
margin: auto;
}
.addToButton {
background: #ffffff;
z-index: 4;
top: 130px !important;
}
.timelineDate {
text-align: right;
margin-top: 20px;
}
.timelineCard {
padding: 5px;
width: 95%;
min-height: 100px !important;
}
.cardSection {
width: 100%;
padding-left: 10px;
}
.contextualMenuSection {
width: 5%;
}
.alignLeft {
text-align: left;
}
.title {
@include ms-font-xl;
}
.subTitle {
@include ms-font-l;
}
.description {
word-break: break-all;
text-overflow: ellipsis;
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
.button {
text-decoration: none;
height: 32px;
// Primary Button
min-width: 80px;
background-color: $ms-color-themePrimary;
border-color: $ms-color-themePrimary;
color: $ms-color-white;
// Basic Button
outline: transparent;
position: relative;
font-family: "Segoe UI WestEuropean", "Segoe UI", -apple-system, BlinkMacSystemFont, Roboto, "Helvetica Neue",
sans-serif;
-webkit-font-smoothing: antialiased;
font-size: $ms-font-size-m;
font-weight: $ms-font-weight-regular;
border-width: 0;
text-align: center;
cursor: pointer;
display: inline-block;
padding: 0 16px;
.label {
font-weight: $ms-font-weight-semibold;
font-size: $ms-font-size-m;
height: 32px;
line-height: 32px;
margin: 0 4px;
vertical-align: top;
display: inline-block;
}
}
}

View File

@ -0,0 +1,79 @@
import * as React from 'react';
import styles from './Timeline.module.scss';
import { ITimelineProps } from './ITimelineProps';
import { ITimelineState } from './ITimelineState';
import { escape } from '@microsoft/sp-lodash-subset';
import TimelineService from '../../../services/TimelineService';
import TimelineActivity from "./TimelineActivity";
import { ITimelineActivity } from "../../../models/ITimelineActivity";
import { SPPermission } from '@microsoft/sp-page-context';
export default class Timeline extends React.Component<ITimelineProps, ITimelineState> {
private TimelineService: TimelineService = null;
private canEdit: any = null;
constructor(props: ITimelineProps) {
super(props);
this.state = {
timelineActivities: [],
isloading: false
};
this.TimelineService = new TimelineService(this.props.context);
this.onDismissPanel = this.onDismissPanel.bind(this);
let permission = new SPPermission(this.props.context.pageContext.web.permissions.value);
this.canEdit = permission.hasPermission(SPPermission.manageWeb);
}
private async onDismissPanel(refresh: boolean) {
if (refresh === true) {
this.TimelineService.getTimelineActivities(this.props.listName, this.props.sortOrder).then((activities: ITimelineActivity[]) => {
this.setState({ timelineActivities: activities });
});
}
}
public render(): React.ReactElement<ITimelineProps> {
return (
<div className={styles.timeline}>
<h1>{this.props.description}</h1>
<div className={this.props.layout == "Vertical" ? `${styles.timelineContainerVertical}` : `${styles.timelineContainerHorizontal}`}>
{
this.state.timelineActivities.map((activity, i) => {
return (
<TimelineActivity activity={activity}
index={i}
context={this.props.context}
onDissmissPanel={this.onDismissPanel}
displayPanel={false}
listName={this.props.listName}
layout={this.props.layout}
showImage={this.props.showImage}
showDescription={this.props.showDescription}
dateFormat={this.props.dateFormat}
canEdit={this.canEdit} >
</TimelineActivity>
);
})}
</div>
</div>
);
}
public componentDidMount(): void {
this.TimelineService.getTimelineActivities(this.props.listName, this.props.sortOrder).then((activities: ITimelineActivity[]) => {
this.setState({ timelineActivities: activities });
});
}
public componentWillReceiveProps(nextProps: ITimelineProps) {
if (this.props.sortOrder !== nextProps.sortOrder) {
this.TimelineService.getTimelineActivities(this.props.listName, nextProps.sortOrder).then((activities: ITimelineActivity[]) => {
this.setState({ timelineActivities: activities });
});
}
}
}

View File

@ -0,0 +1,317 @@
import * as React from 'react';
import styles from './Timeline.module.scss';
import { escape } from '@microsoft/sp-lodash-subset';
import { ITimelineActivity } from "../../../models";
import { Card, ICardTokens, ICardSectionStyles, ICardSectionTokens } from '@uifabric/react-cards';
import { FontWeights } from '@uifabric/styling';
import { IIconStyles, Image, Stack, IStackTokens, Text, ITextStyles, IconButton, IIconProps } from 'office-ui-fabric-react';
import { ContextualMenuItemType } from 'office-ui-fabric-react/lib/ContextualMenu';
import { Dialog, DialogType, DialogFooter } from 'office-ui-fabric-react/lib/Dialog';
import { PrimaryButton, DefaultButton } from 'office-ui-fabric-react/lib/Button';
import { TimelineEvent } from "./Popup/TimelineEvent";
import TimelineService from "../../../services/TimelineService";
import { IPanelModelEnum } from "./Popup/IPanelModeEnum";
import { WebPartContext } from "@microsoft/sp-webpart-base";
import { mergeStyleSets } from 'office-ui-fabric-react/lib/Styling';
import * as moment from 'moment';
export interface IActivityProps {
activity: ITimelineActivity;
index: number;
context: WebPartContext;
onDissmissPanel: (refresh: boolean) => void;
displayPanel: boolean;
listName: string;
layout: string;
showImage: boolean;
showDescription: boolean;
dateFormat: string;
canEdit: boolean;
}
export interface IActivityState {
showEventDialog: boolean;
showDeleteDialog: boolean;
selectedEvent: ITimelineActivity;
panelMode?: IPanelModelEnum;
showImage: boolean;
showDescription: boolean;
layout: string;
dateFormat: string;
}
export default class TimelineActivity extends React.Component<IActivityProps, IActivityState> {
private TimelineService: TimelineService = null;
public constructor(props) {
super(props);
this.state = {
showEventDialog: this.props.displayPanel,
showDeleteDialog: false,
selectedEvent: null,
layout: this.props.layout,
showImage: this.props.showImage,
showDescription: this.props.showDescription,
dateFormat: this.props.dateFormat
};
this.TimelineService = new TimelineService(
this.props.context
);
this.onSelectEvent = this.onSelectEvent.bind(this);
this.onDismissPanel = this.onDismissPanel.bind(this);
this.handleSelectEvent = this.handleSelectEvent.bind(this);
this._dismissCardDetails = this._dismissCardDetails.bind(this);
this.deleteEvent = this.deleteEvent.bind(this);
this.editEvent = this.editEvent.bind(this);
this.createEvent = this.createEvent.bind(this);
}
private async onDismissPanel(refresh: boolean) {
if (refresh === true) {
this.props.onDissmissPanel(true);
}
}
private onSelectEvent(event: any) {
this.setState({ showEventDialog: true, panelMode: 1 });
}
private deleteEvent(TimelineDeleteEvent: ITimelineActivity) {
this.TimelineService.deleteTimelineActivity(
this.props.listName,
TimelineDeleteEvent
).then(() => {
this.setState({
selectedEvent: null,
showDeleteDialog: false
});
this.props.onDissmissPanel(true);
});
}
private editEvent() {
this.setState({
showEventDialog: true,
panelMode: 2
});
}
private createEvent() {
this.setState({
showEventDialog: true,
panelMode: 1
});
}
public componentWillReceiveProps(nextProps: IActivityProps) {
this.setState({
showEventDialog: false,
selectedEvent: null,
layout: nextProps.layout,
showImage: nextProps.showImage,
showDescription: nextProps.showDescription,
dateFormat: nextProps.dateFormat
});
}
private _dismissCardDetails() {
this.setState({ selectedEvent: null });
}
private handleSelectEvent(event: ITimelineActivity) {
this.setState({
selectedEvent: event
});
}
public render(): React.ReactElement<IActivityProps> {
const siteTextStyles: ITextStyles = {
root: {
color: "#025F52",
fontWeight: FontWeights.semibold,
},
};
const descriptionTextStyles: ITextStyles = {
root: {
color: "#333333",
fontWeight: FontWeights.regular,
},
};
const helpfulTextStyles: ITextStyles = {
root: {
color: "#333333",
fontWeight: FontWeights.regular,
},
};
const iconStyles: IIconStyles = {
root: {
fontSize: 16,
fontWeight: FontWeights.regular,
},
};
const footerCardSectionStyles: ICardSectionStyles = {
root: {
alignSelf: "stretch",
borderLeft: "1px solid #F3F2F1",
},
};
const sectionStackTokens: IStackTokens = { childrenGap: 20 };
const cardTokens: ICardTokens = { childrenMargin: 12 };
const footerCardSectionTokens: ICardSectionTokens = { padding: "0px 0px 0px 12px" };
const { activity, index, canEdit, showImage, showDescription } = this.props;
const addToIcon: IIconProps = { iconName: 'AddTo' };
let activityDate: string = moment(activity.acivityDate).format(this.state.dateFormat);
return (
<div>
{
this.props.canEdit &&
<div className={this.state.layout == "Vertical" ? `${styles.timelineAddVertical}` : `${styles.timelineAddHorizontal}`}>
<IconButton iconProps={addToIcon} title="Add Timeline Event" ariaLabel="Add Activity" className={styles.addToButton} onClick={this.createEvent} />
</div>
}
<Dialog type={DialogType.normal}
hidden={!this.state.showDeleteDialog}
title='Delete event?'
subText='Do you want to delete this event?'
isBlocking={true}
containerClassName={'ms-dialogMainOverride'}>
<DialogFooter>
<PrimaryButton onClick={() => { this.deleteEvent(this.state.selectedEvent); }} text="Yes" />
<DefaultButton onClick={() => { this.setState({ selectedEvent: null, showDeleteDialog: false }); }} text="No" />
</DialogFooter>
</Dialog>
<div className={this.state.layout == "Vertical" ? `${styles.timelineContentVertical}` : `${styles.timelineContentHorizontal}`}>
<div className={this.state.layout == "Vertical" ? `${styles.timelineRowVertical}` : `${styles.timelineRowHorizontal}`}>
{index % 2 == 1 &&
<div className={this.state.layout == "Vertical" ? `${styles.timelineColumnVertical}` : `${styles.timelineColumnHorizontal} ${styles.timelineDateHorizontalTop}`}>
<div className={styles.timelineDate}>
<Text styles={helpfulTextStyles}>
{activityDate}
</Text>
</div>
</div>
}
<div className={this.state.layout == "Vertical" ? `${styles.timelineColumnVertical}` : `${styles.timelineColumnHorizontal}`}>
<Stack tokens={sectionStackTokens}>
{this.state.showEventDialog && (
<TimelineEvent
event={this.state.selectedEvent}
panelMode={this.state.panelMode}
onDissmissPanel={this.onDismissPanel}
showPanel={this.state.showEventDialog}
context={this.props.context}
listName={this.props.listName}
/>
)}
<div className={styles.timelineCard}>
<Card
aria-label="Clickable horizontal card "
horizontal
tokens={cardTokens}
>
{
showImage && activity.activityPictureUrl &&
<Card.Item fill>
<Image
src={activity.activityPictureUrl ? activity.activityPictureUrl["Url"] : ''}
alt="Placeholder image."
width="100px"
height="100px"
/>
</Card.Item>
}
<Card.Section className={styles.cardSection}>
<Text variant="small" styles={siteTextStyles}>
{activity.activityLink ? (
<a href={activity.activityLink ? activity.activityLink["Url"] : this.props.context.pageContext.site.absoluteUrl} target="_blank">
{activity.activityTitle}
</a>
) : (
activity.activityTitle
)}
</Text>
{
showDescription &&
<Text styles={descriptionTextStyles} className={styles.description}>
{activity.activityDescription}
</Text>
}
</Card.Section>
<Card.Section
styles={footerCardSectionStyles}
tokens={footerCardSectionTokens}
className={styles.contextualMenuSection}
>
{canEdit &&
<IconButton
id="ContextualMenuButton1"
text=""
split={false}
iconProps={{ iconName: "MoreVertical" }}
style={{ float: "right", width: "10%" }}
menuIconProps={{ iconName: "" }}
menuProps={{
shouldFocusOnMount: true,
items: [
{
key: "Edit",
name: "Edit",
onClick: (event) => {
this.setState({ selectedEvent: activity });
this.editEvent();
},
},
{
key: "divider_1",
itemType: ContextualMenuItemType.Divider,
},
{
key: "Delete",
name: "Delete",
onClick: (event) => {
this.setState({
selectedEvent: activity,
showDeleteDialog: true
});
},
},
],
}}
/>
}
</Card.Section>
</Card>
</div>
</Stack>
</div>
{index % 2 == 0 &&
<div className={this.state.layout == "Vertical" ? `${styles.timelineColumnVertical}` : `${styles.timelineColumnHorizontal} ${styles.timelineDateHorizontal}`}>
<div className={this.state.layout == "Vertical" ? `${styles.timelineDate} ${styles.alignLeft}` : `${styles.timelineDate}`}>
<Text styles={helpfulTextStyles}>
{activityDate}
</Text>
</div>
</div>
}
</div>
</div>
</div>
);
}
}

View File

@ -0,0 +1,13 @@
define([], function() {
return {
"PropertyPaneDescription": "Description",
"BasicGroupName": "Group Name",
"DescriptionFieldLabel": "WebPart Title",
"ListNameFieldLabel":"List Name",
"LayoutFieldLabel":"Layout Type",
"ShowImageFieldLabel":"Show Image",
"ShowDescriptionFieldLabel": "Show Description",
"DateFormatFieldLabel": "Date Format",
"SortOrderFieldLabel": "Sort Direction"
}
});

View File

@ -0,0 +1,16 @@
declare interface ITimelineWebPartStrings {
PropertyPaneDescription: string;
BasicGroupName: string;
DescriptionFieldLabel: string;
ListNameFieldLabel: string;
LayoutFieldLabel: string;
ShowImageFieldLabel: string;
ShowDescriptionFieldLabel: string;
DateFormatFieldLabel: string;
SortOrderFieldLabel: string;
}
declare module 'TimelineWebPartStrings' {
const strings: ITimelineWebPartStrings;
export = strings;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -0,0 +1,38 @@
{
"extends": "./node_modules/@microsoft/rush-stack-compiler-3.3/includes/tsconfig-web.json",
"compilerOptions": {
"target": "es5",
"forceConsistentCasingInFileNames": true,
"module": "esnext",
"moduleResolution": "node",
"jsx": "react",
"declaration": true,
"sourceMap": true,
"experimentalDecorators": true,
"skipLibCheck": true,
"outDir": "lib",
"inlineSources": false,
"strictNullChecks": false,
"noUnusedLocals": false,
"typeRoots": [
"./node_modules/@types",
"./node_modules/@microsoft"
],
"types": [
"es6-promise",
"webpack-env"
],
"lib": [
"es5",
"dom",
"es2015.collection"
]
},
"include": [
"src/**/*.ts"
],
"exclude": [
"node_modules",
"lib"
]
}

View File

@ -0,0 +1,30 @@
{
"extends": "@microsoft/sp-tslint-rules/base-tslint.json",
"rules": {
"class-name": false,
"export-name": false,
"forin": false,
"label-position": false,
"member-access": true,
"no-arg": false,
"no-console": false,
"no-construct": false,
"no-duplicate-variable": true,
"no-eval": false,
"no-function-expression": true,
"no-internal-module": true,
"no-shadowed-variable": true,
"no-switch-case-fall-through": true,
"no-unnecessary-semicolons": true,
"no-unused-expression": true,
"no-use-before-declare": true,
"no-with-statement": true,
"semicolon": true,
"trailing-comma": false,
"typedef": false,
"typedef-whitespace": false,
"use-named-parameter": true,
"variable-name": false,
"whitespace": false
}
}