Added sample "react-timeline"
|
@ -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",
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -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
|
After Width: | Height: | Size: 856 KiB |
After Width: | Height: | Size: 98 KiB |
After Width: | Height: | Size: 178 KiB |
After Width: | Height: | Size: 8.3 KiB |
After Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 1.3 MiB |
After Width: | Height: | Size: 13 KiB |
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/copy-assets.schema.json",
|
||||
"deployCdnPath": "temp/deploy"
|
||||
}
|
|
@ -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 -->"
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -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/"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/write-manifests.schema.json",
|
||||
"cdnBasePath": "<!-- PATH TO CDN -->"
|
||||
}
|
|
@ -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'));
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -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>
|
|
@ -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>
|
|
@ -0,0 +1 @@
|
|||
// A file is required to be in the root of the /src directory by the TypeScript compiler
|
|
@ -0,0 +1,9 @@
|
|||
// Represents attributes of timeline activity
|
||||
export interface ITimelineActivity {
|
||||
id: number;
|
||||
activityTitle: string;
|
||||
activityLink: string;
|
||||
acivityDate: Date;
|
||||
activityPictureUrl: string;
|
||||
activityDescription: string;
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
export * from './ITimelineActivity';
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}]
|
||||
}
|
|
@ -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' }
|
||||
]
|
||||
})
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
import { ITimelineActivity } from "../../../models";
|
||||
|
||||
export interface ITimelineState {
|
||||
timelineActivities: ITimelineActivity[];
|
||||
isloading: boolean;
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
export enum IPanelModelEnum {
|
||||
add=1,
|
||||
edit=2,
|
||||
delete=3
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 });
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
});
|
|
@ -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;
|
||||
}
|
After Width: | Height: | Size: 3.0 KiB |
After Width: | Height: | Size: 1.4 KiB |
|
@ -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"
|
||||
]
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|