Merge pull request #1297 from nbarkhina/master
New Sample - React Word Game
This commit is contained in:
commit
fdddea1979
|
@ -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,11 @@
|
||||||
|
{
|
||||||
|
"@microsoft/generator-sharepoint": {
|
||||||
|
"isCreatingSolution": true,
|
||||||
|
"environment": "onprem19",
|
||||||
|
"version": "1.10.0",
|
||||||
|
"libraryName": "word-game",
|
||||||
|
"libraryId": "751649b4-2c99-4349-9d65-9a6cc7533dfa",
|
||||||
|
"packageManager": "npm",
|
||||||
|
"componentType": "webpart"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,75 @@
|
||||||
|
# Word Game
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
A fun game where you unscramble the words before the time runs out. It stores everyone's high scores in a SharePoint List on the Site.
|
||||||
|
|
||||||
|
![Word Game Preview](./assets/preview.gif)
|
||||||
|
|
||||||
|
|
||||||
|
## Used SharePoint Framework Version
|
||||||
|
|
||||||
|
![1.4.0](https://img.shields.io/badge/version-1.4.0-green.svg)
|
||||||
|
|
||||||
|
## Applies to
|
||||||
|
|
||||||
|
* [SharePoint Framework](https:/dev.office.com/sharepoint)
|
||||||
|
* [Office 365 tenant](https://dev.office.com/sharepoint/docs/spfx/set-up-your-development-environment)
|
||||||
|
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- SharePoint Online Tenant or SharePoint 2019 On Prem
|
||||||
|
|
||||||
|
## Solution
|
||||||
|
|
||||||
|
Solution|Author(s)
|
||||||
|
--------|---------
|
||||||
|
react-word-game | Neil Barkhina ([www.neilb.net](https://www.neilb.net/))
|
||||||
|
|
||||||
|
## Version history
|
||||||
|
|
||||||
|
Version|Date|Comments
|
||||||
|
-------|----|--------
|
||||||
|
1.0|May 27, 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.**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Minimal Path to Awesome
|
||||||
|
|
||||||
|
* Clone this repository
|
||||||
|
* In the command line run:
|
||||||
|
* `npm install`
|
||||||
|
* `gulp serve`
|
||||||
|
* If you haven't trusted your dev cert yet run
|
||||||
|
* `gulp trust-dev-cert`
|
||||||
|
* To Deploy it in your SharePoint Environment
|
||||||
|
* `gulp --ship`
|
||||||
|
* `gulp package-solution --ship`
|
||||||
|
* Upload the .sppkg file from sharepoint\solution folder into your App Catalog
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
This demonstrates how you can create a fun game to play with your SharePoint peers. It was built using React and also uses jQuery to help load the Word List. It demonstrates these features:
|
||||||
|
|
||||||
|
- SharePoint REST API's
|
||||||
|
- Creating List and List Items API
|
||||||
|
- Creating columns in a SharePoint List
|
||||||
|
- Web Part Property Pane Settings
|
||||||
|
- CSS Styling and Animations
|
||||||
|
- Fun and engaging UX
|
||||||
|
- Responsive Design
|
||||||
|
|
||||||
|
|
||||||
|
If you want to disable the high scores feature you can edit the Web Part Settings. The Web Part stores the high scores in a SharePoint List that it creates called WordGameList. You can also change the Game Title in the settings:
|
||||||
|
|
||||||
|
![Settings](./assets/settings.PNG)
|
||||||
|
|
||||||
|
It also uses responsive design which works great on the SharePoint Mobile App:
|
||||||
|
|
||||||
|
![Mobile](./assets/wordgame_mobile.png)
|
||||||
|
|
||||||
|
<img src="https://telemetry.sharepointpnp.com/sp-dev-fx-webparts/samples/react-word-game" />
|
Binary file not shown.
After Width: | Height: | Size: 968 KiB |
Binary file not shown.
After Width: | Height: | Size: 36 KiB |
Binary file not shown.
After Width: | Height: | Size: 36 KiB |
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/config.2.0.schema.json",
|
||||||
|
"version": "2.0",
|
||||||
|
"bundles": {
|
||||||
|
"word-game-web-part": {
|
||||||
|
"components": [
|
||||||
|
{
|
||||||
|
"entrypoint": "./lib/webparts/wordGame/WordGameWebPart.js",
|
||||||
|
"manifest": "./src/webparts/wordGame/WordGameWebPart.manifest.json"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"externals": {},
|
||||||
|
"localizedResources": {
|
||||||
|
"WordGameWebPartStrings": "lib/webparts/wordGame/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": "word-game",
|
||||||
|
"accessKey": "<!-- ACCESS KEY -->"
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/package-solution.schema.json",
|
||||||
|
"solution": {
|
||||||
|
"name": "word-game-client-side-solution",
|
||||||
|
"id": "751649b4-2c99-4349-9d65-9a6cc7533dfa",
|
||||||
|
"version": "1.0.0.3",
|
||||||
|
"includeClientSideAssets": true,
|
||||||
|
"skipFeatureDeployment": true
|
||||||
|
},
|
||||||
|
"paths": {
|
||||||
|
"zippedPackage": "solution/word-game.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,38 @@
|
||||||
|
{
|
||||||
|
"name": "react-word-game",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"private": true,
|
||||||
|
"main": "lib/index.js",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "gulp bundle",
|
||||||
|
"clean": "gulp clean",
|
||||||
|
"test": "gulp test"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"react": "15.6.2",
|
||||||
|
"react-dom": "15.6.2",
|
||||||
|
"@types/react": "15.6.6",
|
||||||
|
"@types/react-dom": "15.5.6",
|
||||||
|
"@microsoft/sp-core-library": "~1.4.0",
|
||||||
|
"@microsoft/sp-webpart-base": "~1.4.0",
|
||||||
|
"@microsoft/sp-lodash-subset": "~1.4.0",
|
||||||
|
"@microsoft/sp-office-ui-fabric-core": "~1.4.0",
|
||||||
|
"@types/webpack-env": "1.13.1",
|
||||||
|
"@types/es6-promise": "0.0.33"
|
||||||
|
},
|
||||||
|
"resolutions": {
|
||||||
|
"@types/react": "15.6.6"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@microsoft/sp-build-web": "~1.4.1",
|
||||||
|
"@microsoft/sp-module-interfaces": "~1.4.1",
|
||||||
|
"@microsoft/sp-webpart-workbench": "~1.4.1",
|
||||||
|
"gulp": "~3.9.1",
|
||||||
|
"@types/chai": "3.4.34",
|
||||||
|
"@types/mocha": "2.2.38",
|
||||||
|
"ajv": "~5.2.2"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
// A file is required to be in the root of the /src directory by the TypeScript compiler
|
|
@ -0,0 +1,26 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://developer.microsoft.com/json-schemas/spfx/client-side-web-part-manifest.schema.json",
|
||||||
|
"id": "e33f043c-3d0d-4873-a5aa-3dd37f70d3bd",
|
||||||
|
"alias": "WordGameWebPart",
|
||||||
|
"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,
|
||||||
|
|
||||||
|
"preconfiguredEntries": [{
|
||||||
|
"groupId": "5c03119e-3074-46fd-976b-c60198311f70", // Other
|
||||||
|
"group": { "default": "Other" },
|
||||||
|
"title": { "default": "WordGame" },
|
||||||
|
"description": { "default": "A fun game where you unscramble the words before the time runs out" },
|
||||||
|
"officeFabricIconFontName": "TextField",
|
||||||
|
"properties": {
|
||||||
|
"description": "WordGame"
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
|
@ -0,0 +1,85 @@
|
||||||
|
import * as React from 'react';
|
||||||
|
import * as ReactDom from 'react-dom';
|
||||||
|
import { Version } from '@microsoft/sp-core-library';
|
||||||
|
import {
|
||||||
|
BaseClientSideWebPart,
|
||||||
|
IPropertyPaneConfiguration,
|
||||||
|
PropertyPaneTextField,
|
||||||
|
PropertyPaneCheckbox
|
||||||
|
} from '@microsoft/sp-webpart-base';
|
||||||
|
|
||||||
|
import * as strings from 'WordGameWebPartStrings';
|
||||||
|
import WordGame from './components/WordGame';
|
||||||
|
import { IWordGameProps } from './components/IWordGameProps';
|
||||||
|
import { WordService } from './components/WordService';
|
||||||
|
|
||||||
|
export interface IWordGameWebPartProps {
|
||||||
|
gameTitle: string;
|
||||||
|
enableHighScores: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class WordGameWebPart extends BaseClientSideWebPart<IWordGameWebPartProps> {
|
||||||
|
|
||||||
|
wordGame: WordGame;
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public render(): void {
|
||||||
|
if (!this.properties.gameTitle)
|
||||||
|
this.properties.gameTitle='Word Game';
|
||||||
|
if (this.properties.enableHighScores==null)
|
||||||
|
this.properties.enableHighScores=true;
|
||||||
|
|
||||||
|
const element: React.ReactElement<IWordGameProps> = React.createElement(
|
||||||
|
WordGame,
|
||||||
|
{
|
||||||
|
description: this.properties.gameTitle,
|
||||||
|
enableHighScores: this.properties.enableHighScores,
|
||||||
|
context: this.context
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
ReactDom.render(element, this.domElement);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
protected onDispose(): void {
|
||||||
|
ReactDom.unmountComponentAtNode(this.domElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected get dataVersion(): Version {
|
||||||
|
return Version.parse('1.0');
|
||||||
|
}
|
||||||
|
|
||||||
|
//only refresh render after applying settings pane
|
||||||
|
protected get disableReactivePropertyChanges(): boolean {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
|
||||||
|
return {
|
||||||
|
pages: [
|
||||||
|
{
|
||||||
|
header: {
|
||||||
|
description: 'Configure Word Game Settings'
|
||||||
|
},
|
||||||
|
groups: [
|
||||||
|
{
|
||||||
|
groupName: 'Main',
|
||||||
|
groupFields: [
|
||||||
|
PropertyPaneCheckbox('enableHighScores', {
|
||||||
|
text: 'Enable High Scores'
|
||||||
|
}),
|
||||||
|
PropertyPaneTextField('gameTitle', {
|
||||||
|
label: 'Game Title'
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
Binary file not shown.
After Width: | Height: | Size: 673 B |
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,23 @@
|
||||||
|
import { WordGameListItem } from "./WordService";
|
||||||
|
import { GameState } from "./WordGame";
|
||||||
|
import { WebPartContext } from "@microsoft/sp-webpart-base";
|
||||||
|
|
||||||
|
export interface IWordGameProps {
|
||||||
|
description: string;
|
||||||
|
enableHighScores: boolean;
|
||||||
|
context: WebPartContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IWordGameState {
|
||||||
|
gamestate: GameState;
|
||||||
|
currentWord: string;
|
||||||
|
possibleAnswersText: string;
|
||||||
|
txtAnswer: string;
|
||||||
|
currentRound: number;
|
||||||
|
score: number;
|
||||||
|
lblTimer: string;
|
||||||
|
lblMessage: string;
|
||||||
|
highScores: WordGameListItem[];
|
||||||
|
mobileMode: boolean;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,186 @@
|
||||||
|
@import '~@microsoft/sp-office-ui-fabric-core/dist/sass/SPFabricCore.scss';
|
||||||
|
|
||||||
|
// WORDGAME STYLES
|
||||||
|
|
||||||
|
:global .wordGameCustom{
|
||||||
|
|
||||||
|
input[type=text], select {
|
||||||
|
width: 100%;
|
||||||
|
padding: 12px 20px;
|
||||||
|
margin: 8px 0;
|
||||||
|
display: inline-block;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 4px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
button,
|
||||||
|
input[type=submit] {
|
||||||
|
width: 100%;
|
||||||
|
background-color: #4CAF50;
|
||||||
|
color: white;
|
||||||
|
padding: 14px 20px;
|
||||||
|
margin: 8px 0;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=submit]:hover {
|
||||||
|
background-color: #45a049;
|
||||||
|
}
|
||||||
|
|
||||||
|
div {
|
||||||
|
border-radius: 5px;
|
||||||
|
background-color: white;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.greyButton{
|
||||||
|
background-color: grey;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blueButton{
|
||||||
|
width: unset;
|
||||||
|
background-color: #007bff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.currentWord{
|
||||||
|
color: #007bff;
|
||||||
|
font-size: 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timer{
|
||||||
|
font-size: 16pt;
|
||||||
|
color: gray;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gameReview{
|
||||||
|
text-align: left;
|
||||||
|
width: 250px;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
color: #155724;
|
||||||
|
background-color: #d4edda;
|
||||||
|
border-color: #c3e6cb;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
ul{
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast{
|
||||||
|
position: absolute;
|
||||||
|
top: 5px;
|
||||||
|
right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toastMiniGreen{
|
||||||
|
color: rgb(103, 181, 89);
|
||||||
|
}
|
||||||
|
|
||||||
|
.toastMiniRed{
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toastGreen{
|
||||||
|
color: #155724;
|
||||||
|
background-color: #d4edda;
|
||||||
|
border-color: #c3e6cb;
|
||||||
|
padding: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toastRed{
|
||||||
|
color: #721c24;
|
||||||
|
background-color: #f8d7da;
|
||||||
|
border-color: #f5c6cb;
|
||||||
|
padding: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.numAnswersTip{
|
||||||
|
padding-top: 3px;
|
||||||
|
padding-bottom: 3px;
|
||||||
|
color: gray;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// OFFICE STYLES
|
||||||
|
|
||||||
|
.wordGame {
|
||||||
|
|
||||||
|
|
||||||
|
.container {
|
||||||
|
max-width: 700px;
|
||||||
|
margin: 0px auto;
|
||||||
|
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.row {
|
||||||
|
@include ms-Grid-row;
|
||||||
|
@include ms-fontColor-white;
|
||||||
|
background-color: $ms-color-themeDark;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.column {
|
||||||
|
@include ms-Grid-col;
|
||||||
|
@include ms-lg10;
|
||||||
|
@include ms-xl8;
|
||||||
|
@include ms-xlPush2;
|
||||||
|
@include ms-lgPush1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
@include ms-font-xl;
|
||||||
|
@include ms-fontColor-white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subTitle {
|
||||||
|
@include ms-font-l;
|
||||||
|
@include ms-fontColor-white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description {
|
||||||
|
@include ms-font-l;
|
||||||
|
@include ms-fontColor-white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button {
|
||||||
|
// Our 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,350 @@
|
||||||
|
import * as React from 'react';
|
||||||
|
import styles from './WordGame.module.scss';
|
||||||
|
import { IWordGameProps, IWordGameState } from './IWordGameProps';
|
||||||
|
import { escape, round } from '@microsoft/sp-lodash-subset';
|
||||||
|
import { WordService, Game } from './WordService';
|
||||||
|
import { Ref } from 'react';
|
||||||
|
import { WordGameListItem } from './WordService';
|
||||||
|
import WordHighScores from './WordHighScores';
|
||||||
|
const logo: any = require('../assets/ajax-loader.gif');
|
||||||
|
require('./anims.css');
|
||||||
|
|
||||||
|
export enum GameState {
|
||||||
|
Title,
|
||||||
|
GameLoading,
|
||||||
|
GamePlaying,
|
||||||
|
GameFinished
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class WordGame extends React.Component<IWordGameProps, IWordGameState> {
|
||||||
|
|
||||||
|
wordService: WordService = new WordService();
|
||||||
|
currentGame: Game;
|
||||||
|
timerInterval: number = -1;
|
||||||
|
numTimer: number;
|
||||||
|
|
||||||
|
state: IWordGameState =
|
||||||
|
{
|
||||||
|
gamestate: GameState.GameLoading,
|
||||||
|
currentWord: '',
|
||||||
|
possibleAnswersText: '',
|
||||||
|
txtAnswer: '',
|
||||||
|
currentRound: 0,
|
||||||
|
score: 0,
|
||||||
|
lblTimer: '',
|
||||||
|
lblMessage: '',
|
||||||
|
highScores: [],
|
||||||
|
mobileMode: false
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
console.log('wordgame constructor finished');
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async componentDidMount() {
|
||||||
|
this.wordService.SetContext(this.props.context);
|
||||||
|
await this.wordService.loadWords();
|
||||||
|
|
||||||
|
if (window.innerWidth<600)
|
||||||
|
this.setState({mobileMode:true})
|
||||||
|
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
gamestate: GameState.Title,
|
||||||
|
} as IWordGameState);
|
||||||
|
|
||||||
|
this.getHighScores();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public async redraw() {
|
||||||
|
this.forceUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
answerChanged(event) {
|
||||||
|
this.setState({ txtAnswer: event.target.value } as IWordGameState);
|
||||||
|
}
|
||||||
|
|
||||||
|
messageCounter = 0;
|
||||||
|
|
||||||
|
showMessage(message: string) {
|
||||||
|
this.setState({ lblMessage: message } as IWordGameState);
|
||||||
|
this.messageCounter = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
async btnAnswer() {
|
||||||
|
let answerFound: boolean = false;
|
||||||
|
|
||||||
|
this.currentGame.rounds[this.state.currentRound].answers.forEach(answer => {
|
||||||
|
if (answer.toLowerCase() == this.state.txtAnswer.toLowerCase()) {
|
||||||
|
this.currentGame.rounds[this.state.currentRound].correctAnswer = this.state.txtAnswer;
|
||||||
|
this.numTimer += 5;
|
||||||
|
this.showMessage('Correct!! +5 Seconds');
|
||||||
|
this.setState({
|
||||||
|
lblTimer: this.numTimer + " seconds",
|
||||||
|
score: this.state.score + 1
|
||||||
|
} as IWordGameState);
|
||||||
|
answerFound = true;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!answerFound) {
|
||||||
|
this.showMessage('Incorrect');
|
||||||
|
this.currentGame.rounds[this.state.currentRound].incorrectAnswers.push(this.state.txtAnswer);
|
||||||
|
this.setState({ txtAnswer: '' } as IWordGameState);
|
||||||
|
this.focusOnTextbox();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.nextRound();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
btnSkip() {
|
||||||
|
this.nextRound();
|
||||||
|
this.focusOnTextbox();
|
||||||
|
}
|
||||||
|
|
||||||
|
async nextRound() {
|
||||||
|
|
||||||
|
//state doesn't modify immediately
|
||||||
|
await this.setState({
|
||||||
|
txtAnswer: '',
|
||||||
|
currentRound: this.state.currentRound + 1,
|
||||||
|
} as IWordGameState);
|
||||||
|
|
||||||
|
if (this.state.currentRound >= this.currentGame.rounds.length) {
|
||||||
|
this.finishGame();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.setState({
|
||||||
|
currentWord: this.currentGame.rounds[this.state.currentRound].word,
|
||||||
|
possibleAnswersText: this.getPossibleAnswersText(this.state.currentRound)
|
||||||
|
} as IWordGameState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getPossibleAnswersText(currentRound:number):string{
|
||||||
|
let possibleAnswers = this.currentGame.rounds[currentRound].answers.length;
|
||||||
|
let possibleAnswersText = possibleAnswers + ' Possible Answer';
|
||||||
|
if (possibleAnswers>1)
|
||||||
|
possibleAnswersText = possibleAnswers + ' Possible Answers';
|
||||||
|
return possibleAnswersText
|
||||||
|
}
|
||||||
|
|
||||||
|
focusOnTextbox() {
|
||||||
|
let element = document.getElementById('txtWordGameAnswer');
|
||||||
|
if (element)
|
||||||
|
element.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
finishGame() {
|
||||||
|
this.sendScore(this.state.score, this.numTimer, JSON.stringify(this.currentGame));
|
||||||
|
this.stopTimer();
|
||||||
|
this.setState({ gamestate: GameState.GameFinished } as IWordGameState);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async sendScore(score: number, seconds: number, details: string) {
|
||||||
|
if (!this.props.enableHighScores)
|
||||||
|
return;
|
||||||
|
await this.wordService.SubmitScore(score, seconds, details);
|
||||||
|
this.getHighScores();
|
||||||
|
}
|
||||||
|
|
||||||
|
async getHighScores(){
|
||||||
|
if (!this.props.enableHighScores)
|
||||||
|
return;
|
||||||
|
|
||||||
|
let scores = await this.wordService.GetHighScores();
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
highScores: scores
|
||||||
|
} as IWordGameState);
|
||||||
|
}
|
||||||
|
|
||||||
|
keyDownAnswer(event: React.KeyboardEvent<HTMLInputElement>) {
|
||||||
|
if (event.keyCode == 13)
|
||||||
|
this.btnAnswer();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async btnStartGame() {
|
||||||
|
this.setState({
|
||||||
|
gamestate: GameState.GameLoading
|
||||||
|
} as IWordGameState);
|
||||||
|
|
||||||
|
this.currentGame = this.wordService.GenerateGame();
|
||||||
|
|
||||||
|
|
||||||
|
await new Promise<void>(resolve => { setTimeout(resolve, 1000); });
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
gamestate: GameState.GamePlaying,
|
||||||
|
currentRound: 0,
|
||||||
|
txtAnswer: '',
|
||||||
|
score: 0,
|
||||||
|
lblMessage: '',
|
||||||
|
currentWord: this.currentGame.rounds[0].word,
|
||||||
|
possibleAnswersText: this.getPossibleAnswersText(0)
|
||||||
|
} as IWordGameState);
|
||||||
|
|
||||||
|
this.numTimer = 30;
|
||||||
|
this.setState({ lblTimer: this.numTimer + " seconds" });
|
||||||
|
this.timerInterval = setInterval(this.timerTick.bind(this), 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
timerTick() {
|
||||||
|
//for debugging
|
||||||
|
// if (this.numTimer>25)
|
||||||
|
this.numTimer--;
|
||||||
|
this.setState({ lblTimer: this.numTimer + " seconds" });
|
||||||
|
if (this.numTimer == 0) {
|
||||||
|
this.finishGame();
|
||||||
|
}
|
||||||
|
|
||||||
|
//toast messages
|
||||||
|
if (this.messageCounter > 0) {
|
||||||
|
this.messageCounter--;
|
||||||
|
if (this.messageCounter == 0)
|
||||||
|
this.setState({ lblMessage: '' } as IWordGameState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stopTimer() {
|
||||||
|
this.setState({ lblTimer: '' });
|
||||||
|
clearInterval(this.timerInterval);
|
||||||
|
}
|
||||||
|
|
||||||
|
public render(): React.ReactElement<IWordGameProps> {
|
||||||
|
window["wordgame"] = this;
|
||||||
|
|
||||||
|
|
||||||
|
let body: JSX.Element;
|
||||||
|
switch (this.state.gamestate) {
|
||||||
|
case GameState.Title:
|
||||||
|
body =
|
||||||
|
<div>
|
||||||
|
<h1>{this.props.description}</h1>
|
||||||
|
{/* <h1>Word Game</h1> */}
|
||||||
|
{/* <h4>Count: {this.wordService.GetWordCount()}</h4> */}
|
||||||
|
<p>Unscramble as many words as you can before the time runs out</p>
|
||||||
|
<button className="blueButton" onClick={this.btnStartGame.bind(this)}>
|
||||||
|
Start Game
|
||||||
|
</button>
|
||||||
|
{
|
||||||
|
this.props.enableHighScores ?
|
||||||
|
<WordHighScores scores={this.state.highScores}></WordHighScores> : ''
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
break;
|
||||||
|
case GameState.GameLoading:
|
||||||
|
body =
|
||||||
|
<div>
|
||||||
|
<img src={logo} />
|
||||||
|
<span style={{ marginLeft: '15px' }}>
|
||||||
|
Loading...
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
break;
|
||||||
|
case GameState.GamePlaying:
|
||||||
|
body =
|
||||||
|
<div>
|
||||||
|
<div className="currentWord" style={{position:"relative"}}>
|
||||||
|
{this.state.currentWord}
|
||||||
|
{/* MOBILE TOAST */}
|
||||||
|
{
|
||||||
|
this.state.lblMessage != '' && this.state.mobileMode?
|
||||||
|
<div className={this.state.lblMessage == 'Incorrect' ? 'toastMiniRed word-fade-in' : 'toastMiniGreen word-fade-in'}
|
||||||
|
style={{position:"absolute",left:"0",right:"0",fontSize:"10pt",top:"83px",padding:"0"}}>
|
||||||
|
{
|
||||||
|
this.state.lblMessage
|
||||||
|
}
|
||||||
|
</div> : ''
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0,user-scalable=0"></meta>
|
||||||
|
<input type="text" autoFocus={true} placeholder="Enter Answer" id="txtWordGameAnswer"
|
||||||
|
autoComplete="off" autoCapitalize="off" autoCorrect="off" spellCheck={false}
|
||||||
|
value={this.state.txtAnswer} onChange={this.answerChanged.bind(this)} onKeyDown={this.keyDownAnswer.bind(this)} />
|
||||||
|
<div className="numAnswersTip">{this.state.possibleAnswersText}</div>
|
||||||
|
<table style={{ marginLeft: 'Auto', marginRight: 'Auto' }}>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<button className="blueButton" style={{ marginTop: '20px' }} onClick={this.btnAnswer.bind(this)}>
|
||||||
|
Submit
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<button className="greyButton" onClick={this.btnSkip.bind(this)} style={{ marginTop: '20px' }} >
|
||||||
|
Skip
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<div className="timer">{this.state.lblTimer}</div>
|
||||||
|
{/* DESKTOP TOAST */}
|
||||||
|
{
|
||||||
|
this.state.lblMessage != '' && !this.state.mobileMode?
|
||||||
|
<div className="toast word-fade-in" >
|
||||||
|
<div className={this.state.lblMessage == 'Incorrect' ? 'toastRed' : 'toastGreen'}>{this.state.lblMessage}</div>
|
||||||
|
</div> : ''
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
break;
|
||||||
|
case GameState.GameFinished:
|
||||||
|
|
||||||
|
body =
|
||||||
|
<div>
|
||||||
|
<h1>Well done! Score {this.state.score} out of 6</h1>
|
||||||
|
<p>See answers below to the current round</p>
|
||||||
|
<button className="blueButton" onClick={this.btnStartGame.bind(this)}
|
||||||
|
style={{ marginBottom: '20px' }}>Play Again</button>
|
||||||
|
<div className="gameReview">
|
||||||
|
<ul>
|
||||||
|
{
|
||||||
|
this.currentGame.rounds.map(round => (
|
||||||
|
<li key={round.word + '_wordgame_word'}><b>Word </b><span>{round.word}</span>
|
||||||
|
{
|
||||||
|
round.answers.map(answer => (
|
||||||
|
<ul key={answer + '_wordgame_answer'}>
|
||||||
|
<b>Answer </b> {answer}
|
||||||
|
{
|
||||||
|
round.correctAnswer == answer ?
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" style={{ marginLeft: '5px' }} width="15" height="15" viewBox="0 0 24 24" color="#155724">
|
||||||
|
<path fill="currentcolor" d="M20.285 2l-11.285 11.567-5.286-5.011-3.714 3.716 9 8.728 15-15.285z" /></svg>
|
||||||
|
: ''
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</li>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
{
|
||||||
|
this.props.enableHighScores ?
|
||||||
|
<WordHighScores scores={this.state.highScores}></WordHighScores> : ''
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
let maindiv = <div className="wordGameCustom" style={{ position: 'relative' }}>{body}</div>;
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.wordGame} style={{ textAlign: 'center' }} >
|
||||||
|
{maindiv}
|
||||||
|
</div >
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
import * as React from "react";
|
||||||
|
import { WordGameListItem } from "./WordService"
|
||||||
|
|
||||||
|
export interface IWordHighScoresProps {
|
||||||
|
scores: WordGameListItem[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class WordHighScores extends React.Component<IWordHighScoresProps, {}> {
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public render(): React.ReactElement<IWordHighScoresProps> {
|
||||||
|
let rank = 1;
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{
|
||||||
|
this.props.scores.length>0 ?
|
||||||
|
<h3 style={{ textDecoration: 'underline' }}>High Scores</h3> : ''
|
||||||
|
|
||||||
|
}
|
||||||
|
<table style={{ marginLeft: 'Auto', marginRight: 'Auto' }}>
|
||||||
|
<tbody>
|
||||||
|
{
|
||||||
|
this.props.scores.map(score => (
|
||||||
|
<tr>
|
||||||
|
<td><b>{rank++ + ') '}</b></td>
|
||||||
|
<td><b>{score.Name} </b></td>
|
||||||
|
<td><span style={{ marginLeft: '10px' }}>Score: {score.Score} </span></td>
|
||||||
|
<td><span style={{ marginLeft: '10px' }}>Seconds: {score.Seconds}</span></td>
|
||||||
|
</tr>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,408 @@
|
||||||
|
// import * as $ from '../assets/jquery.min';
|
||||||
|
import { SPComponentLoader } from '@microsoft/sp-loader';
|
||||||
|
import WordGame from './WordGame';
|
||||||
|
import { WebPartContext } from '@microsoft/sp-webpart-base';
|
||||||
|
import {
|
||||||
|
SPHttpClient,
|
||||||
|
SPHttpClientResponse,
|
||||||
|
HttpClientConfiguration,
|
||||||
|
ISPHttpClientOptions
|
||||||
|
} from '@microsoft/sp-http';
|
||||||
|
const wordjpg: any = require('../assets/wordlist.jpg');
|
||||||
|
const $: any = require('../assets/jquery.min');
|
||||||
|
// const wordlist: any = require('../assets/wordlist.json');
|
||||||
|
|
||||||
|
export class Game
|
||||||
|
{
|
||||||
|
rounds:Round[] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Round
|
||||||
|
{
|
||||||
|
word = "";
|
||||||
|
answers:string[] = [];
|
||||||
|
correctAnswer = "";
|
||||||
|
incorrectAnswers:string[] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export class WordService {
|
||||||
|
|
||||||
|
allwords: string[] = [];
|
||||||
|
words3: string[] = [];
|
||||||
|
words4: string[] = [];
|
||||||
|
words5: string[] = [];
|
||||||
|
words6: string[] = [];
|
||||||
|
words7: string[] = [];
|
||||||
|
words8: string[] = [];
|
||||||
|
context:WebPartContext;
|
||||||
|
|
||||||
|
GenerateGame():Game
|
||||||
|
{
|
||||||
|
|
||||||
|
let game = new Game();
|
||||||
|
|
||||||
|
let round1 = new Round(); round1.word = this.GetRandomScrambledWord(5); round1.answers = this.FindPossibleWords(round1.word);
|
||||||
|
let round2 = new Round(); round2.word = this.GetRandomScrambledWord(5); round2.answers = this.FindPossibleWords(round2.word);
|
||||||
|
let round3 = new Round(); round3.word = this.GetRandomScrambledWord(5); round3.answers = this.FindPossibleWords(round3.word);
|
||||||
|
let round4 = new Round(); round4.word = this.GetRandomScrambledWord(6); round4.answers = this.FindPossibleWords(round4.word);
|
||||||
|
let round5 = new Round(); round5.word = this.GetRandomScrambledWord(6); round5.answers = this.FindPossibleWords(round5.word);
|
||||||
|
let round6 = new Round(); round6.word = this.GetRandomScrambledWord(6); round6.answers = this.FindPossibleWords(round6.word);
|
||||||
|
|
||||||
|
game.rounds.push(round1);
|
||||||
|
game.rounds.push(round2);
|
||||||
|
game.rounds.push(round3);
|
||||||
|
game.rounds.push(round4);
|
||||||
|
game.rounds.push(round5);
|
||||||
|
game.rounds.push(round6);
|
||||||
|
|
||||||
|
return game;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async loadWords() {
|
||||||
|
|
||||||
|
window["wordService"] = this;
|
||||||
|
|
||||||
|
/* SP Loader Implementation */
|
||||||
|
// console.log(jquery);
|
||||||
|
// await SPComponentLoader.loadScript('../assets/jquery.min.js', { globalExportsName: "ScriptGlobal" });
|
||||||
|
// console.log('jquery loaded');
|
||||||
|
|
||||||
|
|
||||||
|
/* JSON File Implementation
|
||||||
|
If you have a custom word list you would like to use
|
||||||
|
add it as a JSON file in assets/wordlist.json and
|
||||||
|
uncomment the const wordlist at the top of this file.
|
||||||
|
Then comment out the Text File implementation below */
|
||||||
|
// let wordvalues = (Object as any).values(wordlist) as any;
|
||||||
|
// let wordlistlength = wordvalues.length as number;
|
||||||
|
// for(let i=0;i<wordlistlength;i++)
|
||||||
|
// this.allwords.push(wordvalues[i]);
|
||||||
|
|
||||||
|
|
||||||
|
/* Text File Implementation
|
||||||
|
Yields the smallest file download size vs JSON (700k vs 1.3mb)
|
||||||
|
The word list is a text file stored as wordlist.jpg and
|
||||||
|
loaded as text/plain using an overrided mime type */
|
||||||
|
var responseText = await $.ajax({
|
||||||
|
url: wordjpg,
|
||||||
|
beforeSend: function (xhr) {
|
||||||
|
xhr.overrideMimeType("text/plain; charset=x-user-defined");
|
||||||
|
}
|
||||||
|
}) as string;
|
||||||
|
this.allwords = responseText.split("\r\n");
|
||||||
|
|
||||||
|
this.allwords.forEach(word => {
|
||||||
|
if (word.indexOf("-") > -1)
|
||||||
|
return;
|
||||||
|
if (word.indexOf("-") > -1)
|
||||||
|
return;
|
||||||
|
switch (word.length) {
|
||||||
|
case 3:
|
||||||
|
this.words3.push(word);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
this.words4.push(word);
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
this.words5.push(word);
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
this.words6.push(word);
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
this.words7.push(word);
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
this.words8.push(word);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('words length: ' + this.allwords.length);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
GetWordCount(): number {
|
||||||
|
return this.allwords.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
GetRandomScrambledWord(level: number) {
|
||||||
|
let randomWord = "";
|
||||||
|
let randwordnum = 0;
|
||||||
|
switch (level) {
|
||||||
|
case 3:
|
||||||
|
randwordnum = Math.floor(Math.random() * Math.floor(this.words3.length));
|
||||||
|
randomWord = this.words3[randwordnum];
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
randwordnum = Math.floor(Math.random() * Math.floor(this.words4.length));
|
||||||
|
randomWord = this.words4[randwordnum];
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
randwordnum = Math.floor(Math.random() * Math.floor(this.words5.length));
|
||||||
|
randomWord = this.words5[randwordnum];
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
randwordnum = Math.floor(Math.random() * Math.floor(this.words6.length));
|
||||||
|
randomWord = this.words6[randwordnum];
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
randwordnum = Math.floor(Math.random() * Math.floor(this.words7.length));
|
||||||
|
randomWord = this.words7[randwordnum];
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
randwordnum = Math.floor(Math.random() * Math.floor(this.words8.length));
|
||||||
|
randomWord = this.words8[randwordnum];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
let scrambledWord = this.ScrambleWord(randomWord);
|
||||||
|
return scrambledWord;
|
||||||
|
}
|
||||||
|
|
||||||
|
FindPossibleWords(currentWord: string) {
|
||||||
|
//coati
|
||||||
|
//taco
|
||||||
|
|
||||||
|
//currentWord = "coati";
|
||||||
|
let possibleWords: string[] = [];
|
||||||
|
this.allwords.forEach(word => {
|
||||||
|
let tempword = word;//taco
|
||||||
|
for (let i = 0; i < currentWord.length; i++) {
|
||||||
|
|
||||||
|
let letter = currentWord[i];
|
||||||
|
if (tempword.indexOf(letter) > -1) {
|
||||||
|
tempword = tempword.slice(0, tempword.indexOf(letter)) + tempword.slice(tempword.indexOf(letter) + 1);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
tempword = 'n';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (tempword.length == 0)
|
||||||
|
possibleWords.push(word)
|
||||||
|
});
|
||||||
|
|
||||||
|
return possibleWords;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//replace a character in a string
|
||||||
|
private replaceCharAt(orig:string, index:number, replacement:string): string {
|
||||||
|
return orig.substr(0, index) + replacement + orig.substr(index + replacement.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
ScrambleWord(word: string): string {
|
||||||
|
let notScrambled = true;
|
||||||
|
let scrambledWord = "";
|
||||||
|
let count = 0;
|
||||||
|
var originalword = word;
|
||||||
|
while (notScrambled) {
|
||||||
|
word = originalword;
|
||||||
|
let chars = '';
|
||||||
|
for (let i = 0; i < word.length; i++)
|
||||||
|
chars += ' ';
|
||||||
|
|
||||||
|
let index = 0;
|
||||||
|
while (word.length > 0) {
|
||||||
|
let next = Math.floor(Math.random() * Math.floor(word.length)); // Get a random number between 0 and the length of the word.
|
||||||
|
chars = this.replaceCharAt(chars, index, word[next]); // Take the character from the random position and add to our char array.
|
||||||
|
word = word.substr(0, next) + word.substr(next + 1); // Remove the character from the word.
|
||||||
|
++index;
|
||||||
|
}
|
||||||
|
scrambledWord = chars.slice(0);
|
||||||
|
count++;
|
||||||
|
|
||||||
|
if (originalword!=scrambledWord)
|
||||||
|
notScrambled = false;
|
||||||
|
|
||||||
|
//just in case there is a problem
|
||||||
|
if (count == 10)
|
||||||
|
notScrambled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return scrambledWord;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//SHAREPOINT APIS
|
||||||
|
|
||||||
|
SetContext(context:WebPartContext){
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async SubmitScore(score:number,seconds:number,details:string){
|
||||||
|
try{
|
||||||
|
await this.CreateListIfNotExists();
|
||||||
|
await this.CreateListItem(score,seconds,details);
|
||||||
|
}catch(error){}
|
||||||
|
}
|
||||||
|
|
||||||
|
async GetHighScores():Promise<WordGameListItem[]>{
|
||||||
|
|
||||||
|
var scores:WordGameListItem[] = [];
|
||||||
|
try{
|
||||||
|
let result = await this.context.spHttpClient.get(this.context.pageContext.web.absoluteUrl + "/_api/web/lists/GetByTitle('WordGameList')/items", SPHttpClient.configurations.v1);
|
||||||
|
let json:any = await result.json();
|
||||||
|
console.log(json);
|
||||||
|
|
||||||
|
|
||||||
|
json.value.forEach(item => {
|
||||||
|
scores.push(new WordGameListItem(item.Title,item.Score,item.Seconds,item.Details));
|
||||||
|
});
|
||||||
|
|
||||||
|
scores.sort((a,b)=> {return b.Score-a.Score});
|
||||||
|
|
||||||
|
//top 10
|
||||||
|
if (scores.length>10)
|
||||||
|
scores = scores.slice(0,10);
|
||||||
|
|
||||||
|
console.log('high scores',scores);
|
||||||
|
}catch(error){
|
||||||
|
console.log('could not find list');
|
||||||
|
}
|
||||||
|
|
||||||
|
return scores;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async CreateListIfNotExists(){
|
||||||
|
let result = await this.context.spHttpClient.get(this.context.pageContext.web.absoluteUrl + '/_api/web/lists', SPHttpClient.configurations.v1);
|
||||||
|
let json:any = await result.json();
|
||||||
|
let exists = false;
|
||||||
|
json.value.forEach(list => {
|
||||||
|
if (list.Title=='WordGameList'){
|
||||||
|
console.log('list found');
|
||||||
|
exists = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
console.log(json);
|
||||||
|
if (exists==false){
|
||||||
|
console.log('Attempting to create list');
|
||||||
|
await this.CreateList();
|
||||||
|
await this.AddListColumnNumber('Score');
|
||||||
|
await this.AddListColumnNumber('Seconds');
|
||||||
|
await this.AddListColumnMultiLineText('Details');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async CreateListItem(score:number,seconds:number,details:string){
|
||||||
|
var listMetadata = {
|
||||||
|
"__metadata": {
|
||||||
|
"type": "SP.Data.WordGameListListItem"
|
||||||
|
},
|
||||||
|
"Title": this.context.pageContext.user.displayName,
|
||||||
|
"Score": score,
|
||||||
|
"Seconds": seconds,
|
||||||
|
"Details": details
|
||||||
|
};
|
||||||
|
|
||||||
|
var options: ISPHttpClientOptions = {
|
||||||
|
headers: {
|
||||||
|
"Accept": "application/json;odata=verbose",
|
||||||
|
"Content-Type": "application/json;odata=verbose",
|
||||||
|
"OData-Version": "" //Really important to specify
|
||||||
|
},
|
||||||
|
body: JSON.stringify(listMetadata)
|
||||||
|
};
|
||||||
|
|
||||||
|
let result = await this.context.spHttpClient.post(
|
||||||
|
this.context.pageContext.web.absoluteUrl + "/_api/web/lists/GetByTitle('WordGameList')/items", SPHttpClient.configurations.v1,options);
|
||||||
|
let json:any = await result.json();
|
||||||
|
console.log(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
async CreateList(){
|
||||||
|
var listMetadata = {
|
||||||
|
"__metadata": {
|
||||||
|
"type": "SP.List"
|
||||||
|
},
|
||||||
|
"AllowContentTypes": true,
|
||||||
|
"BaseTemplate": 100,
|
||||||
|
"ContentTypesEnabled": true,
|
||||||
|
"Description": "Holds high scores for the word game",
|
||||||
|
"Title": "WordGameList"
|
||||||
|
};
|
||||||
|
|
||||||
|
var options: ISPHttpClientOptions = {
|
||||||
|
headers: {
|
||||||
|
"Accept": "application/json;odata=verbose",
|
||||||
|
"Content-Type": "application/json;odata=verbose",
|
||||||
|
"OData-Version": "" //Really important to specify
|
||||||
|
},
|
||||||
|
body: JSON.stringify(listMetadata)
|
||||||
|
};
|
||||||
|
|
||||||
|
let result = await this.context.spHttpClient.post(
|
||||||
|
this.context.pageContext.web.absoluteUrl + '/_api/web/lists', SPHttpClient.configurations.v1,options);
|
||||||
|
let json:any = await result.json();
|
||||||
|
console.log(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
async AddListColumnMultiLineText(name:string){
|
||||||
|
var listMetadata = {
|
||||||
|
'__metadata': {'type':'SP.FieldNumber'},
|
||||||
|
'FieldTypeKind': 3,
|
||||||
|
'Title': name,
|
||||||
|
};
|
||||||
|
|
||||||
|
var options: ISPHttpClientOptions = {
|
||||||
|
headers: {
|
||||||
|
"Accept": "application/json;odata=verbose",
|
||||||
|
"Content-Type": "application/json;odata=verbose",
|
||||||
|
"OData-Version": "" //Really important to specify
|
||||||
|
},
|
||||||
|
body: JSON.stringify(listMetadata)
|
||||||
|
};
|
||||||
|
|
||||||
|
let result = await this.context.spHttpClient.post(
|
||||||
|
this.context.pageContext.web.absoluteUrl + "/_api/web/lists/getbytitle('WordGameList')/fields", SPHttpClient.configurations.v1,options);
|
||||||
|
let json:any = await result.json();
|
||||||
|
console.log(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
async AddListColumnNumber(name:string){
|
||||||
|
var listMetadata = {
|
||||||
|
'__metadata': {'type':'SP.FieldNumber'},
|
||||||
|
'FieldTypeKind': 9,
|
||||||
|
'Title': name,
|
||||||
|
'MinimumValue': 0,
|
||||||
|
'MaximumValue': 1000000
|
||||||
|
};
|
||||||
|
|
||||||
|
var options: ISPHttpClientOptions = {
|
||||||
|
headers: {
|
||||||
|
"Accept": "application/json;odata=verbose",
|
||||||
|
"Content-Type": "application/json;odata=verbose",
|
||||||
|
"OData-Version": "" //Really important to specify
|
||||||
|
},
|
||||||
|
body: JSON.stringify(listMetadata)
|
||||||
|
};
|
||||||
|
|
||||||
|
let result = await this.context.spHttpClient.post(
|
||||||
|
this.context.pageContext.web.absoluteUrl + "/_api/web/lists/getbytitle('WordGameList')/fields", SPHttpClient.configurations.v1,options);
|
||||||
|
let json:any = await result.json();
|
||||||
|
console.log(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export class WordGameListItem{
|
||||||
|
Name:string;
|
||||||
|
Score:number;
|
||||||
|
Seconds:number;
|
||||||
|
Details:string;
|
||||||
|
constructor(name:string,score:number,seconds:number,details:string){
|
||||||
|
this.Name = name;
|
||||||
|
this.Score = score;
|
||||||
|
this.Seconds = seconds;
|
||||||
|
this.Details = details;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
|
||||||
|
.word-fade-in {
|
||||||
|
animation-name: wordFadeIn;
|
||||||
|
animation-duration: 0.5s;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@keyframes wordFadeIn {
|
||||||
|
0% {opacity: 0;}
|
||||||
|
100% {opacity: 1;}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
define([], function() {
|
||||||
|
return {
|
||||||
|
"PropertyPaneDescription": "Description",
|
||||||
|
"BasicGroupName": "Group Name",
|
||||||
|
"DescriptionFieldLabel": "Description Field"
|
||||||
|
}
|
||||||
|
});
|
|
@ -0,0 +1,10 @@
|
||||||
|
declare interface IWordGameWebPartStrings {
|
||||||
|
PropertyPaneDescription: string;
|
||||||
|
BasicGroupName: string;
|
||||||
|
DescriptionFieldLabel: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module 'WordGameWebPartStrings' {
|
||||||
|
const strings: IWordGameWebPartStrings;
|
||||||
|
export = strings;
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es5",
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"module": "esnext",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"jsx": "react",
|
||||||
|
"declaration": true,
|
||||||
|
"sourceMap": true,
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"typeRoots": [
|
||||||
|
"./node_modules/@types",
|
||||||
|
"./node_modules/@microsoft"
|
||||||
|
],
|
||||||
|
"types": [
|
||||||
|
"es6-promise",
|
||||||
|
"webpack-env"
|
||||||
|
],
|
||||||
|
"lib": [
|
||||||
|
"es5",
|
||||||
|
"dom",
|
||||||
|
"es2015.collection"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue