107 files changed
+15274
-5319
No files matched your search
@@ -1,4 +1,17 @@
|
|||||||
{
|
{
|
||||||
"presets": ["es2015", "stage-0"],
|
"presets": [
|
||||||
"plugins": ["transform-react-jsx"]
|
[
|
||||||
|
"env",
|
||||||
|
{
|
||||||
|
"targets": {
|
||||||
|
"node": "8.5.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"plugins": [
|
||||||
|
"transform-react-jsx",
|
||||||
|
"transform-object-rest-spread",
|
||||||
|
"transform-class-properties"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
+18
@@ -0,0 +1,18 @@
|
|||||||
|
pipeline:
|
||||||
|
build:
|
||||||
|
image: docker
|
||||||
|
commands:
|
||||||
|
- docker build -t tmshv/hudozka:latest -t tmshv/hudozka:build-${DRONE_BUILD_NUMBER} .
|
||||||
|
- docker save -o hudozka-docker_container${DRONE_BUILD_NUMBER}.tar tmshv/hudozka:build-${DRONE_BUILD_NUMBER}
|
||||||
|
volumes:
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
|
|
||||||
|
scp:
|
||||||
|
image: appleboy/drone-scp
|
||||||
|
host: art.shlisselburg.org
|
||||||
|
username: master
|
||||||
|
target: ~/Hudozka
|
||||||
|
source: hudozka-docker_container${DRONE_BUILD_NUMBER}.tar
|
||||||
|
secrets: [scp_key]
|
||||||
|
when:
|
||||||
|
branch: master
|
||||||
@@ -1 +0,0 @@
|
|||||||
6.9.1
|
|
||||||
@@ -1,12 +1,14 @@
|
|||||||
# Build
|
# Build
|
||||||
FROM node:8 as build
|
FROM node:10 as build
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
COPY package.json package-lock.json gulpfile.babel.js .babelrc ./
|
COPY package.json package-lock.json ./
|
||||||
RUN npm i
|
RUN npm i
|
||||||
|
COPY webpack.config.js gulpfile.js .babelrc ./
|
||||||
|
COPY robots.txt ./
|
||||||
COPY src ./src
|
COPY src ./src
|
||||||
RUN npm run gulp deploy
|
RUN npm run gulp
|
||||||
|
RUN npm run build-app
|
||||||
RUN npm run out
|
RUN npm run out
|
||||||
|
|
||||||
|
|
||||||
@@ -14,7 +16,7 @@ RUN npm run out
|
|||||||
FROM node:8-alpine
|
FROM node:8-alpine
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
COPY package.json package-lock.json gulpfile.babel.js .babelrc ./
|
COPY package.json package-lock.json ./
|
||||||
RUN npm i --production
|
RUN npm i --production
|
||||||
|
|
||||||
COPY --from=build /app/out ./src
|
COPY --from=build /app/out ./src
|
||||||
@@ -1,30 +1,27 @@
|
|||||||
# [ART.SHLISSELBURG.ORG](https://art.shlisselburg.org)
|
# [ART.SHLISSELBURG.ORG](https://art.shlisselburg.org)
|
||||||
Art school of **Shlisselburg**
|
|
||||||
|
Site of Art school of Shlisselburg
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
|
### Build
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ docker build -t tmshv/hudozka .
|
||||||
|
```
|
||||||
|
|
||||||
### How to run
|
### How to run
|
||||||
|
|
||||||
```bash
|
#### Add .env file
|
||||||
$ <ENV> npm start
|
|
||||||
|
```
|
||||||
|
MONGO_HUDOZKA_PASSWORD=<strong_password>
|
||||||
|
HUDOZKA_DB_URI=mongodb://hudozka:strong_password@mongo:27017/hudozka?authSource=admin
|
||||||
```
|
```
|
||||||
|
|
||||||
### Environments variables
|
#### Run
|
||||||
|
|
||||||
#### PORT
|
|
||||||
|
|
||||||
TCP Port for app
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
PORT=8080 npm start
|
$ docker-compose up -d
|
||||||
```
|
|
||||||
|
|
||||||
#### PRIVATE
|
|
||||||
|
|
||||||
Path to JSON file with secret keys
|
|
||||||
|
|
||||||
```bash
|
|
||||||
PRIVATE=private.json npm start
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### How to sync data base
|
### How to sync data base
|
||||||
@@ -39,23 +36,11 @@ PWD=<mongo_password>
|
|||||||
mongodump -d ${DB} --gzip --archive | mongorestore -h ${HOST} -u ${USER} -p ${PWD} -d ${DB} --gzip --archive
|
mongodump -d ${DB} --gzip --archive | mongorestore -h ${HOST} -u ${USER} -p ${PWD} -d ${DB} --gzip --archive
|
||||||
```
|
```
|
||||||
|
|
||||||
## Build client
|
## Based on:
|
||||||
|
- [NodeJS](http://nodejs.org) 10.0
|
||||||
```bash
|
|
||||||
$ NODE_ENV=production gulp deploy
|
|
||||||
```
|
|
||||||
|
|
||||||
For developer purposes:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ gulp deploy
|
|
||||||
```
|
|
||||||
|
|
||||||
### Based on:
|
|
||||||
- [NodeJS](http://nodejs.org) 6.0
|
|
||||||
- [koa.js](http://koajs.com) 2.0
|
- [koa.js](http://koajs.com) 2.0
|
||||||
- [angular.js](https://angularjs.org) 1.5
|
- [React](https://react.org) 16.3
|
||||||
- [MongoDB](http://mongodb.org) 3.2
|
- [MongoDB](http://mongodb.org) 3.6
|
||||||
|
|
||||||
## Contributors
|
## Contributors
|
||||||
- Roman Timashev ([roman@tmshv.ru](mailto:roman@tmshv.ru))
|
- Roman Timashev ([roman@tmshv.ru](mailto:roman@tmshv.ru))
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
echo "Deploying art.shburg.org"
|
|
||||||
|
|
||||||
server="art.shlisselburg.org"
|
|
||||||
tarfile="build.tar.gz"
|
|
||||||
tarpath=$TMPDIR${tarfile}
|
|
||||||
destpath="~/www/art.shburg.org"
|
|
||||||
|
|
||||||
echo "Building..."
|
|
||||||
npm run out
|
|
||||||
NODE_ENV=production npm run gulp deploy
|
|
||||||
|
|
||||||
echo "Packing $tarpath"
|
|
||||||
env GZIP=-9 tar -czf ${tarpath} \
|
|
||||||
--disable-copyfile \
|
|
||||||
--exclude=node_modules \
|
|
||||||
--exclude=.git \
|
|
||||||
--exclude=.idea \
|
|
||||||
--exclude=.DS_Store \
|
|
||||||
--exclude=src \
|
|
||||||
--exclude=deploy.sh \
|
|
||||||
--exclude=install.sh \
|
|
||||||
.
|
|
||||||
|
|
||||||
echo "Uploading..."
|
|
||||||
ssh hoster@${server} "rm -rf $destpath/public/*"
|
|
||||||
scp ${tarpath} hoster@${server}:${destpath}
|
|
||||||
|
|
||||||
echo "Installing..."
|
|
||||||
ssh hoster@${server} "bash -s" -- < ./install.sh ${destpath} ${tarfile}
|
|
||||||
|
|
||||||
echo "Clear..."
|
|
||||||
rm ${tarpath}
|
|
||||||
|
|
||||||
echo "Done"
|
|
||||||
+19
-1
@@ -1,10 +1,28 @@
|
|||||||
version: '3'
|
version: '3'
|
||||||
|
volumes:
|
||||||
|
data:
|
||||||
|
|
||||||
services:
|
services:
|
||||||
|
mongo:
|
||||||
|
image: mongo:3.6
|
||||||
|
restart: always
|
||||||
|
volumes:
|
||||||
|
- data:/data/db
|
||||||
|
ports:
|
||||||
|
- "1817:27017"
|
||||||
|
environment:
|
||||||
|
- MONGO_INITDB_ROOT_USERNAME=hudozka
|
||||||
|
- MONGO_INITDB_ROOT_PASSWORD=${MONGO_HUDOZKA_PASSWORD}
|
||||||
|
|
||||||
app:
|
app:
|
||||||
restart: always
|
restart: always
|
||||||
|
depends_on:
|
||||||
|
- mongo
|
||||||
build:
|
build:
|
||||||
context: ./
|
context: ./
|
||||||
dockerfile: Hudozka.Dockerfile
|
dockerfile: Dockerfile
|
||||||
|
links:
|
||||||
|
- mongo
|
||||||
ports:
|
ports:
|
||||||
- "1880:3000"
|
- "1880:3000"
|
||||||
environment:
|
environment:
|
||||||
|
|||||||
@@ -1,115 +0,0 @@
|
|||||||
import gulp from 'gulp'
|
|
||||||
import postcss from 'gulp-postcss'
|
|
||||||
import sass from 'gulp-sass'
|
|
||||||
import imageMin from 'gulp-imagemin'
|
|
||||||
import uglify from 'gulp-uglify'
|
|
||||||
import concat from 'gulp-concat'
|
|
||||||
import webpack from 'webpack-stream'
|
|
||||||
import named from 'vinyl-named'
|
|
||||||
import autoprefixer from 'autoprefixer'
|
|
||||||
import cssnano from 'cssnano'
|
|
||||||
|
|
||||||
let env = process.env['NODE_ENV'] || 'production'
|
|
||||||
let isProduction = env === 'production'
|
|
||||||
|
|
||||||
gulp.task('compile 3rdparty', () => {
|
|
||||||
const files = [
|
|
||||||
'./node_modules/spin.js/spin.js',
|
|
||||||
|
|
||||||
'./node_modules/babel-polyfill/dist/polyfill.min.js'
|
|
||||||
]
|
|
||||||
|
|
||||||
return gulp.src(files)
|
|
||||||
.pipe(concat('libs.js'))
|
|
||||||
.pipe(uglify())
|
|
||||||
.pipe(gulp.dest('./public'))
|
|
||||||
})
|
|
||||||
|
|
||||||
gulp.task('style', () => {
|
|
||||||
const processors = [
|
|
||||||
autoprefixer({
|
|
||||||
browsers: ['last 1 version']
|
|
||||||
}),
|
|
||||||
cssnano({
|
|
||||||
zindex: false
|
|
||||||
})
|
|
||||||
]
|
|
||||||
|
|
||||||
return gulp.src('./src/style/style.scss')
|
|
||||||
.pipe(sass())
|
|
||||||
.pipe(postcss(processors))
|
|
||||||
.pipe(concat('style.css'))
|
|
||||||
.pipe(gulp.dest('./public'))
|
|
||||||
})
|
|
||||||
|
|
||||||
gulp.task('copy fonts', () => {
|
|
||||||
return gulp.src('./src/assets/fonts/*/*')
|
|
||||||
.pipe(gulp.dest('./public/fonts'))
|
|
||||||
})
|
|
||||||
|
|
||||||
gulp.task('copy graphics', () => {
|
|
||||||
return gulp.src('./src/assets/graphics/*')
|
|
||||||
.pipe(gulp.dest('./public/graphics'))
|
|
||||||
})
|
|
||||||
|
|
||||||
gulp.task('copy 3rdparty', () => {
|
|
||||||
return gulp.src('./src/3rdparty/*')
|
|
||||||
.pipe(gulp.dest('./public'))
|
|
||||||
})
|
|
||||||
|
|
||||||
gulp.task('copy robots.txt', () => {
|
|
||||||
return gulp.src('./robots.txt')
|
|
||||||
.pipe(gulp.dest('./public'))
|
|
||||||
})
|
|
||||||
|
|
||||||
gulp.task('copy views', () => {
|
|
||||||
return gulp.src('./src/views/**')
|
|
||||||
.pipe(gulp.dest('./out/views'))
|
|
||||||
})
|
|
||||||
|
|
||||||
gulp.task('compile', () => {
|
|
||||||
const doWatch = !isProduction
|
|
||||||
const doSourceMaps = !isProduction
|
|
||||||
|
|
||||||
return gulp.src([
|
|
||||||
'./src/public/app.js'
|
|
||||||
])
|
|
||||||
.pipe(named())
|
|
||||||
.pipe(webpack({
|
|
||||||
// devtool: doSourceMaps ? 'inline-source-map' : null,
|
|
||||||
// watch: doWatch,
|
|
||||||
output: {
|
|
||||||
filename: '[name].js'
|
|
||||||
},
|
|
||||||
module: {
|
|
||||||
loaders: [
|
|
||||||
{test: /\.js$/, loader: 'babel'},
|
|
||||||
// {test: /\.html$/, loader: 'raw!html-minify'}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
// 'html-minify-loader': {
|
|
||||||
// empty: true,
|
|
||||||
// cdata: true,
|
|
||||||
// comments: false,
|
|
||||||
// dom: {
|
|
||||||
// lowerCaseAttributeNames: false
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}))
|
|
||||||
.pipe(gulp.dest('./public'))
|
|
||||||
})
|
|
||||||
|
|
||||||
gulp.task('imagemin', () => {
|
|
||||||
return gulp.src('./src/assets/graphics/**/*')
|
|
||||||
.pipe(imageMin())
|
|
||||||
.pipe(gulp.dest('./public/graphics'))
|
|
||||||
})
|
|
||||||
|
|
||||||
gulp.task('production', () => {
|
|
||||||
isProduction = true
|
|
||||||
})
|
|
||||||
|
|
||||||
gulp.task('default', ['style', 'compile'])
|
|
||||||
gulp.task('copy', ['copy views', 'copy fonts', 'copy graphics', 'copy 3rdparty', 'copy robots.txt'])
|
|
||||||
gulp.task('deploy', ['default', 'copy', 'compile 3rdparty', 'imagemin'])
|
|
||||||
gulp.task('compile production', ['production', 'compile'])
|
|
||||||
+58
@@ -0,0 +1,58 @@
|
|||||||
|
const gulp = require('gulp')
|
||||||
|
const postcss = require('gulp-postcss')
|
||||||
|
const sass = require('gulp-sass')
|
||||||
|
const imageMin = require('gulp-imagemin')
|
||||||
|
const concat = require('gulp-concat')
|
||||||
|
const autoprefixer = require('autoprefixer')
|
||||||
|
const cssnano = require('cssnano')
|
||||||
|
|
||||||
|
gulp.task('style', () => {
|
||||||
|
const processors = [
|
||||||
|
autoprefixer({
|
||||||
|
browsers: ['last 1 version']
|
||||||
|
}),
|
||||||
|
cssnano({
|
||||||
|
zindex: false
|
||||||
|
})
|
||||||
|
]
|
||||||
|
|
||||||
|
return gulp.src('./src/style/style.scss')
|
||||||
|
.pipe(sass())
|
||||||
|
.pipe(postcss(processors))
|
||||||
|
.pipe(concat('style.css'))
|
||||||
|
.pipe(gulp.dest('./public'))
|
||||||
|
})
|
||||||
|
|
||||||
|
gulp.task('copy fonts', () => {
|
||||||
|
return gulp.src('./src/assets/fonts/*/*')
|
||||||
|
.pipe(gulp.dest('./public/fonts'))
|
||||||
|
})
|
||||||
|
|
||||||
|
gulp.task('copy graphics', () => {
|
||||||
|
return gulp.src('./src/assets/graphics/*')
|
||||||
|
.pipe(gulp.dest('./public/graphics'))
|
||||||
|
})
|
||||||
|
|
||||||
|
gulp.task('copy 3rdparty', () => {
|
||||||
|
return gulp.src('./src/3rdparty/*')
|
||||||
|
.pipe(gulp.dest('./public'))
|
||||||
|
})
|
||||||
|
|
||||||
|
gulp.task('copy robots.txt', () => {
|
||||||
|
return gulp.src('./robots.txt')
|
||||||
|
.pipe(gulp.dest('./public'))
|
||||||
|
})
|
||||||
|
|
||||||
|
gulp.task('copy views', () => {
|
||||||
|
return gulp.src('./src/views/**')
|
||||||
|
.pipe(gulp.dest('./out/views'))
|
||||||
|
})
|
||||||
|
|
||||||
|
gulp.task('imagemin', () => {
|
||||||
|
return gulp.src('./src/assets/graphics/**/*')
|
||||||
|
.pipe(imageMin())
|
||||||
|
.pipe(gulp.dest('./public/graphics'))
|
||||||
|
})
|
||||||
|
|
||||||
|
gulp.task('copy', gulp.series('copy views', 'copy fonts', 'copy graphics', 'copy robots.txt', 'copy 3rdparty'))
|
||||||
|
gulp.task('default', gulp.series('style', 'copy', 'imagemin'))
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
rsa-key-size = 4096
|
|
||||||
email = roman@tmshv.ru
|
|
||||||
domains = art.shlisselburg.org
|
|
||||||
webroot-path = /home/hoster/www/art.shburg.org/public
|
|
||||||
Generated
+11789
-4730
File diff suppressed because it is too large.
Load diff
+43
-16
@@ -6,6 +6,8 @@
|
|||||||
"main": "src/bin/www.js",
|
"main": "src/bin/www.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node src/index.js",
|
"start": "node src/index.js",
|
||||||
|
"build-edit": "webpack -w --env.NODE_ENV=production --env.TARGET=edit",
|
||||||
|
"build-app": "webpack --env.NODE_ENV=production --env.TARGET=app",
|
||||||
"out": "babel src/ -d out/",
|
"out": "babel src/ -d out/",
|
||||||
"gulp": "gulp",
|
"gulp": "gulp",
|
||||||
"test": "mocha --compilers js:babel-register",
|
"test": "mocha --compilers js:babel-register",
|
||||||
@@ -18,16 +20,22 @@
|
|||||||
"author": "Roman Timashev",
|
"author": "Roman Timashev",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@mikeljames/draft-js-drag-n-drop-upload-plugin": "^2.0.1",
|
||||||
"async-file": "^2.0.2",
|
"async-file": "^2.0.2",
|
||||||
"babel-core": "^6.7.4",
|
"classnames": "^2.2.5",
|
||||||
"babel-polyfill": "^6.7.4",
|
|
||||||
"babel-preset-es2015": "^6.5.0",
|
|
||||||
"babel-preset-stage-0": "^6.5.0",
|
|
||||||
"co": "^4.4.0",
|
"co": "^4.4.0",
|
||||||
|
"draft-js": "^0.10.5",
|
||||||
|
"draft-js-alignment-plugin": "^2.0.3",
|
||||||
|
"draft-js-drag-n-drop-plugin": "^2.0.3",
|
||||||
|
"draft-js-focus-plugin": "^2.0.4",
|
||||||
|
"draft-js-image-plugin": "^2.0.5",
|
||||||
|
"draft-js-plugins-editor": "^2.0.8",
|
||||||
|
"draft-js-resizeable-plugin": "^2.0.6",
|
||||||
"ejson": "^2.1.2",
|
"ejson": "^2.1.2",
|
||||||
"handlebars": "^4.0.5",
|
"handlebars": "^4.0.5",
|
||||||
|
"immutable": "^3.8.2",
|
||||||
"instagram-node": "^0.5.1",
|
"instagram-node": "^0.5.1",
|
||||||
"koa": "^2.0.0-alpha.3",
|
"koa": "^2.5.1",
|
||||||
"koa-bodyparser": "^3.0.0",
|
"koa-bodyparser": "^3.0.0",
|
||||||
"koa-compose": "^3.1.0",
|
"koa-compose": "^3.1.0",
|
||||||
"koa-conditional-get": "^1.0.2",
|
"koa-conditional-get": "^1.0.2",
|
||||||
@@ -37,46 +45,65 @@
|
|||||||
"koa-helmet": "^1.0.0",
|
"koa-helmet": "^1.0.0",
|
||||||
"koa-logger": "^2.0.0",
|
"koa-logger": "^2.0.0",
|
||||||
"koa-mount": "^2.0.0",
|
"koa-mount": "^2.0.0",
|
||||||
"koa-query": "0.0.2",
|
|
||||||
"koa-route": "^3.1.0",
|
"koa-route": "^3.1.0",
|
||||||
"koa-session": "^4.0.0",
|
"koa-session": "^4.0.0",
|
||||||
"koa-static": "^3.0.0",
|
"koa-static": "^3.0.0",
|
||||||
"moment": "^2.18.1",
|
"markdown": "^0.5.0",
|
||||||
|
"moment": "^2.22.2",
|
||||||
"mongodb": "^2.1.14",
|
"mongodb": "^2.1.14",
|
||||||
"mz": "^2.4.0",
|
"mz": "^2.4.0",
|
||||||
"react": "^15.3.2",
|
"react": "^16.3.2",
|
||||||
"react-dom": "^15.3.2",
|
"react-dom": "^16.3.2",
|
||||||
"react-redux": "^4.4.5",
|
"react-redux": "^4.4.8",
|
||||||
"redux": "^3.6.0",
|
"react-router": "^4.2.0",
|
||||||
|
"react-router-redux": "^4.0.8",
|
||||||
|
"redux": "^3.7.2",
|
||||||
|
"redux-thunk": "^2.2.0",
|
||||||
"request-promise": "^2.0.1",
|
"request-promise": "^2.0.1",
|
||||||
"sitemap": "^1.5.0",
|
"sitemap": "^1.5.0",
|
||||||
"thunkify": "^2.1.2"
|
"thunkify": "^2.1.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@babel/cli": "^7.0.0-beta.47",
|
||||||
|
"@babel/core": "^7.0.0-beta.47",
|
||||||
|
"@babel/plugin-proposal-class-properties": "^7.0.0-beta.47",
|
||||||
|
"@babel/polyfill": "^7.0.0-beta.47",
|
||||||
|
"@babel/preset-env": "^7.0.0-beta.47",
|
||||||
|
"@babel/register": "^7.0.0-beta.47",
|
||||||
"autoprefixer": "^6.3.6",
|
"autoprefixer": "^6.3.6",
|
||||||
"babel-cli": "^6.6.5",
|
"babel-loader": "^8.0.0-beta.3",
|
||||||
"babel-loader": "^6.2.2",
|
"babel-plugin-transform-class-properties": "^6.24.1",
|
||||||
|
"babel-plugin-transform-object-rest-spread": "^6.26.0",
|
||||||
"babel-plugin-transform-react-jsx": "^6.23.0",
|
"babel-plugin-transform-react-jsx": "^6.23.0",
|
||||||
|
"babel-plugin-transform-regenerator": "^6.26.0",
|
||||||
|
"babel-preset-env": "^1.7.0",
|
||||||
|
"babel-register": "^6.26.0",
|
||||||
|
"css-loader": "^0.28.9",
|
||||||
"cssnano": "^3.4.0",
|
"cssnano": "^3.4.0",
|
||||||
"eslint": "^3.9.1",
|
"eslint": "^3.9.1",
|
||||||
"eslint-config-standard": "^6.2.1",
|
"eslint-config-standard": "^6.2.1",
|
||||||
"eslint-plugin-promise": "^3.3.1",
|
"eslint-plugin-promise": "^3.3.1",
|
||||||
"eslint-plugin-react": "^7.4.0",
|
"eslint-plugin-react": "^7.4.0",
|
||||||
"eslint-plugin-standard": "^2.0.1",
|
"eslint-plugin-standard": "^2.0.1",
|
||||||
"gulp": "^3.8.10",
|
"gulp": "^4.0.0",
|
||||||
"gulp-concat": "^2.4.1",
|
"gulp-concat": "^2.4.1",
|
||||||
"gulp-if": "^2.0.0",
|
"gulp-if": "^2.0.0",
|
||||||
"gulp-imagemin": "~2.4.0",
|
"gulp-imagemin": "~2.4.0",
|
||||||
"gulp-postcss": "^6.0.1",
|
"gulp-postcss": "^6.0.1",
|
||||||
"gulp-rename": "^1.2.2",
|
"gulp-rename": "^1.2.2",
|
||||||
"gulp-sass": "^3.1.0",
|
"gulp-sass": "^4.0.1",
|
||||||
"gulp-uglify": "~1.5.0",
|
"gulp-uglify": "~1.5.0",
|
||||||
"html-minify-loader": "~1.1.0",
|
"html-minify-loader": "~1.1.0",
|
||||||
|
"html-webpack-plugin": "^2.30.1",
|
||||||
"istanbul": "~0.4.2",
|
"istanbul": "~0.4.2",
|
||||||
|
"less": "^3.0.4",
|
||||||
|
"less-loader": "^4.1.0",
|
||||||
"mocha": "~2.4.5",
|
"mocha": "~2.4.5",
|
||||||
"raw-loader": "~0.5.1",
|
"raw-loader": "~0.5.1",
|
||||||
"should": "^8.2.2",
|
"should": "^8.2.2",
|
||||||
|
"style-loader": "^0.20.2",
|
||||||
"vinyl-named": "~1.1.0",
|
"vinyl-named": "~1.1.0",
|
||||||
"webpack-stream": "~3.1.0"
|
"webpack": "^3.11.0",
|
||||||
|
"webpack-merge": "^4.1.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
const React = require('react')
|
const React = require('react')
|
||||||
|
|
||||||
const mailto = email => `mailto:${email}`
|
const mailto = email => `mailto:${email}`
|
||||||
|
const currentYear = () => (new Date()).getFullYear()
|
||||||
|
|
||||||
const Footer = ({children, address, telephone, email, showAuthor}) => (
|
const Footer = ({children, address, telephone, email, showAuthor}) => (
|
||||||
<footer>
|
<footer>
|
||||||
@@ -16,16 +17,28 @@ const Footer = ({children, address, telephone, email, showAuthor}) => (
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="copyright">
|
<div className="copyright">
|
||||||
<span>© 2012—2017 Шлиссельбургская детская художественная школа</span>
|
<Copyright
|
||||||
|
yearStart={2012}
|
||||||
|
yearEnd={currentYear()}
|
||||||
|
/>
|
||||||
{!showAuthor ? null : (
|
{!showAuthor ? null : (
|
||||||
<span>Разработка и поддержка сайта —
|
<Author/>
|
||||||
<a href="http://tmshv.ru?utm_source=artshburg&utm_medium=developer&utm_campaign=tmshv_ru">
|
|
||||||
www.tmshv.ru
|
|
||||||
</a>
|
|
||||||
</span>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const Author = () => (
|
||||||
|
<span>
|
||||||
|
Разработка и поддержка сайта —
|
||||||
|
<a href="http://tmshv.ru?utm_source=artshburg&utm_medium=developer&utm_campaign=tmshv_ru">
|
||||||
|
www.tmshv.ru
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
|
||||||
|
const Copyright = ({yearStart, yearEnd}) => (
|
||||||
|
<span>© {yearStart}—{yearEnd} Шлиссельбургская детская художественная школа</span>
|
||||||
|
)
|
||||||
|
|
||||||
module.exports = Footer
|
module.exports = Footer
|
||||||
+2
-1
@@ -1,7 +1,7 @@
|
|||||||
import path from 'path'
|
import path from 'path'
|
||||||
|
|
||||||
export const port = process.env['PORT'] || 3000
|
export const port = process.env['PORT'] || 3000
|
||||||
export const dbUri = process.env['DB_URI'] || 'mongodb://localhost:27017/hudozka'
|
export const dbUri = process.env['DB_URI'] || 'mongodb://mongo/hudozka'
|
||||||
|
|
||||||
export const name = 'Hudozka'
|
export const name = 'Hudozka'
|
||||||
export const host = 'art.shlisselburg.org'
|
export const host = 'art.shlisselburg.org'
|
||||||
@@ -12,6 +12,7 @@ export const index = path.join(__dirname, 'templates/main.html')
|
|||||||
export const sitemapCacheTime = 600000// 600 sec - cache purge period
|
export const sitemapCacheTime = 600000// 600 sec - cache purge period
|
||||||
|
|
||||||
export const viewMain = path.join(__dirname, 'views/main.hbs')
|
export const viewMain = path.join(__dirname, 'views/main.hbs')
|
||||||
|
export const viewEdit = path.join(__dirname, 'views/edit.hbs')
|
||||||
export const view404 = path.join(__dirname, 'views/404.hbs')
|
export const view404 = path.join(__dirname, 'views/404.hbs')
|
||||||
|
|
||||||
export const sessionConfig = {
|
export const sessionConfig = {
|
||||||
|
|||||||
@@ -0,0 +1,90 @@
|
|||||||
|
// import 'babel-core/register'
|
||||||
|
// import 'babel-polyfill'
|
||||||
|
|
||||||
|
import {render} from 'react-dom'
|
||||||
|
import getEditApp from './edit'
|
||||||
|
import TextToken from './edit/features/Marker/lib/marker/TextToken'
|
||||||
|
import {Marker} from './edit/features/Marker/lib/marker/Marker'
|
||||||
|
import SplitToken from './edit/features/Marker/lib/marker/SplitToken'
|
||||||
|
import YoutubeToken from './edit/features/Marker/lib/marker/YoutubeToken'
|
||||||
|
import {TokenFactory} from './edit/features/Marker/lib/marker/TokenFactory'
|
||||||
|
import React from 'react'
|
||||||
|
import UrlToken from './edit/features/Marker/lib/marker/UrlToken'
|
||||||
|
|
||||||
|
function getMarker() {
|
||||||
|
return (new Marker())
|
||||||
|
.addTokenFactory(new TokenFactory({token: SplitToken}))
|
||||||
|
.addTokenFactory(new TokenFactory({
|
||||||
|
token: YoutubeToken,
|
||||||
|
component: ({url}) => (
|
||||||
|
<div
|
||||||
|
className='MarkerWidgetYoutube'
|
||||||
|
>
|
||||||
|
<a href={url}>
|
||||||
|
{url}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
// component: ({url}) => (
|
||||||
|
// <div
|
||||||
|
// className='MarkerWidgetYoutube'
|
||||||
|
// >
|
||||||
|
// <div className='kazimir__video'>
|
||||||
|
// <iframe
|
||||||
|
// src={url}
|
||||||
|
// frameBorder='0'
|
||||||
|
// allowFullScreen
|
||||||
|
// />
|
||||||
|
// </div>
|
||||||
|
// </div>
|
||||||
|
// )
|
||||||
|
}))
|
||||||
|
// .addTokenFactory(new TokenFactory({token: InstagramToken}))
|
||||||
|
// .addTokenFactory(BuildTokenFactory(DocumentUrlToken, build=read_document))
|
||||||
|
// .addTokenFactory(new TokenFactory({token: ImageToken}))
|
||||||
|
// .addTokenFactory(BuildTokenFactory(DocumentToken, build=build_document))
|
||||||
|
// .addTokenFactory(BuildTokenFactory(CSVToken, build=read_csv))
|
||||||
|
// .addTokenFactory(new TokenFactory({token: FileToken}))
|
||||||
|
.addTokenFactory(new TokenFactory({
|
||||||
|
token: TextToken,
|
||||||
|
component: ({html}) => (
|
||||||
|
<div
|
||||||
|
className='MarkerWidgetHtml'
|
||||||
|
dangerouslySetInnerHTML={{__html: html}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}), {
|
||||||
|
blank: true,
|
||||||
|
})
|
||||||
|
// .add_tree_middleware(fix_links_quotes)
|
||||||
|
|
||||||
|
// sample_tree = await marker.create_tree(sample_text)
|
||||||
|
}
|
||||||
|
|
||||||
|
function getData() {
|
||||||
|
return `
|
||||||
|
# Edit
|
||||||
|
https://www.youtube.com/watch?v=pxtCffGya9g
|
||||||
|
|
||||||
|
Text row
|
||||||
|
`
|
||||||
|
// return [
|
||||||
|
//
|
||||||
|
// 'bla-bla',
|
||||||
|
// '### pop-pop',
|
||||||
|
// 'uiu',
|
||||||
|
// 'Url: [ya](http://ya.ru)',
|
||||||
|
// ]
|
||||||
|
}
|
||||||
|
|
||||||
|
function main() {
|
||||||
|
const mount = document.querySelector('#app')
|
||||||
|
const marker = getMarker()
|
||||||
|
const data = getData()
|
||||||
|
const app = getEditApp(marker, data)
|
||||||
|
|
||||||
|
render(app, mount)
|
||||||
|
}
|
||||||
|
|
||||||
|
main()
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export default function effects() {
|
||||||
|
return []
|
||||||
|
}
|
||||||
@@ -0,0 +1,141 @@
|
|||||||
|
import React, {Component} from 'react'
|
||||||
|
import Marker from '../Marker'
|
||||||
|
import './App.less'
|
||||||
|
import {MarkerWidgetHtml, MarkerWidgetText, MarkerWidgetUrl} from '../Marker/MarkerWidget'
|
||||||
|
import ContentEditable from '../Marker/ContentEditable'
|
||||||
|
import Editor from '../Editor/'
|
||||||
|
|
||||||
|
// import Map from '../Map'
|
||||||
|
// import Body from '../Body'
|
||||||
|
// import AppWrapper from './AppWrapper'
|
||||||
|
//
|
||||||
|
// import {bindActionCreators} from 'redux'
|
||||||
|
// import {connect} from 'react-redux'
|
||||||
|
// import * as actions from './duck'
|
||||||
|
//
|
||||||
|
// import {routerActions} from 'react-router-redux'
|
||||||
|
// import {title as defaultTitle} from '../../../config'
|
||||||
|
// import {getVariable} from '../../../lib/style'
|
||||||
|
// import {debounce} from '../../../lib/fn'
|
||||||
|
// const hit = debounce(50, path => ym('hit', path))
|
||||||
|
//
|
||||||
|
// const projectPath = project => `/projects/${project.name}`
|
||||||
|
// const frameSize = isVertical => [window.innerWidth, window.innerHeight]
|
||||||
|
// const frameSizeMultiplier = () => getVariable('--content-width-state-normal')
|
||||||
|
// const landscapeProjectBounds = (width, height, ratio=frameSizeMultiplier()) => [
|
||||||
|
// [0, 0],
|
||||||
|
// [width * ratio, 0],
|
||||||
|
// ]
|
||||||
|
// const portraitProjectBounds = (width, height, ratio=frameSizeMultiplier()) => [
|
||||||
|
// [0, 0],
|
||||||
|
// [0, width * ratio],
|
||||||
|
// ]
|
||||||
|
//
|
||||||
|
// function mapStateToProps(state) {
|
||||||
|
// const {fullPage, isVertical, halfPageAvailable} = state.app.page
|
||||||
|
//
|
||||||
|
// return {
|
||||||
|
// projects: state.app.projects.items,
|
||||||
|
// activeProject: state.app.projects.activeProject,
|
||||||
|
// fullPage: halfPageAvailable
|
||||||
|
// ? fullPage
|
||||||
|
// : true,
|
||||||
|
// showFullPageToggle: halfPageAvailable,
|
||||||
|
// isVertical,
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// function mapDispatchToProps(dispatch) {
|
||||||
|
// return bindActionCreators({
|
||||||
|
// ...actions,
|
||||||
|
// push: routerActions.push
|
||||||
|
// }, dispatch)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// async function build_document(data) {
|
||||||
|
// // text = data['caption'] if data['caption'] else data['file']
|
||||||
|
// // # document = await self.build()
|
||||||
|
// document = {
|
||||||
|
// 'url': data['file'],
|
||||||
|
// 'image_url': data['file'],
|
||||||
|
// 'file_url': data['file'],
|
||||||
|
// 'title': text,
|
||||||
|
// 'file_size': '0 KB',
|
||||||
|
// 'file_format': 'PDF',
|
||||||
|
// }
|
||||||
|
// return document
|
||||||
|
// }
|
||||||
|
|
||||||
|
// async function read_csv(data) {
|
||||||
|
// return '\n'.join([
|
||||||
|
// 'N,Name',
|
||||||
|
// `0,${data}`,
|
||||||
|
// '1,Pop',
|
||||||
|
// '2,Top',
|
||||||
|
// ])
|
||||||
|
// }
|
||||||
|
|
||||||
|
// async function read_document(data) {
|
||||||
|
// const doc = await Document.find_one({'id': data})
|
||||||
|
// return {
|
||||||
|
// 'url': doc['url'],
|
||||||
|
// 'image_url': doc['url'],
|
||||||
|
// 'file_url': doc['url'],
|
||||||
|
// 'title': doc['title'],
|
||||||
|
// 'file_size': '0 KB',
|
||||||
|
// 'file_format': 'PDF',
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
export default class App extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
|
||||||
|
this.onMarkerUpdate = this.onMarkerUpdate.bind(this)
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
tokens: [],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMarkerUpdate(tokens) {
|
||||||
|
// console.log('Update')
|
||||||
|
// console.log(tokens)
|
||||||
|
|
||||||
|
this.setState({tokens})
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillMount() {
|
||||||
|
const {data, marker} = this.props
|
||||||
|
|
||||||
|
marker.evaluate(data)
|
||||||
|
.then(tokens => {
|
||||||
|
this.setState({
|
||||||
|
tokens,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// this.setState({
|
||||||
|
// data: this
|
||||||
|
// .getData()
|
||||||
|
// .reduce((acc, x) => [...acc, ...x], []),
|
||||||
|
// })
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className="App">
|
||||||
|
<div className={'App-Body'}>
|
||||||
|
<Editor/>
|
||||||
|
{/* <Marker */}
|
||||||
|
{/* data={this.state.tokens} */}
|
||||||
|
{/* marker={this.props.marker} */}
|
||||||
|
{/* onUpdate={this.onMarkerUpdate} */}
|
||||||
|
{/* /> */}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// export default connect(mapStateToProps, mapDispatchToProps)(App)
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
.App {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
&-Body {
|
||||||
|
padding-top: 10px;
|
||||||
|
width: 800px;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import ReactDOM from 'react-dom';
|
||||||
|
import App from './App';
|
||||||
|
|
||||||
|
it('renders without crashing', () => {
|
||||||
|
const div = document.createElement('div');
|
||||||
|
ReactDOM.render(<App />, div);
|
||||||
|
});
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
.AppWrapper {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
|
||||||
|
z-index: 1000;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: flex-start; /* align items in Main Axis */
|
||||||
|
align-items: stretch; /* align items in Cross Axis */
|
||||||
|
align-content: stretch; /* Extra space in Cross Axis */
|
||||||
|
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.AppWrapper footer {
|
||||||
|
background-color: rgba(34, 34, 34, 0.95);
|
||||||
|
color: #ebebeb;
|
||||||
|
|
||||||
|
padding: 1em 0.5em 0.5em;
|
||||||
|
font-weight: 100;
|
||||||
|
}
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
import React, {Component} from 'react'
|
||||||
|
import {Link} from 'react-router'
|
||||||
|
|
||||||
|
import './AppWrapper.css'
|
||||||
|
import './AppWrapperHeader.css'
|
||||||
|
import './AppWrapperBody.css'
|
||||||
|
import './AppWrapperContent.css'
|
||||||
|
import './Icon.css'
|
||||||
|
|
||||||
|
const Icon = ({children: iconName}) => (
|
||||||
|
<span className={`Icon ${iconName}`}/>
|
||||||
|
)
|
||||||
|
|
||||||
|
class Toggle extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
this.state = {
|
||||||
|
value: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onClick = () => {
|
||||||
|
const {value} = this.state
|
||||||
|
const {onClick} = this.props
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
value: !value,
|
||||||
|
})
|
||||||
|
onClick(!value)
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {value} = this.state
|
||||||
|
const icon = value
|
||||||
|
? 'IconFullScreenOn'
|
||||||
|
: 'IconFullScreenOff'
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button className="InvisibleButton" onClick={this.onClick}>
|
||||||
|
<Icon>{icon}</Icon>
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const Head = ({title, onToggleFullPage, showFullPageToggle, showControls = false}) => (
|
||||||
|
<header className="AppWrapperHeader">
|
||||||
|
<div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="AppWrapperHeader-main">
|
||||||
|
<h1>{title}</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
{!showControls ? null : (
|
||||||
|
<div className="controls">
|
||||||
|
{!showFullPageToggle ? null: (
|
||||||
|
<Toggle onClick={onToggleFullPage}/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
)
|
||||||
|
|
||||||
|
const Body = ({children}) => (
|
||||||
|
<div className="AppWrapperBody">
|
||||||
|
<div className="AppWrapperContent">
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
const AppWrapper = ({title, children, showControls, showFullPageToggle, onToggleFullPage}) => (
|
||||||
|
<div className="AppWrapper">
|
||||||
|
<Head
|
||||||
|
title={title}
|
||||||
|
showControls={showControls}
|
||||||
|
onToggleFullPage={onToggleFullPage}
|
||||||
|
showFullPageToggle={showFullPageToggle}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Body>{children}</Body>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default AppWrapper
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
.AppWrapperBody {
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
.AppWrapperContent {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
/* Needed for when the area gets squished too far and there is content
|
||||||
|
that can't be displayed */
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
.AppWrapperHeader {
|
||||||
|
background-color: rgba(34, 34, 34, 0.95);
|
||||||
|
color: #ebebeb;
|
||||||
|
|
||||||
|
/*background-color: rgba(255, 255, 255, 0.95);*/
|
||||||
|
/*color: #222;*/
|
||||||
|
|
||||||
|
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.15);
|
||||||
|
|
||||||
|
pointer-events: auto;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.AppWrapperHeader-main {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.AppWrapperHeader h1 {
|
||||||
|
text-align: center;
|
||||||
|
margin: 0;
|
||||||
|
padding: .5em 0 .4em;
|
||||||
|
font-size: 1.25rem;
|
||||||
|
font-weight: 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
.controls {
|
||||||
|
margin: 0 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.controls a{
|
||||||
|
color: #bbb;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.controls a:hover{
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
.Icon {
|
||||||
|
display: inline-block;
|
||||||
|
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
|
||||||
|
/*background: grey;*/
|
||||||
|
background-size: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.IconFullScreenOn {
|
||||||
|
background-image: url('/icon-fullscreen-on.svg');
|
||||||
|
}
|
||||||
|
|
||||||
|
.IconFullScreenOff {
|
||||||
|
background-image: url('/icon-fullscreen-off.svg');
|
||||||
|
}
|
||||||
|
|
||||||
|
.InvisibleButton {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.InvisibleButton {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
padding: 0;
|
||||||
|
border-radius: 0;
|
||||||
|
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*.InvisibleButton:focus {*/
|
||||||
|
/*border: 2px solid #2f9fff;*/
|
||||||
|
/*}*/
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
import {combineReducers} from 'redux'
|
||||||
|
import api from '../../../api'
|
||||||
|
import {pack} from '../../../lib/fn'
|
||||||
|
import {isMobile, isLandscape} from '../../../lib/device'
|
||||||
|
|
||||||
|
const FETCH_PROJECTS = 'arch/app/FETCH_PROJECTS'
|
||||||
|
const FOCUS_PROJECT = 'arch/app/FOCUS_PROJECT'
|
||||||
|
const TOGGLE_FULL_PAGE = 'arch/app/TOGGLE_FULL_PAGE'
|
||||||
|
const CHANGE_PAGE_ORIENTATION = 'arch/app/CHANGE_PAGE_ORIENTATION'
|
||||||
|
|
||||||
|
export const PAGE_ORIENTATION_LANDSCAPE = 'landscape'
|
||||||
|
export const PAGE_ORIENTATION_PORTRAIT = 'portrait'
|
||||||
|
|
||||||
|
export function fetchProjects() {
|
||||||
|
return {
|
||||||
|
type: FETCH_PROJECTS,
|
||||||
|
async: api.projects.fetch()
|
||||||
|
.then(pack('data'))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function focusProject(projectName) {
|
||||||
|
return {
|
||||||
|
type: FOCUS_PROJECT,
|
||||||
|
projectName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function toggleFullPage() {
|
||||||
|
return {
|
||||||
|
type: TOGGLE_FULL_PAGE,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function changePageOrientation(orientation) {
|
||||||
|
return {
|
||||||
|
type: CHANGE_PAGE_ORIENTATION,
|
||||||
|
orientation,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const init = {
|
||||||
|
items: [],
|
||||||
|
total: 0,
|
||||||
|
activeProject: null,
|
||||||
|
}
|
||||||
|
|
||||||
|
const initPage = {
|
||||||
|
fullPage: false,
|
||||||
|
halfPageAvailable: !isMobile(),
|
||||||
|
isVertical: !isLandscape(),
|
||||||
|
}
|
||||||
|
|
||||||
|
function projects(state = init, action) {
|
||||||
|
switch (action.type) {
|
||||||
|
case FETCH_PROJECTS: {
|
||||||
|
const {items, total} = action.data
|
||||||
|
return {...state, items, total}
|
||||||
|
}
|
||||||
|
|
||||||
|
case FOCUS_PROJECT: {
|
||||||
|
const projectName = action.projectName
|
||||||
|
const activeProject = state.items.find(p => p.name === projectName)
|
||||||
|
|
||||||
|
return {...state, activeProject}
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function page(state = initPage, action) {
|
||||||
|
switch (action.type) {
|
||||||
|
case TOGGLE_FULL_PAGE: {
|
||||||
|
const fullPage = !state.fullPage
|
||||||
|
|
||||||
|
return {...state, fullPage}
|
||||||
|
}
|
||||||
|
|
||||||
|
case CHANGE_PAGE_ORIENTATION: {
|
||||||
|
const {orientation} = action
|
||||||
|
const isVertical = orientation === PAGE_ORIENTATION_PORTRAIT
|
||||||
|
|
||||||
|
return {...state, isVertical}
|
||||||
|
}
|
||||||
|
|
||||||
|
case FOCUS_PROJECT: {
|
||||||
|
let fullPage = state.fullPage
|
||||||
|
|
||||||
|
const projectName = action.projectName
|
||||||
|
fullPage = !projectName
|
||||||
|
? false
|
||||||
|
: fullPage
|
||||||
|
|
||||||
|
return {...state, fullPage}
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default combineReducers({
|
||||||
|
projects,
|
||||||
|
page,
|
||||||
|
})
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export {default} from './App'
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
/**
|
||||||
|
* Draft v0.10.5
|
||||||
|
*
|
||||||
|
* Copyright (c) 2013-present, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*/
|
||||||
|
.DraftEditor-editorContainer,.DraftEditor-root,.public-DraftEditor-content{height:inherit;text-align:initial}.public-DraftEditor-content[contenteditable=true]{-webkit-user-modify:read-write-plaintext-only}.DraftEditor-root{position:relative}.DraftEditor-editorContainer{background-color:rgba(255,255,255,0);border-left:.1px solid transparent;position:relative;z-index:1}.public-DraftEditor-block{position:relative}.DraftEditor-alignLeft .public-DraftStyleDefault-block{text-align:left}.DraftEditor-alignLeft .public-DraftEditorPlaceholder-root{left:0;text-align:left}.DraftEditor-alignCenter .public-DraftStyleDefault-block{text-align:center}.DraftEditor-alignCenter .public-DraftEditorPlaceholder-root{margin:0 auto;text-align:center;width:100%}.DraftEditor-alignRight .public-DraftStyleDefault-block{text-align:right}.DraftEditor-alignRight .public-DraftEditorPlaceholder-root{right:0;text-align:right}.public-DraftEditorPlaceholder-root{color:#9197a3;position:absolute;z-index:1}.public-DraftEditorPlaceholder-hasFocus{color:#bdc1c9}.DraftEditorPlaceholder-hidden{display:none}.public-DraftStyleDefault-block{position:relative;white-space:pre-wrap}.public-DraftStyleDefault-ltr{direction:ltr;text-align:left}.public-DraftStyleDefault-rtl{direction:rtl;text-align:right}.public-DraftStyleDefault-listLTR{direction:ltr}.public-DraftStyleDefault-listRTL{direction:rtl}.public-DraftStyleDefault-ol,.public-DraftStyleDefault-ul{margin:16px 0;padding:0}.public-DraftStyleDefault-depth0.public-DraftStyleDefault-listLTR{margin-left:1.5em}.public-DraftStyleDefault-depth0.public-DraftStyleDefault-listRTL{margin-right:1.5em}.public-DraftStyleDefault-depth1.public-DraftStyleDefault-listLTR{margin-left:3em}.public-DraftStyleDefault-depth1.public-DraftStyleDefault-listRTL{margin-right:3em}.public-DraftStyleDefault-depth2.public-DraftStyleDefault-listLTR{margin-left:4.5em}.public-DraftStyleDefault-depth2.public-DraftStyleDefault-listRTL{margin-right:4.5em}.public-DraftStyleDefault-depth3.public-DraftStyleDefault-listLTR{margin-left:6em}.public-DraftStyleDefault-depth3.public-DraftStyleDefault-listRTL{margin-right:6em}.public-DraftStyleDefault-depth4.public-DraftStyleDefault-listLTR{margin-left:7.5em}.public-DraftStyleDefault-depth4.public-DraftStyleDefault-listRTL{margin-right:7.5em}.public-DraftStyleDefault-unorderedListItem{list-style-type:square;position:relative}.public-DraftStyleDefault-unorderedListItem.public-DraftStyleDefault-depth0{list-style-type:disc}.public-DraftStyleDefault-unorderedListItem.public-DraftStyleDefault-depth1{list-style-type:circle}.public-DraftStyleDefault-orderedListItem{list-style-type:none;position:relative}.public-DraftStyleDefault-orderedListItem.public-DraftStyleDefault-listLTR:before{left:-36px;position:absolute;text-align:right;width:30px}.public-DraftStyleDefault-orderedListItem.public-DraftStyleDefault-listRTL:before{position:absolute;right:-36px;text-align:left;width:30px}.public-DraftStyleDefault-orderedListItem:before{content:counter(ol0) ". ";counter-increment:ol0}.public-DraftStyleDefault-orderedListItem.public-DraftStyleDefault-depth1:before{content:counter(ol1) ". ";counter-increment:ol1}.public-DraftStyleDefault-orderedListItem.public-DraftStyleDefault-depth2:before{content:counter(ol2) ". ";counter-increment:ol2}.public-DraftStyleDefault-orderedListItem.public-DraftStyleDefault-depth3:before{content:counter(ol3) ". ";counter-increment:ol3}.public-DraftStyleDefault-orderedListItem.public-DraftStyleDefault-depth4:before{content:counter(ol4) ". ";counter-increment:ol4}.public-DraftStyleDefault-depth0.public-DraftStyleDefault-reset{counter-reset:ol0}.public-DraftStyleDefault-depth1.public-DraftStyleDefault-reset{counter-reset:ol1}.public-DraftStyleDefault-depth2.public-DraftStyleDefault-reset{counter-reset:ol2}.public-DraftStyleDefault-depth3.public-DraftStyleDefault-reset{counter-reset:ol3}.public-DraftStyleDefault-depth4.public-DraftStyleDefault-reset{counter-reset:ol4}
|
||||||
@@ -0,0 +1,313 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import Immutable from 'immutable'
|
||||||
|
import {
|
||||||
|
DefaultDraftBlockRenderMap,
|
||||||
|
EditorState,
|
||||||
|
RichUtils,
|
||||||
|
convertFromRaw,
|
||||||
|
getDefaultKeyBinding,
|
||||||
|
AtomicBlockUtils,
|
||||||
|
Modifier
|
||||||
|
} from 'draft-js'
|
||||||
|
import DraftEditor, {composeDecorators} from 'draft-js-plugins-editor'
|
||||||
|
|
||||||
|
import createImagePlugin from 'draft-js-image-plugin'
|
||||||
|
import createAlignmentPlugin from 'draft-js-alignment-plugin'
|
||||||
|
import createFocusPlugin from 'draft-js-focus-plugin'
|
||||||
|
import createResizeablePlugin from 'draft-js-resizeable-plugin'
|
||||||
|
import createBlockDndPlugin from 'draft-js-drag-n-drop-plugin'
|
||||||
|
// import createDragNDropUploadPlugin from 'draft-js-drag-n-drop-upload-plugin'
|
||||||
|
import createDndFileUploadPlugin from '@mikeljames/draft-js-drag-n-drop-upload-plugin'
|
||||||
|
|
||||||
|
// import './Draft.less'
|
||||||
|
import 'draft-js/dist/Draft.css'
|
||||||
|
import 'draft-js-image-plugin/lib/plugin.css'
|
||||||
|
|
||||||
|
const focusPlugin = createFocusPlugin()
|
||||||
|
const resizeablePlugin = createResizeablePlugin()
|
||||||
|
const blockDndPlugin = createBlockDndPlugin()
|
||||||
|
const alignmentPlugin = createAlignmentPlugin()
|
||||||
|
const {AlignmentTool} = alignmentPlugin
|
||||||
|
|
||||||
|
const decorator = composeDecorators(
|
||||||
|
resizeablePlugin.decorator,
|
||||||
|
alignmentPlugin.decorator,
|
||||||
|
focusPlugin.decorator,
|
||||||
|
blockDndPlugin.decorator
|
||||||
|
)
|
||||||
|
const imagePlugin = createImagePlugin({decorator})
|
||||||
|
|
||||||
|
const dndFileUploadPlugin = createDndFileUploadPlugin({
|
||||||
|
handleUpload: console.log,
|
||||||
|
addImage: imagePlugin.addImage,
|
||||||
|
})
|
||||||
|
// const dragNDropFileUploadPlugin = createDragNDropUploadPlugin({
|
||||||
|
// handleUpload: mockUpload,
|
||||||
|
// addImage: imagePlugin.addImage,
|
||||||
|
// })
|
||||||
|
|
||||||
|
const plugins = [
|
||||||
|
dndFileUploadPlugin,
|
||||||
|
blockDndPlugin,
|
||||||
|
focusPlugin,
|
||||||
|
alignmentPlugin,
|
||||||
|
resizeablePlugin,
|
||||||
|
imagePlugin
|
||||||
|
]
|
||||||
|
|
||||||
|
const initialState = {
|
||||||
|
'entityMap': {
|
||||||
|
'0': {
|
||||||
|
'type': 'IMAGE',
|
||||||
|
'mutability': 'IMMUTABLE',
|
||||||
|
'data': {
|
||||||
|
'src': 'https://static.shlisselburg.org/art/images/7c206b4e211bb8486801fd84a3bf309b-big.jpg'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'blocks': [
|
||||||
|
{
|
||||||
|
'key': '9gm3s',
|
||||||
|
'text': 'You can have images in your text field. This is a very rudimentary example, but you can enhance the image plugin with resizing, focus or alignment plugins.',
|
||||||
|
'type': 'unstyled',
|
||||||
|
'depth': 0,
|
||||||
|
'inlineStyleRanges': [],
|
||||||
|
'entityRanges': [],
|
||||||
|
'data': {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'key': 'ov7r',
|
||||||
|
'text': ' ',
|
||||||
|
'type': 'atomic',
|
||||||
|
'depth': 0,
|
||||||
|
'inlineStyleRanges': [],
|
||||||
|
'entityRanges': [{
|
||||||
|
'offset': 0,
|
||||||
|
'length': 1,
|
||||||
|
'key': 0
|
||||||
|
}],
|
||||||
|
'data': {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'key': 'e23a8',
|
||||||
|
'text': 'See advanced examples further down …',
|
||||||
|
'type': 'unstyled',
|
||||||
|
'depth': 0,
|
||||||
|
'inlineStyleRanges': [],
|
||||||
|
'entityRanges': [],
|
||||||
|
'data': {}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyCustomBlock extends React.Component {
|
||||||
|
// constructor(props) {
|
||||||
|
// super(props)
|
||||||
|
// }
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className='MyCustomBlock'>
|
||||||
|
{/* here, this.props.children contains a <section> container, as that was the matching element */}
|
||||||
|
{this.props.children}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MediaComponent extends React.Component {
|
||||||
|
render() {
|
||||||
|
const {block, contentState} = this.props
|
||||||
|
const {foo} = this.props.blockProps
|
||||||
|
const data = contentState.getEntity(block.getEntityAt(0)).getData()
|
||||||
|
|
||||||
|
console.log(data)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<i>
|
||||||
|
{data.text}
|
||||||
|
<img src={data.text} alt=""/>
|
||||||
|
</i>
|
||||||
|
)
|
||||||
|
|
||||||
|
// Return a <figure> or some other content using this data.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function blockRendererFn(contentBlock) {
|
||||||
|
const type = contentBlock.getType()
|
||||||
|
|
||||||
|
if (type === 'atomic') {
|
||||||
|
console.log('blockRendererFn', contentBlock.getKey(), type)
|
||||||
|
|
||||||
|
return {
|
||||||
|
component: MediaComponent,
|
||||||
|
editable: false,
|
||||||
|
props: {
|
||||||
|
foo: 'bar',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const blockRenderMap = Immutable.Map({
|
||||||
|
'section': {
|
||||||
|
element: 'section',
|
||||||
|
wrapper: MyCustomBlock,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Include 'paragraph' as a valid block and updated the unstyled element but
|
||||||
|
// keep support for other draft default block types
|
||||||
|
const extendedBlockRenderMap = DefaultDraftBlockRenderMap.merge(blockRenderMap)
|
||||||
|
|
||||||
|
export default class Editor extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
editorState: EditorState.createWithContent(convertFromRaw(initialState)),
|
||||||
|
}
|
||||||
|
this.onChange = this.onChange.bind(this)
|
||||||
|
this.onBoldClick = () => {
|
||||||
|
this.onChange(RichUtils.toggleInlineStyle(this.state.editorState, 'BOLD'))
|
||||||
|
}
|
||||||
|
this.focus = () => {
|
||||||
|
this.editor.focus()
|
||||||
|
}
|
||||||
|
|
||||||
|
this.onZomboClick = () => {
|
||||||
|
const contentState = this.state.editorState.getCurrentContent();
|
||||||
|
const contentStateWithEntity = contentState.createEntity(
|
||||||
|
'LINK',
|
||||||
|
'MUTABLE',
|
||||||
|
{url: 'http://www.zombo.com'}
|
||||||
|
)
|
||||||
|
const entityKey = contentStateWithEntity.getLastCreatedEntityKey();
|
||||||
|
const contentStateWithLink = Modifier.applyEntity(
|
||||||
|
contentStateWithEntity,
|
||||||
|
selectionState,
|
||||||
|
entityKey
|
||||||
|
)
|
||||||
|
this.onChange(contentStateWithLink);
|
||||||
|
|
||||||
|
// this.onChange(RichUtils.toggleInlineStyle(this.state.editorState, 'BOLD'))
|
||||||
|
}
|
||||||
|
|
||||||
|
this.handleKeyCommand = this.handleKeyCommand.bind(this)
|
||||||
|
this.keyBindingFn = this.keyBindingFn.bind(this)
|
||||||
|
this.insertBlock = this.insertBlock.bind(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
insertBlock(type, data) {
|
||||||
|
const {editorState} = this.state
|
||||||
|
const contentState = editorState.getCurrentContent()
|
||||||
|
const contentStateWithEntity = contentState.createEntity(type, 'IMMUTABLE', data)
|
||||||
|
const entityKey = contentStateWithEntity.getLastCreatedEntityKey()
|
||||||
|
|
||||||
|
const newEditorState = EditorState.set(editorState, {
|
||||||
|
currentContent: contentStateWithEntity
|
||||||
|
})
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
editorState: AtomicBlockUtils.insertAtomicBlock(newEditorState, entityKey, ' '),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
onChange(editorState) {
|
||||||
|
const content = editorState.getCurrentContent()
|
||||||
|
const s = editorState.getSelection()
|
||||||
|
|
||||||
|
const key = s.getStartKey()
|
||||||
|
const prevKey = content.getKeyBefore(key)
|
||||||
|
|
||||||
|
const block = content.getBlockForKey(key)
|
||||||
|
const prevBlock = content.getBlockForKey(prevKey)
|
||||||
|
|
||||||
|
const text = block.getText()
|
||||||
|
if (/\.jpg/.test(text)) {
|
||||||
|
this.insertBlock('jpg', {text})
|
||||||
|
// const newContent = Modifier.setBlockType(content, s, 'atomic')
|
||||||
|
//
|
||||||
|
// const newEditorState = EditorState.set(editorState, {
|
||||||
|
// currentContent: newContent,
|
||||||
|
// })
|
||||||
|
//
|
||||||
|
// this.setState({
|
||||||
|
// editorState: newEditorState,
|
||||||
|
// })
|
||||||
|
} else {
|
||||||
|
this.setState({editorState})
|
||||||
|
}
|
||||||
|
|
||||||
|
// const blockData = block.getData()
|
||||||
|
|
||||||
|
// console.log(block.getText(), block.getType())
|
||||||
|
|
||||||
|
// console.log(block.getEntityAt(0))
|
||||||
|
}
|
||||||
|
|
||||||
|
keyBindingFn(e) {
|
||||||
|
// console.log("K", e.key)
|
||||||
|
|
||||||
|
// if (e.key === 'Enter') {
|
||||||
|
// return 'enter'
|
||||||
|
// }
|
||||||
|
return getDefaultKeyBinding(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
handleKeyCommand(command) {
|
||||||
|
console.log('CMD', command)
|
||||||
|
const newState = RichUtils.handleKeyCommand(this.state.editorState, command)
|
||||||
|
|
||||||
|
if (newState) {
|
||||||
|
this.onChange(newState)
|
||||||
|
return 'handled'
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'not-handled'
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const content = this.state.editorState.getCurrentContent()
|
||||||
|
const e = this.state.editorState
|
||||||
|
|
||||||
|
const s = e.getSelection()
|
||||||
|
const key = s.getStartKey()
|
||||||
|
const so = s.getStartOffset()
|
||||||
|
const block = content.getBlockForKey(key)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<DraftEditor
|
||||||
|
editorState={this.state.editorState}
|
||||||
|
onChange={this.onChange}
|
||||||
|
blockRendererFn={blockRendererFn}
|
||||||
|
blockRenderMap={extendedBlockRenderMap}
|
||||||
|
handleKeyCommand={this.handleKeyCommand}
|
||||||
|
keyBindingFn={this.keyBindingFn}
|
||||||
|
plugins={plugins}
|
||||||
|
ref={(element) => {
|
||||||
|
this.editor = element
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{/* <AlignmentTool/> */}
|
||||||
|
<button onClick={this.onBoldClick}>Bold</button>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
block key: {key} type: {block.getType()}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
start offset {so}
|
||||||
|
</p>
|
||||||
|
<pre>
|
||||||
|
{content.getPlainText('\n')}
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
Whitespace-only changes.
@@ -0,0 +1 @@
|
|||||||
|
export {default} from './Editor'
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
import React, {Component} from 'react'
|
||||||
|
|
||||||
|
export default class ContentEditable extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
|
||||||
|
this.emitChange = this.emitChange.bind(this)
|
||||||
|
this.onRef = this.onRef.bind(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
getHtml() {
|
||||||
|
return this.props.children
|
||||||
|
}
|
||||||
|
|
||||||
|
onRef(element) {
|
||||||
|
this.htmlEl = element
|
||||||
|
}
|
||||||
|
|
||||||
|
emitChange() {
|
||||||
|
if (!this.htmlEl) return
|
||||||
|
const html = this.htmlEl.innerHTML
|
||||||
|
|
||||||
|
if (html !== this.lastHtml) this.props.onChange(html)
|
||||||
|
this.lastHtml = html
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.htmlEl.focus()
|
||||||
|
}
|
||||||
|
|
||||||
|
shouldComponentUpdate(nextProps) {
|
||||||
|
let {props, htmlEl} = this
|
||||||
|
|
||||||
|
// We need not rerender if the change of props simply reflects the user's edits.
|
||||||
|
// Rerendering in this case would make the cursor/caret jump
|
||||||
|
|
||||||
|
// Rerender if there is no element yet... (somehow?)
|
||||||
|
if (!this.htmlEl) return true
|
||||||
|
|
||||||
|
// // ...or if html really changed... (programmatically, not by user edit)
|
||||||
|
// if (nextProps.html !== htmlEl.innerHTML && nextProps.html !== props.html) {
|
||||||
|
// return true
|
||||||
|
// }
|
||||||
|
|
||||||
|
return false
|
||||||
|
|
||||||
|
// let optional = ['style', 'className', 'disabled', 'tagName']
|
||||||
|
//
|
||||||
|
// // Handle additional properties
|
||||||
|
// return optional.some(name => props[name] !== nextProps[name])
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate() {
|
||||||
|
const html = this.getHtml()
|
||||||
|
if (this.htmlEl && this.htmlEl.innerHTML !== html) {
|
||||||
|
// Perhaps React (whose VDOM gets outdated because we often prevent
|
||||||
|
// rerendering) did not update the DOM. So we update it manually now.
|
||||||
|
this.htmlEl.innerHTML = html
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {mix} = this.props;
|
||||||
|
|
||||||
|
return <div
|
||||||
|
className={mix}
|
||||||
|
ref={this.onRef}
|
||||||
|
onInput={this.emitChange}
|
||||||
|
onChange={this.emitChange}
|
||||||
|
// onBlur={this.props.onBlur || this.emitChange}
|
||||||
|
contentEditable={true}
|
||||||
|
suppressContentEditableWarning={true}
|
||||||
|
// dangerouslySetInnerHTML={{__html: this.getHtml()}}
|
||||||
|
>
|
||||||
|
{this.getHtml()}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,214 @@
|
|||||||
|
import React, {Component} from 'react'
|
||||||
|
import {MarkerTabIndex} from './MarkerTabIndex'
|
||||||
|
import {MarkerCell} from './MarkerCell'
|
||||||
|
|
||||||
|
import './Marker.less'
|
||||||
|
import {exist} from '../../lib/fn'
|
||||||
|
import ContentEditable from './ContentEditable'
|
||||||
|
import {constrain} from "./lib/fn";
|
||||||
|
|
||||||
|
export class Marker extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
currentCellIndex: 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
this.onCellUpdate = this.onCellUpdate.bind(this)
|
||||||
|
this.onCellSelect = this.onCellSelect.bind(this)
|
||||||
|
this.onKeyDown = this.onKeyDown.bind(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
onKeyDown(event) {
|
||||||
|
switch (event.key) {
|
||||||
|
case 'ArrowUp': {
|
||||||
|
console.log('Marker ArrowUp')
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
currentCellIndex: this.getPrevCellIndex(),
|
||||||
|
})
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'ArrowDown': {
|
||||||
|
console.log('Marker ArrowDown')
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
currentCellIndex: this.getNextCellIndex(),
|
||||||
|
})
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'Enter': {
|
||||||
|
console.log('Marker Enter')
|
||||||
|
|
||||||
|
const s = getSelection()
|
||||||
|
console.log(s, s.getRangeAt(0))
|
||||||
|
|
||||||
|
event.preventDefault()
|
||||||
|
|
||||||
|
// if (!event.shiftKey) return
|
||||||
|
// event.stopPropagation()
|
||||||
|
|
||||||
|
return this.handleEnter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
getCellIndex(n) {
|
||||||
|
const {data} = this.props
|
||||||
|
const {currentCellIndex} = this.state
|
||||||
|
const index = currentCellIndex + n
|
||||||
|
|
||||||
|
return constrain(index, 0, data.length - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
getPrevCellIndex(...args) {
|
||||||
|
return this.getCellIndex(-1, ...args)
|
||||||
|
}
|
||||||
|
|
||||||
|
getNextCellIndex(...args) {
|
||||||
|
return this.getCellIndex(1, ...args)
|
||||||
|
}
|
||||||
|
|
||||||
|
onCellSelect(index) {
|
||||||
|
this.setState({
|
||||||
|
currentCellIndex: index,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
getDataAt(index) {
|
||||||
|
return this.props.data[index]
|
||||||
|
}
|
||||||
|
|
||||||
|
getBlankTextAt(index) {
|
||||||
|
if (index === 0) return ''
|
||||||
|
|
||||||
|
const cellBefore = this.getDataAt(index)
|
||||||
|
const textBefore = cellBefore.data
|
||||||
|
|
||||||
|
let text
|
||||||
|
|
||||||
|
const ol = (text) => {
|
||||||
|
const m = /^(\d+)\.\s/.exec(text)
|
||||||
|
if (m) {
|
||||||
|
const d = parseFloat(m[1])
|
||||||
|
return `${d + 1}.`
|
||||||
|
} else {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const ul = (text) => /^-\s/.test(text)
|
||||||
|
? '- '
|
||||||
|
: null
|
||||||
|
|
||||||
|
text = ol(textBefore)
|
||||||
|
if (text) return text
|
||||||
|
|
||||||
|
text = ul(textBefore)
|
||||||
|
if (text) return text
|
||||||
|
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
|
async handleEnter() {
|
||||||
|
const {currentCellIndex} = this.state
|
||||||
|
const currentCell = this.getDataAt(currentCellIndex)
|
||||||
|
|
||||||
|
if (currentCell.data) {
|
||||||
|
const nextCellIndex = currentCellIndex + 1
|
||||||
|
|
||||||
|
if (nextCellIndex > this.props.data.length - 1) {
|
||||||
|
const {data, marker, onUpdate} = this.props
|
||||||
|
const blankText = this.getBlankTextAt(currentCellIndex)
|
||||||
|
const newCell = await marker.blank({text: blankText})
|
||||||
|
const newData = [
|
||||||
|
...data,
|
||||||
|
newCell,
|
||||||
|
]
|
||||||
|
|
||||||
|
onUpdate(newData)
|
||||||
|
} else {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
currentCellIndex: nextCellIndex,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async onCellUpdate(index, text) {
|
||||||
|
console.log('UPDATE', text)
|
||||||
|
|
||||||
|
const {data, marker, onUpdate} = this.props
|
||||||
|
|
||||||
|
const cells = await marker.evaluate(text)
|
||||||
|
|
||||||
|
const before = data.slice(0, index)
|
||||||
|
const after = data.slice(index + 1)
|
||||||
|
|
||||||
|
let newData = [
|
||||||
|
...before,
|
||||||
|
...cells,
|
||||||
|
...after,
|
||||||
|
]
|
||||||
|
|
||||||
|
// const next = index + cells.length
|
||||||
|
// if (next > newData.length - 1) {
|
||||||
|
// const newCell = await marker.blank()
|
||||||
|
// newData.push(newCell)
|
||||||
|
// }
|
||||||
|
|
||||||
|
console.log(newData.map(x => x.data).join('\n'))
|
||||||
|
|
||||||
|
onUpdate(newData)
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillMount() {
|
||||||
|
// this.setState({
|
||||||
|
// currentCellIndex: 0,
|
||||||
|
// })
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {data} = this.props
|
||||||
|
if (!data.length) return null
|
||||||
|
|
||||||
|
const {currentCellIndex} = this.state
|
||||||
|
|
||||||
|
const tabIndex = 1
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className='Marker'
|
||||||
|
// onKeyDownCapture={this.onKeyDown}
|
||||||
|
onKeyDown={this.onKeyDown}
|
||||||
|
>
|
||||||
|
{data.map((x, i) => {
|
||||||
|
return (i === currentCellIndex ? (
|
||||||
|
<ContentEditable
|
||||||
|
mix={'MarkerCell'}
|
||||||
|
tabIndex={tabIndex + i}
|
||||||
|
key={i}
|
||||||
|
onChange={text => this.onCellUpdate(i, text)}
|
||||||
|
>
|
||||||
|
{x.data}
|
||||||
|
</ContentEditable>
|
||||||
|
) : (
|
||||||
|
<div
|
||||||
|
className={'MarkerCell'}
|
||||||
|
tabIndex={tabIndex + i}
|
||||||
|
key={i}
|
||||||
|
onFocus={() => this.onCellSelect(i)}
|
||||||
|
>
|
||||||
|
{x.render()}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
.Marker {
|
||||||
|
//--active-color: #73acf6;
|
||||||
|
--active-color: #f0f0f0;
|
||||||
|
--cell-padding: 8px;
|
||||||
|
--font-size: 16px;
|
||||||
|
|
||||||
|
font-size: var(--font-size);
|
||||||
|
font-family: sans-serif;
|
||||||
|
//line-height: var(--font-size);
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
width: 100%;
|
||||||
|
font-size: var(--font-size);
|
||||||
|
font-family: sans-serif;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
height: var(--font-size);
|
||||||
|
//height: calc(2px + var(--font-size));
|
||||||
|
border: none;
|
||||||
|
background-color: var(--active-color);
|
||||||
|
|
||||||
|
outline: none;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.MarkerCellTabIndex {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.MarkerCell {
|
||||||
|
//padding: var(--cell-padding);
|
||||||
|
padding-bottom: 8px;
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&--active {
|
||||||
|
background-color: var(--active-color);
|
||||||
|
//background-color: #f6903b;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--edit {
|
||||||
|
div {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
//background-color: #00b7ec;
|
||||||
|
background-color: var(--active-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
import React, {Component} from 'react'
|
||||||
|
import className from 'classnames'
|
||||||
|
import {trimNewline} from './lib/fn'
|
||||||
|
|
||||||
|
import './Marker.less'
|
||||||
|
import {MarkerEdit} from './MarkerEdit'
|
||||||
|
|
||||||
|
const Cell = ({children, active = false, edit = false}) => (
|
||||||
|
<div className={className('MarkerCell', {
|
||||||
|
'MarkerCell--active': active,
|
||||||
|
'MarkerCell--edit': edit,
|
||||||
|
})}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
export class MarkerCell extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
|
||||||
|
this.onSubmit = this.onSubmit.bind(this)
|
||||||
|
this.onChange = this.onChange.bind(this)
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
content: null,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getCellData() {
|
||||||
|
return this.state.content
|
||||||
|
? this.state.content
|
||||||
|
: this.props.cell.data
|
||||||
|
}
|
||||||
|
|
||||||
|
getCellComponent() {
|
||||||
|
return this.props.cell.render()
|
||||||
|
}
|
||||||
|
|
||||||
|
onSubmit(e) {
|
||||||
|
e.preventDefault()
|
||||||
|
const {onUpdate} = this.props
|
||||||
|
onUpdate(this.getCellData())
|
||||||
|
}
|
||||||
|
|
||||||
|
onChange(e) {
|
||||||
|
const content = trimNewline(e.target.value)
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
content,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {active, edit} = this.props
|
||||||
|
|
||||||
|
return edit
|
||||||
|
? (
|
||||||
|
<Cell edit={edit}>
|
||||||
|
<MarkerEdit
|
||||||
|
onSubmit={this.onSubmit}
|
||||||
|
onChange={this.onChange}
|
||||||
|
>
|
||||||
|
{this.getCellData()}
|
||||||
|
</MarkerEdit>
|
||||||
|
</Cell>
|
||||||
|
) : (
|
||||||
|
<Cell active={active}>
|
||||||
|
{this.getCellComponent()}
|
||||||
|
</Cell>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
import React, {Component, createContext} from 'react'
|
||||||
|
import className from 'classnames'
|
||||||
|
import {trim} from './lib/fn'
|
||||||
|
import {MarkerContext} from './Marker'
|
||||||
|
|
||||||
|
import './Marker.less'
|
||||||
|
import ContentEditable from './ContentEditable'
|
||||||
|
|
||||||
|
export class MarkerEdit extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
|
||||||
|
// this.onInputRef = this.onInputRef.bind(this)
|
||||||
|
this.onChange = this.onChange.bind(this)
|
||||||
|
this.onFocus = this.onFocus.bind(this)
|
||||||
|
this.onKeyDown = this.onKeyDown.bind(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
onChange(event) {
|
||||||
|
console.log(event, event.target)
|
||||||
|
this.props.onChange(event)
|
||||||
|
}
|
||||||
|
|
||||||
|
onFocus(e) {
|
||||||
|
e.stopPropagation()
|
||||||
|
}
|
||||||
|
|
||||||
|
onKeyDown(event) {
|
||||||
|
switch (event.key) {
|
||||||
|
case 'ArrowUp': {
|
||||||
|
if (event.shiftKey) return
|
||||||
|
event.stopPropagation()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'ArrowDown': {
|
||||||
|
if (event.shiftKey) return
|
||||||
|
event.stopPropagation()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'Enter': {
|
||||||
|
console.log('MarkerEdit Enter')
|
||||||
|
if (event.shiftKey) return
|
||||||
|
// event.stopPropagation()
|
||||||
|
|
||||||
|
const {onSubmit} = this.props
|
||||||
|
onSubmit(event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// onInputRef(e) {
|
||||||
|
// this.input = e
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// componentDidMount() {
|
||||||
|
// this.input.focus()
|
||||||
|
// }
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {children} = this.props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ContentEditable
|
||||||
|
// ref={this.onInputRef}
|
||||||
|
onChange={this.onChange}
|
||||||
|
onFocus={this.onFocus}
|
||||||
|
onKeyDown={this.onKeyDown}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</ContentEditable>
|
||||||
|
)
|
||||||
|
|
||||||
|
// return (
|
||||||
|
// <textarea
|
||||||
|
// value={children}
|
||||||
|
// ref={this.onInputRef}
|
||||||
|
// onChange={onChange}
|
||||||
|
// onFocus={this.onFocus}
|
||||||
|
// onKeyDown={this.onKeyDown}
|
||||||
|
// />
|
||||||
|
// )
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import React, {Component, createContext} from 'react'
|
||||||
|
|
||||||
|
import './Marker.less'
|
||||||
|
|
||||||
|
export class MarkerTabIndex extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
|
||||||
|
this.onFocus = this.onFocus.bind(this)
|
||||||
|
this.onRef = this.onRef.bind(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
onFocus() {
|
||||||
|
const {onFocus, data} = this.props
|
||||||
|
onFocus(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
onRef(e) {
|
||||||
|
this.element = e
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate() {
|
||||||
|
const {focus} = this.props
|
||||||
|
if (focus) {
|
||||||
|
this.element.focus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {tabIndex, children} = this.props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={'MarkerCellTabIndex'}
|
||||||
|
children={children}
|
||||||
|
tabIndex={tabIndex}
|
||||||
|
onFocus={this.onFocus}
|
||||||
|
ref={this.onRef}
|
||||||
|
role={'button'}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
import './Marker.less'
|
||||||
|
|
||||||
|
export const MarkerWidgetText = ({text}) => (
|
||||||
|
<div
|
||||||
|
className='MarkerWidgetText'
|
||||||
|
>
|
||||||
|
{text}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
export const MarkerWidgetHtml = ({html}) => (
|
||||||
|
<div
|
||||||
|
className='MarkerWidgetHtml'
|
||||||
|
dangerouslySetInnerHTML={{__html: html}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
|
||||||
|
export const MarkerWidgetUrl = ({url, text}) => (
|
||||||
|
<div
|
||||||
|
className='MarkerWidgetUrl'
|
||||||
|
>
|
||||||
|
<a href={url}>
|
||||||
|
{text}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export {Marker as default} from './Marker'
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
/**
|
||||||
|
* Remove trailing spaces from string
|
||||||
|
*
|
||||||
|
* f(_x_) => x
|
||||||
|
*
|
||||||
|
* @param str
|
||||||
|
*/
|
||||||
|
export function trim(str) {
|
||||||
|
return str
|
||||||
|
.replace(/^\s+/, '')
|
||||||
|
.replace(/\s+$/, '')
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove trailing spaces from string
|
||||||
|
*
|
||||||
|
* f(_x_) => x
|
||||||
|
*
|
||||||
|
* @param str
|
||||||
|
*/
|
||||||
|
export function trimNewline(str) {
|
||||||
|
return str
|
||||||
|
.replace(/^\n+/, '')
|
||||||
|
.replace(/\n+$/, '')
|
||||||
|
}
|
||||||
|
|
||||||
|
export function constrain(n, min, max) {
|
||||||
|
if (n < min) return min
|
||||||
|
if (n > max) return max
|
||||||
|
return n
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
import Token from './Token'
|
||||||
|
import {trim} from '../fn'
|
||||||
|
import {getHtml} from './index'
|
||||||
|
|
||||||
|
|
||||||
|
export default class FileToken extends Token {
|
||||||
|
static test(data) {
|
||||||
|
return is_file(data, documentExtensions + imageExtensions)
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor({data}) {
|
||||||
|
super({
|
||||||
|
name: 'file',
|
||||||
|
data,
|
||||||
|
})
|
||||||
|
this.joinable = false
|
||||||
|
}
|
||||||
|
|
||||||
|
async compile() {
|
||||||
|
const data = parse_file(this.getData())
|
||||||
|
const url = data['file']
|
||||||
|
const text = data.caption
|
||||||
|
? data.caption
|
||||||
|
: url
|
||||||
|
|
||||||
|
return (
|
||||||
|
<a href={url}>
|
||||||
|
{text}
|
||||||
|
</a>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
import Token from './Token'
|
||||||
|
import {isImage, parseFile} from './index'
|
||||||
|
|
||||||
|
export class ImageToken extends Token {
|
||||||
|
static test(data) {
|
||||||
|
return isImage(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor({data}) {
|
||||||
|
super({
|
||||||
|
name: 'image',
|
||||||
|
data,
|
||||||
|
})
|
||||||
|
this.joinable = true
|
||||||
|
this.build = null
|
||||||
|
}
|
||||||
|
|
||||||
|
merge(token) {
|
||||||
|
const mergedToken = new ImageCollectionToken([this.data, token.data])
|
||||||
|
mergedToken.build = this.build
|
||||||
|
return mergedToken
|
||||||
|
}
|
||||||
|
|
||||||
|
async compile() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ImageCollectionToken extends ImageToken {
|
||||||
|
constructor({data}) {
|
||||||
|
super({data})
|
||||||
|
this.name = 'image_collection'
|
||||||
|
this.build = null
|
||||||
|
}
|
||||||
|
|
||||||
|
merge(token) {
|
||||||
|
const data = Array.isArray(token.getData())
|
||||||
|
? token.getData()
|
||||||
|
: [token.getData()]
|
||||||
|
|
||||||
|
const mergedToken = new ImageCollectionToken({
|
||||||
|
data: [...self.getData(), ...data]
|
||||||
|
})
|
||||||
|
mergedToken.build = self.build
|
||||||
|
return mergedToken
|
||||||
|
}
|
||||||
|
|
||||||
|
async compile() {
|
||||||
|
const data = this.parseData()
|
||||||
|
let images = []
|
||||||
|
for (let x of data) {
|
||||||
|
const img = await this.build(x)
|
||||||
|
images.push(img)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='kazimir__image-collection'>
|
||||||
|
<div
|
||||||
|
className='fotorama'
|
||||||
|
data-width='100%'
|
||||||
|
data-ratio='800/600'
|
||||||
|
>
|
||||||
|
{images.map(({src}) => (
|
||||||
|
<img src={src}/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
parseData() {
|
||||||
|
return this
|
||||||
|
.getData()
|
||||||
|
.map(parseFile)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,138 @@
|
|||||||
|
import SplitToken from './SplitToken'
|
||||||
|
|
||||||
|
export class Marker {
|
||||||
|
constructor() {
|
||||||
|
this.tokenFactories = []
|
||||||
|
this.treeMiddlewares = []
|
||||||
|
|
||||||
|
this.join = true
|
||||||
|
this.blankFactory = null
|
||||||
|
}
|
||||||
|
|
||||||
|
async blank({text}) {
|
||||||
|
if (!this.blankFactory) {
|
||||||
|
throw new Error(`Blank factory is not defined`)
|
||||||
|
}
|
||||||
|
const token = await this.blankFactory.create(text)
|
||||||
|
return token.compile()
|
||||||
|
}
|
||||||
|
|
||||||
|
addTokenFactory(tokenFactory, {blank = false} = {}) {
|
||||||
|
this.tokenFactories.push(tokenFactory)
|
||||||
|
if (blank) {
|
||||||
|
this.blankFactory = tokenFactory
|
||||||
|
}
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
addTreeMiddleware(callback) {
|
||||||
|
this.treeMiddlewares.push(callback)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
async evaluate(text) {
|
||||||
|
let tokens = text.split('\n')
|
||||||
|
tokens = this.markupTokens(tokens)
|
||||||
|
if (this.join) {
|
||||||
|
tokens = this.joinTokens(tokens)
|
||||||
|
}
|
||||||
|
|
||||||
|
return compileTokens(tokens)
|
||||||
|
// tree = lxml.html.fromstring(compiled)
|
||||||
|
// return await self.process_tree(tree)
|
||||||
|
}
|
||||||
|
|
||||||
|
joinTokens(tokens) {
|
||||||
|
return mergeTokens(joinTokens(tokens))
|
||||||
|
}
|
||||||
|
|
||||||
|
async processTree(tree) {
|
||||||
|
// for fn in self.treeMiddlewares:
|
||||||
|
// tree = await fn(tree)
|
||||||
|
// return tree
|
||||||
|
}
|
||||||
|
|
||||||
|
markupTokens(tokens) {
|
||||||
|
const markedTokens = []
|
||||||
|
|
||||||
|
tokens.forEach(data => {
|
||||||
|
const token = this.createToken(data)
|
||||||
|
if (!token) {
|
||||||
|
throw new Error(`Token not recognized for data ${data}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
markedTokens.push(token)
|
||||||
|
})
|
||||||
|
|
||||||
|
return markedTokens
|
||||||
|
}
|
||||||
|
|
||||||
|
createToken(data) {
|
||||||
|
for (let factory of this.tokenFactories) {
|
||||||
|
if (factory.test(data)) {
|
||||||
|
return factory.create(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function joinTokens(tokens) {
|
||||||
|
const isSplit = x => x instanceof SplitToken
|
||||||
|
|
||||||
|
let bufferTag = null
|
||||||
|
let tokensBuffer = []
|
||||||
|
let tokensList = []
|
||||||
|
for (let token of tokens) {
|
||||||
|
const tag = token.name
|
||||||
|
const changed = bufferTag !== null && tag !== bufferTag
|
||||||
|
|
||||||
|
let flush = isSplit(token)
|
||||||
|
if (changed) {
|
||||||
|
flush = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!token.isJoinable()) {
|
||||||
|
flush = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flush) {
|
||||||
|
if (tokensBuffer.length) {
|
||||||
|
tokensList.push(tokensBuffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isSplit(token)) {
|
||||||
|
tokensBuffer = []
|
||||||
|
bufferTag = null
|
||||||
|
} else {
|
||||||
|
tokensBuffer = [token]
|
||||||
|
bufferTag = tag
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
bufferTag = tag
|
||||||
|
tokensBuffer.push(token)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tokensBuffer.length) {
|
||||||
|
tokensList.push(tokensBuffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
return tokensList
|
||||||
|
}
|
||||||
|
|
||||||
|
function mergeTokens(tokensList) {
|
||||||
|
return tokensList
|
||||||
|
.map(ts => ts
|
||||||
|
.reduce(
|
||||||
|
(token, x) => token.merge(x)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function compileTokens(tokens) {
|
||||||
|
return Promise.all(
|
||||||
|
tokens.map(x => x.compile())
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import Token from './Token'
|
||||||
|
import {trim} from '../fn'
|
||||||
|
|
||||||
|
export default class SplitToken extends Token {
|
||||||
|
static test(data) {
|
||||||
|
data = trim(data)
|
||||||
|
return data === ''
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(options) {
|
||||||
|
super({
|
||||||
|
...options,
|
||||||
|
name: 'split',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
merge(token) {
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
import Token from './Token'
|
||||||
|
import {trim} from '../fn'
|
||||||
|
import {getHtml} from './index'
|
||||||
|
|
||||||
|
export default class TextToken extends Token {
|
||||||
|
static test(data) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(options) {
|
||||||
|
super({
|
||||||
|
...options,
|
||||||
|
name: 'text',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
merge(token) {
|
||||||
|
const data = [this.data, token.data].join('\n')
|
||||||
|
|
||||||
|
return new TextToken(this.getMergeOptions({
|
||||||
|
data: trim(data),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
async getData() {
|
||||||
|
const html = getHtml(this.data)
|
||||||
|
return {html}
|
||||||
|
}
|
||||||
|
|
||||||
|
// async compile() {
|
||||||
|
// // return getHtml(this.getData())
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// return (
|
||||||
|
// <div
|
||||||
|
// className='MarkerWidgetHtml'
|
||||||
|
// dangerouslySetInnerHTML={{__html: html}}
|
||||||
|
// />
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
export default class Token {
|
||||||
|
static test(data) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor({name, data, component}) {
|
||||||
|
this.name = name
|
||||||
|
this.data = data
|
||||||
|
this.component = component
|
||||||
|
this.joinable = true
|
||||||
|
|
||||||
|
this.compiledData = null
|
||||||
|
}
|
||||||
|
|
||||||
|
isJoinable() {
|
||||||
|
return this.joinable
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
if (!this.compiledData) {
|
||||||
|
throw new Error('Token not compiled. Call Token::compile before.')
|
||||||
|
}
|
||||||
|
return this.component(this.compiledData)
|
||||||
|
}
|
||||||
|
|
||||||
|
async getData() {
|
||||||
|
return this.data
|
||||||
|
}
|
||||||
|
|
||||||
|
async compile() {
|
||||||
|
this.compiledData = await this.getData()
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
merge(token) {
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getMergeOptions(extend) {
|
||||||
|
return {
|
||||||
|
name: this.name,
|
||||||
|
data: this.data,
|
||||||
|
component: this.component,
|
||||||
|
...extend,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toString() {
|
||||||
|
return `[Token ${this.name} ${this.data}]`
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
export class TokenFactory {
|
||||||
|
constructor({token, component}) {
|
||||||
|
this.token = token
|
||||||
|
this.component = component
|
||||||
|
}
|
||||||
|
|
||||||
|
test(sample) {
|
||||||
|
return this.token.test(sample)
|
||||||
|
}
|
||||||
|
|
||||||
|
create(data) {
|
||||||
|
const Token = this.token
|
||||||
|
return new Token({
|
||||||
|
data,
|
||||||
|
component: this.component,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class BuildTokenFactory extends TokenFactory {
|
||||||
|
constructor({token, build, component}) {
|
||||||
|
super({token})
|
||||||
|
this.build = build
|
||||||
|
this.component = component
|
||||||
|
}
|
||||||
|
|
||||||
|
create(data) {
|
||||||
|
const Token = this.token
|
||||||
|
return new Token({
|
||||||
|
data,
|
||||||
|
component: this.component,
|
||||||
|
build: this.build,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import Token from './Token'
|
||||||
|
import {isUrl} from './index'
|
||||||
|
|
||||||
|
export default class UrlToken extends Token {
|
||||||
|
static test(data) {
|
||||||
|
return isUrl(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(options) {
|
||||||
|
super({
|
||||||
|
...options,
|
||||||
|
name: 'url',
|
||||||
|
})
|
||||||
|
this.joinable = false
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import {isUrl} from './index'
|
||||||
|
import React from 'react'
|
||||||
|
import UrlToken from './UrlToken'
|
||||||
|
|
||||||
|
export default class YoutubeToken extends UrlToken {
|
||||||
|
static test(data) {
|
||||||
|
if (!isUrl(data)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = new URL(data)
|
||||||
|
return ['www.youtube.com', 'youtube.com']
|
||||||
|
.includes(url.hostname)
|
||||||
|
}
|
||||||
|
|
||||||
|
async getData() {
|
||||||
|
const url = new URL(this.data)
|
||||||
|
|
||||||
|
const youtubeId = url.searchParams.get('v')
|
||||||
|
return {
|
||||||
|
url: `//www.youtube.com/embed/${youtubeId}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
import {markdown} from 'markdown'
|
||||||
|
|
||||||
|
const {toHTML: markdownToHtml} = markdown
|
||||||
|
|
||||||
|
const splitExt = s => {
|
||||||
|
const parts = s.split('.')
|
||||||
|
const name = parts.splice(0, parts.length - 1)
|
||||||
|
const ext = parts[parts.length - 1]
|
||||||
|
return {name, ext}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const imageExtensions = ['.jpg', '.jpeg', '.png']
|
||||||
|
export const documentExtensions = ['.doc', '.docx', '.pdf', '.xls', '.xlsx']
|
||||||
|
|
||||||
|
export function getHtml(text) {
|
||||||
|
return markdownToHtml(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isUrl(sample) {
|
||||||
|
try {
|
||||||
|
return Boolean(new URL(sample))
|
||||||
|
} catch (e) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isImage(sample) {
|
||||||
|
const file = parseFile(sample)['file']
|
||||||
|
return isFile(file, imageExtensions)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isDocument(sample) {
|
||||||
|
const file = parseFile(sample)['file']
|
||||||
|
return isFile(file, documentExtensions)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isFile(sample, extensions) {
|
||||||
|
let {name, ext} = splitExt(sample)
|
||||||
|
ext = ext.toLowerCase()
|
||||||
|
return extensions.include(ext)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function parseFile(sample) {
|
||||||
|
const re = /)/
|
||||||
|
const p = re.exec(sample)
|
||||||
|
let caption
|
||||||
|
let file
|
||||||
|
if (p) {
|
||||||
|
caption = p.group(1)
|
||||||
|
file = p.group(2)
|
||||||
|
} else {
|
||||||
|
sample = sample.split(' ')
|
||||||
|
file = sample[0]
|
||||||
|
caption = ' '.join(sample.substr(1))
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
file,
|
||||||
|
caption,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function parseLink(sample) {
|
||||||
|
const re = /)/
|
||||||
|
const p = re.exec(sample)
|
||||||
|
|
||||||
|
if (!p) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = p[2]
|
||||||
|
return {
|
||||||
|
url: new URL(url),
|
||||||
|
alt: p[1],
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
:root {
|
||||||
|
--content-width-state-normal: 60%;
|
||||||
|
--content-width-state-full: 100%;
|
||||||
|
--content-width: var(--content-width-state-normal);
|
||||||
|
|
||||||
|
--module-m: .5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
html, body, #root {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
position: absolute;
|
||||||
|
|
||||||
|
background-color: #222;
|
||||||
|
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: "Input Sans Compressed", sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.leaflet-container {
|
||||||
|
height: 100%;
|
||||||
|
/*height: 100px;*/
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 1.25em;
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import App from './features/App'
|
||||||
|
|
||||||
|
// import {createDevTools} from 'redux-devtools'
|
||||||
|
// import LogMonitor from 'redux-devtools-log-monitor'
|
||||||
|
// import DockMonitor from 'redux-devtools-dock-monitor'
|
||||||
|
|
||||||
|
import {Provider} from 'react-redux'
|
||||||
|
import reducer from './reducers'
|
||||||
|
import effects from './effects'
|
||||||
|
|
||||||
|
import {create} from './store'
|
||||||
|
|
||||||
|
import {Router, Route, browserHistory} from 'react-router'
|
||||||
|
import {syncHistoryWithStore} from 'react-router-redux'
|
||||||
|
|
||||||
|
const initialState = {}
|
||||||
|
|
||||||
|
// const DevTools = createDevTools(
|
||||||
|
// <DockMonitor defaultIsVisible={false}
|
||||||
|
// toggleVisibilityKey="ctrl-h"
|
||||||
|
// changePositionKey="ctrl-q"
|
||||||
|
// >
|
||||||
|
// <LogMonitor theme="tomorrow" preserveScrollTop={false}/>
|
||||||
|
// </DockMonitor>
|
||||||
|
// )
|
||||||
|
|
||||||
|
const store = create(reducer, initialState, [
|
||||||
|
// DevTools.instrument()
|
||||||
|
], effects())
|
||||||
|
// const history = syncHistoryWithStore(browserHistory, store)
|
||||||
|
|
||||||
|
export default function (marker, data) {
|
||||||
|
// return (
|
||||||
|
// <App/>
|
||||||
|
// )
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Provider store={store}>
|
||||||
|
<div>
|
||||||
|
<App
|
||||||
|
marker={marker}
|
||||||
|
data={data}
|
||||||
|
/>
|
||||||
|
{/*<Router history={history}>*/}
|
||||||
|
{/*<Route path="/edit" component={App}>*/}
|
||||||
|
|
||||||
|
{/*</Route>*/}
|
||||||
|
{/*</Router>*/}
|
||||||
|
|
||||||
|
{/*<DevTools/>*/}
|
||||||
|
</div>
|
||||||
|
</Provider>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,516 @@
|
|||||||
|
import L from 'leaflet'
|
||||||
|
|
||||||
|
/*
|
||||||
|
Leaflet.pattern, Provides tools to set the backgrounds of vector shapes in Leaflet to be patterns.
|
||||||
|
https://github.com/teastman/Leaflet.pattern
|
||||||
|
(c) 2015, Tyler Eastman
|
||||||
|
*/
|
||||||
|
(function (window, document) {
|
||||||
|
/*
|
||||||
|
* L.Pattern is the base class for fill patterns for leaflet Paths.
|
||||||
|
*/
|
||||||
|
|
||||||
|
L.Pattern = L.Class.extend({
|
||||||
|
// includes: [L.Mixin.Events],
|
||||||
|
includes: [L.Evented],
|
||||||
|
|
||||||
|
options: {
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
width: 8,
|
||||||
|
height: 8,
|
||||||
|
patternUnits: 'userSpaceOnUse',
|
||||||
|
patternContentUnits: 'userSpaceOnUse'
|
||||||
|
// angle: <0 - 360>
|
||||||
|
// patternTransform: <transform-list>
|
||||||
|
},
|
||||||
|
|
||||||
|
_addShapes: L.Util.falseFn,
|
||||||
|
_update: L.Util.falseFn,
|
||||||
|
|
||||||
|
initialize: function (options) {
|
||||||
|
this._shapes = {};
|
||||||
|
L.setOptions(this, options);
|
||||||
|
},
|
||||||
|
|
||||||
|
onAdd: function (map) {
|
||||||
|
this._map = map.target ? map.target : map;
|
||||||
|
this._map._initDefRoot();
|
||||||
|
|
||||||
|
// Create the DOM Object for the pattern.
|
||||||
|
this._initDom();
|
||||||
|
|
||||||
|
// Any shapes that were added before this was added to the map need to have their onAdd called.
|
||||||
|
// for (let i of this._shapes) {
|
||||||
|
// i.onAdd(this);
|
||||||
|
// }
|
||||||
|
Object
|
||||||
|
.values(this._shapes)
|
||||||
|
.forEach(x => x.onAdd(this))
|
||||||
|
|
||||||
|
// Call any children that want to add their own shapes.
|
||||||
|
this._addShapes();
|
||||||
|
|
||||||
|
// Add the DOM Object to the DOM Tree
|
||||||
|
this._addDom();
|
||||||
|
this.redraw();
|
||||||
|
|
||||||
|
if (this.getEvents) {
|
||||||
|
this._map.on(this.getEvents(), this);
|
||||||
|
}
|
||||||
|
this.fire('add');
|
||||||
|
this._map.fire('patternadd', {pattern: this});
|
||||||
|
},
|
||||||
|
|
||||||
|
onRemove: function () {
|
||||||
|
this._removeDom();
|
||||||
|
},
|
||||||
|
|
||||||
|
redraw: function () {
|
||||||
|
if (this._map) {
|
||||||
|
this._update();
|
||||||
|
// for (let i of this._shapes) {
|
||||||
|
// i.redraw();
|
||||||
|
// }
|
||||||
|
Object
|
||||||
|
.values(this._shapes)
|
||||||
|
.forEach(x => x.redraw())
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
setStyle: function (style) {
|
||||||
|
L.setOptions(this, style);
|
||||||
|
if (this._map) {
|
||||||
|
this._updateStyle();
|
||||||
|
this.redraw();
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
addTo: function (map) {
|
||||||
|
map.addPattern(this);
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
remove: function () {
|
||||||
|
return this.removeFrom(this._map);
|
||||||
|
},
|
||||||
|
|
||||||
|
removeFrom: function (map) {
|
||||||
|
if (map) {
|
||||||
|
map.removePattern(this);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
L.Map.addInitHook(function () {
|
||||||
|
this._patterns = {};
|
||||||
|
});
|
||||||
|
|
||||||
|
L.Map.include({
|
||||||
|
addPattern: function (pattern) {
|
||||||
|
var id = L.stamp(pattern);
|
||||||
|
if (this._patterns[id]) { return pattern; }
|
||||||
|
this._patterns[id] = pattern;
|
||||||
|
|
||||||
|
this.whenReady(pattern.onAdd, pattern);
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
removePattern: function (pattern) {
|
||||||
|
var id = L.stamp(pattern);
|
||||||
|
if (!this._patterns[id]) { return this; }
|
||||||
|
|
||||||
|
if (this._loaded) {
|
||||||
|
pattern.onRemove(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pattern.getEvents) {
|
||||||
|
this.off(pattern.getEvents(), pattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete this._patterns[id];
|
||||||
|
|
||||||
|
if (this._loaded) {
|
||||||
|
this.fire('patternremove', {pattern: pattern});
|
||||||
|
pattern.fire('remove');
|
||||||
|
}
|
||||||
|
|
||||||
|
pattern._map = null;
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
hasPattern: function (pattern) {
|
||||||
|
return !!pattern && (L.stamp(pattern) in this._patterns);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
L.Pattern.SVG_NS = 'http://www.w3.org/2000/svg';
|
||||||
|
|
||||||
|
L.Pattern = L.Pattern.extend({
|
||||||
|
_createElement: function (name) {
|
||||||
|
return document.createElementNS(L.Pattern.SVG_NS, name);
|
||||||
|
},
|
||||||
|
|
||||||
|
_initDom: function () {
|
||||||
|
this._dom = this._createElement('pattern');
|
||||||
|
if (this.options.className) {
|
||||||
|
L.DomUtil.addClass(this._dom, this.options.className);
|
||||||
|
}
|
||||||
|
this._updateStyle();
|
||||||
|
},
|
||||||
|
|
||||||
|
_addDom: function () {
|
||||||
|
this._map._defRoot.appendChild(this._dom);
|
||||||
|
},
|
||||||
|
|
||||||
|
_removeDom: function () {
|
||||||
|
L.DomUtil.remove(this._dom);
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateStyle: function () {
|
||||||
|
var dom = this._dom,
|
||||||
|
options = this.options;
|
||||||
|
|
||||||
|
if (!dom) { return; }
|
||||||
|
|
||||||
|
dom.setAttribute('id', L.stamp(this));
|
||||||
|
dom.setAttribute('x', options.x);
|
||||||
|
dom.setAttribute('y', options.y);
|
||||||
|
dom.setAttribute('width', options.width);
|
||||||
|
dom.setAttribute('height', options.height);
|
||||||
|
dom.setAttribute('patternUnits', options.patternUnits);
|
||||||
|
dom.setAttribute('patternContentUnits', options.patternContentUnits);
|
||||||
|
|
||||||
|
if (options.patternTransform || options.angle) {
|
||||||
|
var transform = options.patternTransform ? options.patternTransform + " " : "";
|
||||||
|
transform += options.angle ? "rotate(" + options.angle + ") " : "";
|
||||||
|
dom.setAttribute('patternTransform', transform);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
dom.removeAttribute('patternTransform');
|
||||||
|
}
|
||||||
|
|
||||||
|
// for (let i of this._shapes) {
|
||||||
|
// i._updateStyle();
|
||||||
|
// }
|
||||||
|
|
||||||
|
Object
|
||||||
|
.values(this._shapes)
|
||||||
|
.forEach(x => x._updateStyle())
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
L.Map.include({
|
||||||
|
_initDefRoot: function () {
|
||||||
|
if (!this._defRoot) {
|
||||||
|
if (typeof this.getRenderer === 'function') {
|
||||||
|
var renderer = this.getRenderer(this);
|
||||||
|
this._defRoot = L.Pattern.prototype._createElement('defs');
|
||||||
|
renderer._container.appendChild(this._defRoot);
|
||||||
|
} else {
|
||||||
|
if (!this._pathRoot) {
|
||||||
|
this._initPathRoot();
|
||||||
|
}
|
||||||
|
this._defRoot = L.Pattern.prototype._createElement('defs');
|
||||||
|
this._pathRoot.appendChild(this._defRoot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (L.SVG) {
|
||||||
|
L.SVG.include({
|
||||||
|
_superUpdateStyle: L.SVG.prototype._updateStyle,
|
||||||
|
|
||||||
|
_updateStyle: function (layer) {
|
||||||
|
this._superUpdateStyle(layer);
|
||||||
|
|
||||||
|
if (layer.options.fill && layer.options.fillPattern) {
|
||||||
|
layer._path.setAttribute('fill', 'url(#' + L.stamp(layer.options.fillPattern) + ")");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
L.Path.include({
|
||||||
|
_superUpdateStyle: L.Path.prototype._updateStyle,
|
||||||
|
|
||||||
|
_updateStyle: function () {
|
||||||
|
this._superUpdateStyle();
|
||||||
|
|
||||||
|
if (this.options.fill && this.options.fillPattern) {
|
||||||
|
this._path.setAttribute('fill', 'url(#' + L.stamp(this.options.fillPattern) + ")");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* L.StripePattern is an implementation of Pattern that creates stripes.
|
||||||
|
*/
|
||||||
|
|
||||||
|
L.StripePattern = L.Pattern.extend({
|
||||||
|
|
||||||
|
options: {
|
||||||
|
weight: 4,
|
||||||
|
spaceWeight: 4,
|
||||||
|
color: '#000000',
|
||||||
|
spaceColor: '#ffffff',
|
||||||
|
opacity: 1.0,
|
||||||
|
spaceOpacity: 0.0
|
||||||
|
},
|
||||||
|
|
||||||
|
_addShapes: function () {
|
||||||
|
this._stripe = new L.PatternPath({
|
||||||
|
stroke: true,
|
||||||
|
weight: this.options.weight,
|
||||||
|
color: this.options.color,
|
||||||
|
opacity: this.options.opacity
|
||||||
|
});
|
||||||
|
|
||||||
|
this._space = new L.PatternPath({
|
||||||
|
stroke: true,
|
||||||
|
weight: this.options.spaceWeight,
|
||||||
|
color: this.options.spaceColor,
|
||||||
|
opacity: this.options.spaceOpacity
|
||||||
|
});
|
||||||
|
|
||||||
|
this.addShape(this._stripe);
|
||||||
|
this.addShape(this._space);
|
||||||
|
|
||||||
|
this._update();
|
||||||
|
},
|
||||||
|
|
||||||
|
_update: function () {
|
||||||
|
this._stripe.options.d = 'M0 ' + this._stripe.options.weight / 2 + ' H ' + this.options.width;
|
||||||
|
this._space.options.d = 'M0 ' + (this._stripe.options.weight + this._space.options.weight / 2) + ' H ' + this.options.width;
|
||||||
|
},
|
||||||
|
|
||||||
|
setStyle: L.Pattern.prototype.setStyle
|
||||||
|
});
|
||||||
|
|
||||||
|
L.stripePattern = function (options) {
|
||||||
|
return new L.StripePattern(options);
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* L.PatternShape is the base class that is used to define the shapes in Patterns.
|
||||||
|
*/
|
||||||
|
|
||||||
|
L.PatternShape = L.Class.extend({
|
||||||
|
|
||||||
|
options: {
|
||||||
|
stroke: true,
|
||||||
|
color: '#3388ff',
|
||||||
|
weight: 3,
|
||||||
|
opacity: 1,
|
||||||
|
lineCap: 'round',
|
||||||
|
lineJoin: 'round',
|
||||||
|
// dashArray: null
|
||||||
|
// dashOffset: null
|
||||||
|
|
||||||
|
// fill: false
|
||||||
|
// fillColor: same as color by default
|
||||||
|
fillOpacity: 0.2,
|
||||||
|
fillRule: 'evenodd',
|
||||||
|
// fillPattern: L.Pattern
|
||||||
|
},
|
||||||
|
|
||||||
|
initialize: function (options) {
|
||||||
|
L.setOptions(this, options);
|
||||||
|
},
|
||||||
|
|
||||||
|
// Called when the parent Pattern get's added to the map,
|
||||||
|
// or when added to a Pattern that is already on the map.
|
||||||
|
onAdd: function (pattern) {
|
||||||
|
this._pattern = pattern;
|
||||||
|
if (this._pattern._dom) {
|
||||||
|
this._initDom(); // This function is implemented by it's children.
|
||||||
|
this._addDom();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
addTo: function (pattern) {
|
||||||
|
pattern.addShape(this);
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
redraw: function () {
|
||||||
|
if (this._pattern) {
|
||||||
|
this._updateShape(); // This function is implemented by it's children.
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
setStyle: function (style) {
|
||||||
|
L.setOptions(this, style);
|
||||||
|
if (this._pattern) {
|
||||||
|
this._updateStyle();
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
setShape: function (shape) {
|
||||||
|
this.options = L.extend({}, this.options, shape);
|
||||||
|
this._updateShape();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
L.Pattern.include({
|
||||||
|
addShape: function (shape) {
|
||||||
|
var id = L.stamp(shape);
|
||||||
|
if (this._shapes[id]) { return shape; }
|
||||||
|
this._shapes[id] = shape;
|
||||||
|
shape.onAdd(this);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
L.PatternShape.SVG_NS = 'http://www.w3.org/2000/svg';
|
||||||
|
|
||||||
|
L.PatternShape = L.PatternShape.extend({
|
||||||
|
_createElement: function (name) {
|
||||||
|
return document.createElementNS(L.PatternShape.SVG_NS, name);
|
||||||
|
},
|
||||||
|
|
||||||
|
_initDom: L.Util.falseFn,
|
||||||
|
_updateShape: L.Util.falseFn,
|
||||||
|
|
||||||
|
_initDomElement: function (type) {
|
||||||
|
this._dom = this._createElement(type);
|
||||||
|
if (this.options.className) {
|
||||||
|
L.DomUtil.addClass(this._dom, this.options.className);
|
||||||
|
}
|
||||||
|
this._updateStyle();
|
||||||
|
},
|
||||||
|
|
||||||
|
_addDom: function () {
|
||||||
|
this._pattern._dom.appendChild(this._dom);
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateStyle: function () {
|
||||||
|
var dom = this._dom,
|
||||||
|
options = this.options;
|
||||||
|
|
||||||
|
if (!dom) { return; }
|
||||||
|
|
||||||
|
if (options.stroke) {
|
||||||
|
dom.setAttribute('stroke', options.color);
|
||||||
|
dom.setAttribute('stroke-opacity', options.opacity);
|
||||||
|
dom.setAttribute('stroke-width', options.weight);
|
||||||
|
dom.setAttribute('stroke-linecap', options.lineCap);
|
||||||
|
dom.setAttribute('stroke-linejoin', options.lineJoin);
|
||||||
|
|
||||||
|
if (options.dashArray) {
|
||||||
|
dom.setAttribute('stroke-dasharray', options.dashArray);
|
||||||
|
} else {
|
||||||
|
dom.removeAttribute('stroke-dasharray');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.dashOffset) {
|
||||||
|
dom.setAttribute('stroke-dashoffset', options.dashOffset);
|
||||||
|
} else {
|
||||||
|
dom.removeAttribute('stroke-dashoffset');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dom.setAttribute('stroke', 'none');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.fill) {
|
||||||
|
if (options.fillPattern) {
|
||||||
|
dom.setAttribute('fill', 'url(#' + L.stamp(options.fillPattern) + ")");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
dom.setAttribute('fill', options.fillColor || options.color);
|
||||||
|
}
|
||||||
|
dom.setAttribute('fill-opacity', options.fillOpacity);
|
||||||
|
dom.setAttribute('fill-rule', options.fillRule || 'evenodd');
|
||||||
|
} else {
|
||||||
|
dom.setAttribute('fill', 'none');
|
||||||
|
}
|
||||||
|
|
||||||
|
dom.setAttribute('pointer-events', options.pointerEvents || (options.interactive ? 'visiblePainted' : 'none'));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* L.PatternPath is the implementation of PatternShape for adding Paths
|
||||||
|
*/
|
||||||
|
|
||||||
|
L.PatternPath = L.PatternShape.extend({
|
||||||
|
// options: {
|
||||||
|
// d: <svg path code>
|
||||||
|
// },
|
||||||
|
|
||||||
|
_initDom: function () {
|
||||||
|
this._initDomElement('path');
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateShape: function () {
|
||||||
|
if (!this._dom) { return; }
|
||||||
|
this._dom.setAttribute('d', this.options.d);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
|
* L.PatternCircle is the implementation of PatternShape for adding Circles
|
||||||
|
*/
|
||||||
|
|
||||||
|
L.PatternCircle = L.PatternShape.extend({
|
||||||
|
options: {
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
radius: 0
|
||||||
|
},
|
||||||
|
|
||||||
|
_initDom: function () {
|
||||||
|
this._initDomElement('circle');
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateShape: function () {
|
||||||
|
if (!this._dom) { return; }
|
||||||
|
this._dom.setAttribute('cx', this.options.x);
|
||||||
|
this._dom.setAttribute('cy', this.options.y);
|
||||||
|
this._dom.setAttribute('r', this.options.radius);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
|
* L.PatternRect is the implementation of PatternShape for adding Rectangles
|
||||||
|
*/
|
||||||
|
|
||||||
|
L.PatternRect = L.PatternShape.extend({
|
||||||
|
options: {
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
width: 10,
|
||||||
|
height: 10,
|
||||||
|
// rx: x radius for rounded corners
|
||||||
|
// ry: y radius for rounded corners
|
||||||
|
},
|
||||||
|
|
||||||
|
_initDom: function () {
|
||||||
|
this._initDomElement('rect');
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateShape: function () {
|
||||||
|
if (!this._dom) { return; }
|
||||||
|
this._dom.setAttribute('x', this.options.x);
|
||||||
|
this._dom.setAttribute('y', this.options.y);
|
||||||
|
this._dom.setAttribute('width', this.options.width);
|
||||||
|
this._dom.setAttribute('height', this.options.height);
|
||||||
|
if (this.options.rx) { this._dom.setAttribute('rx', this.options.rx); }
|
||||||
|
if (this.options.ry) { this._dom.setAttribute('ry', this.options.ry); }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}(window, document));
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
export function actionRequest(type){
|
||||||
|
return type + '_REQUEST'
|
||||||
|
}
|
||||||
|
|
||||||
|
export function actionFailure(type){
|
||||||
|
return type + '_FAILURE'
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import {mobileDetectionScreenSize} from '../config'
|
||||||
|
|
||||||
|
export const isLandscapeMedia = matchMedia('(orientation: landscape)')
|
||||||
|
|
||||||
|
function matchMedia(query) {
|
||||||
|
return window.matchMedia(query)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isMobile() {
|
||||||
|
const landscape = isLandscape()
|
||||||
|
const mobileWidth = landscape
|
||||||
|
? mobileDetectionScreenSize[0]
|
||||||
|
: mobileDetectionScreenSize[1]
|
||||||
|
|
||||||
|
const isMobileMedia = matchMedia(`(max-width: ${mobileWidth}px)`)
|
||||||
|
return isMobileMedia.matches
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isLandscape(){
|
||||||
|
return isLandscapeMedia.matches
|
||||||
|
}
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
/**
|
||||||
|
* f([x, y, z, ...], [m, n, k, ...]) -> [[x, m], [y, n], [z, k], ...]
|
||||||
|
* @param list1
|
||||||
|
* @param list2
|
||||||
|
*/
|
||||||
|
export function zip(list1, list2) {
|
||||||
|
const [small, other] = list1.length <= list2.length
|
||||||
|
? [list1, list2]
|
||||||
|
: [list2, list1]
|
||||||
|
return small.map((s, i) => [s, other[i]])
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* f(g, [x, y, z, ...], [m, n, k, ...]) -> [[x, m], [y, n], [z, k], ...] based on G
|
||||||
|
* @param list1
|
||||||
|
* @param list2
|
||||||
|
*/
|
||||||
|
export function zipBy (key, list1, list2) {
|
||||||
|
const [small, other] = list1.length <= list2.length
|
||||||
|
? [list1, list2]
|
||||||
|
: [list2, list1]
|
||||||
|
|
||||||
|
return small.map(s => {
|
||||||
|
const k = key(s)
|
||||||
|
const o = other.find(i => key(i) === k)
|
||||||
|
return [s, o]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function arraysIsEqual(list1, list2){
|
||||||
|
if (list1.length !== list2.length) return false
|
||||||
|
|
||||||
|
const booleans = zip(list1, list2)
|
||||||
|
.map((a, b) => a === b)
|
||||||
|
return all(booleans)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function all(list){
|
||||||
|
return list
|
||||||
|
.map(Boolean)
|
||||||
|
.reduce((acc, i) => acc && i, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function any(list){
|
||||||
|
return list
|
||||||
|
.map(Boolean)
|
||||||
|
.reduce((acc, i) => acc || i, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function exist(value){
|
||||||
|
return all([
|
||||||
|
value !== null,
|
||||||
|
value !== undefined,
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* f(g) -> m(x) -> g(x) -> x
|
||||||
|
*
|
||||||
|
* @param fn
|
||||||
|
* @returns {function(*)}
|
||||||
|
*/
|
||||||
|
export function pass(fn) {
|
||||||
|
return x => {
|
||||||
|
fn(x)
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* f(n) -> g(x) -> {[n]: x}
|
||||||
|
*
|
||||||
|
* @param name
|
||||||
|
* @returns {function(*): {}}
|
||||||
|
*/
|
||||||
|
export function pack(name) {
|
||||||
|
//return value => {[name]: value}
|
||||||
|
|
||||||
|
return value => ({[name]: value})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* f(g, m, ...) -> g . m . ...
|
||||||
|
*
|
||||||
|
* @param functions
|
||||||
|
* @returns {function(*=): *}
|
||||||
|
*/
|
||||||
|
export function compose(...functions) {
|
||||||
|
const fs = functions.reverse()
|
||||||
|
return (...value) => fs.reduce((acc, fn) => fn(...acc), value)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* f(t, g) -> g(x) after t milliseconds
|
||||||
|
*
|
||||||
|
* @param ms
|
||||||
|
* @param callback
|
||||||
|
* @returns {function(...[*])}
|
||||||
|
*/
|
||||||
|
export function debounce(ms, callback) {
|
||||||
|
let id = 0
|
||||||
|
return (...args) => {
|
||||||
|
clearTimeout(id)
|
||||||
|
id = setTimeout(callback, ms, ...args)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import Leaflet from 'leaflet'
|
||||||
|
|
||||||
|
export function getBoundsOf(feature) {
|
||||||
|
return Leaflet
|
||||||
|
.geoJSON(feature)
|
||||||
|
.getBounds()
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import {actionRequest, actionFailure} from './action'
|
||||||
|
|
||||||
|
export default function promiseMiddleware() {
|
||||||
|
return next => action => {
|
||||||
|
const {async, type, ...rest} = action
|
||||||
|
if (!async) return next(action)
|
||||||
|
|
||||||
|
next({...rest, type: actionRequest(type)})
|
||||||
|
return async
|
||||||
|
.then(res => {
|
||||||
|
next({...rest, ...res, error: null, type})
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
next({...rest, error, type: actionFailure(type)})
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
export default function createEffects(effects) {
|
||||||
|
return store => next => action => {
|
||||||
|
next(action)
|
||||||
|
effects.forEach(effect => effect(store.getState(), action, store.dispatch))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export function sleep(ms = 0) {
|
||||||
|
return new Promise(resolve => setTimeout(resolve, ms))
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
export function getVariable(name, element = document.documentElement) {
|
||||||
|
const value = getComputedStyle(element).getPropertyValue(name)
|
||||||
|
return parseValue(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseValue(value) {
|
||||||
|
const values = /\s*(\d+)(%|px)\s*/.exec(value)
|
||||||
|
if (values) {
|
||||||
|
const v = Number(values[1])
|
||||||
|
// const v = values[1]
|
||||||
|
const w = values[2]
|
||||||
|
if (w === '%') return v / 100
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import {combineReducers} from 'redux'
|
||||||
|
import {routerReducer} from 'react-router-redux'
|
||||||
|
// import app from '../features/App/duck'
|
||||||
|
|
||||||
|
export default combineReducers({
|
||||||
|
// app,
|
||||||
|
routing: routerReducer
|
||||||
|
})
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import {createStore, applyMiddleware, compose} from 'redux'
|
||||||
|
// import thunk from 'redux-thunk'
|
||||||
|
// import async from '../lib/promiseMiddleware'
|
||||||
|
// import createEffects from '../lib/sideEffectsMiddleware'
|
||||||
|
|
||||||
|
// import {routerMiddleware} from 'react-router-redux'
|
||||||
|
// import {browserHistory} from 'react-router'
|
||||||
|
|
||||||
|
export function create(reducer, init, enhancers = [], effects = []) {
|
||||||
|
// const browser = routerMiddleware(browserHistory)
|
||||||
|
// const es = createEffects(effects)
|
||||||
|
// const enhancer = compose(applyMiddleware(browser, async, thunk, es), ...enhancers)
|
||||||
|
const enhancer = compose(applyMiddleware(), ...enhancers)
|
||||||
|
return createStore(reducer, init, enhancer)
|
||||||
|
}
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
import 'babel-polyfill'
|
|
||||||
|
|
||||||
import {dbUri, port, name} from './config'
|
import {dbUri, port, name} from './config'
|
||||||
import {connect} from './core/db'
|
import {connect} from './core/db'
|
||||||
import server from './server'
|
import server from './server'
|
||||||
|
|||||||
+14
-1
@@ -11,7 +11,7 @@ const isEqualPate = require('../lib/url').isEqualPath
|
|||||||
const {compose, any} = require('../lib/common')
|
const {compose, any} = require('../lib/common')
|
||||||
|
|
||||||
const menuModel = require('../models/menu').default
|
const menuModel = require('../models/menu').default
|
||||||
const {viewMain} = require('../config')
|
const {viewMain, viewEdit} = require('../config')
|
||||||
|
|
||||||
const defaultOptions = {
|
const defaultOptions = {
|
||||||
commentsEnabled: false,
|
commentsEnabled: false,
|
||||||
@@ -105,4 +105,17 @@ async function render(path, data, meta, options = {}) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function renderEdit() {
|
||||||
|
const renderOptions = {
|
||||||
|
...defaultOptions,
|
||||||
|
templateFile: viewEdit,
|
||||||
|
}
|
||||||
|
|
||||||
|
const source = await readFile(renderOptions.templateFile, 'utf-8')
|
||||||
|
const template = handlebars.compile(source)
|
||||||
|
|
||||||
|
return template({})
|
||||||
|
}
|
||||||
|
|
||||||
exports.render = render
|
exports.render = render
|
||||||
|
exports.renderEdit = renderEdit
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
const {get} = require('koa-route')
|
||||||
|
const {renderEdit} = require('../lib/render')
|
||||||
|
|
||||||
|
function getEdit() {
|
||||||
|
return get('/edit', async ctx => {
|
||||||
|
ctx.body = await renderEdit()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.getEdit = getEdit
|
||||||
+5
-6
@@ -10,15 +10,14 @@ export function queryObject() {
|
|||||||
const processors = [toTrue, toFalse]
|
const processors = [toTrue, toFalse]
|
||||||
|
|
||||||
return async(ctx, next) => {
|
return async(ctx, next) => {
|
||||||
let query = ctx.query
|
|
||||||
ctx.query = Object
|
ctx.query = Object
|
||||||
.keys(query)
|
.keys(ctx.query)
|
||||||
.reduce((query, key) => assign(
|
.reduce((query, key) => assign(
|
||||||
query,
|
query,
|
||||||
key,
|
key,
|
||||||
processors
|
processors
|
||||||
.reduce((value, fn) => fn(value), query[key])
|
.reduce((value, fn) => fn(value), query[key])
|
||||||
), query)
|
), ctx.query)
|
||||||
|
|
||||||
await next()
|
await next()
|
||||||
}
|
}
|
||||||
@@ -26,9 +25,9 @@ export function queryObject() {
|
|||||||
|
|
||||||
export function accepts(routes) {
|
export function accepts(routes) {
|
||||||
return async function (ctx) {
|
return async function (ctx) {
|
||||||
let request = ctx.request
|
const types = Object
|
||||||
let types = Object.keys(routes)
|
.keys(routes)
|
||||||
.filter(type => request.accepts(type))
|
.filter(type => ctx.request.accepts(type))
|
||||||
|
|
||||||
if (types.length) {
|
if (types.length) {
|
||||||
let fn = routes[types[0]]
|
let fn = routes[types[0]]
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ const document = require('./routes/document')
|
|||||||
const schedule = require('./routes/schedule')
|
const schedule = require('./routes/schedule')
|
||||||
const pages = require('./routes/pages')
|
const pages = require('./routes/pages')
|
||||||
const sitemap = require('./routes/sitemap')
|
const sitemap = require('./routes/sitemap')
|
||||||
|
const edit = require('./routes/edit')
|
||||||
|
|
||||||
const dirPublic = path.join(__dirname, '../public')
|
const dirPublic = path.join(__dirname, '../public')
|
||||||
|
|
||||||
@@ -65,6 +66,7 @@ export default function (config) {
|
|||||||
app.use(error.notFound(view404))
|
app.use(error.notFound(view404))
|
||||||
app.use(sitemap())
|
app.use(sitemap())
|
||||||
|
|
||||||
|
// app.use(edit.getEdit())
|
||||||
app.use(home.getHome(config.articlesPerPage))
|
app.use(home.getHome(config.articlesPerPage))
|
||||||
app.use(gallery.getGallery())
|
app.use(gallery.getGallery())
|
||||||
app.use(article.getArticles(config.articlesPerPage))
|
app.use(article.getArticles(config.articlesPerPage))
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
@import "variables";
|
|
||||||
|
|
||||||
.body-wrapper {
|
.body-wrapper {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
|
|
||||||
font-family: $text-font;
|
font-family: var(--text-font);
|
||||||
font-size: var(--font-size-default);
|
font-size: var(--font-size-default);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
@import "../variables";
|
|
||||||
|
|
||||||
.Article {
|
.Article {
|
||||||
--article-line-height: 1.75em;
|
--article-line-height: 1.75em;
|
||||||
--article-quote-padding: var(--half-margin);
|
--article-quote-padding: var(--half-margin);
|
||||||
@@ -8,7 +6,7 @@
|
|||||||
|
|
||||||
hyphens: auto;
|
hyphens: auto;
|
||||||
|
|
||||||
margin-bottom: $post-margin;
|
margin-bottom: var(--post-margin);
|
||||||
|
|
||||||
&-Head {
|
&-Head {
|
||||||
margin-bottom: 1.25em;
|
margin-bottom: 1.25em;
|
||||||
@@ -29,8 +27,8 @@
|
|||||||
--text-margin: 10px;
|
--text-margin: 10px;
|
||||||
|
|
||||||
ul, ol {
|
ul, ol {
|
||||||
margin-left: $half-margin;
|
margin-left: var(--half-margin);
|
||||||
margin-bottom: $single-margin;
|
margin-bottom: var(--single-margin);
|
||||||
|
|
||||||
li {
|
li {
|
||||||
margin-bottom: var(--half-margin);
|
margin-bottom: var(--half-margin);
|
||||||
@@ -54,8 +52,8 @@
|
|||||||
blockquote {
|
blockquote {
|
||||||
background-color: hsl(215, 10%, 96%);
|
background-color: hsl(215, 10%, 96%);
|
||||||
padding: var(--article-quote-padding);
|
padding: var(--article-quote-padding);
|
||||||
margin: $single-margin 0;
|
margin: var(--single-margin) 0;
|
||||||
font-family: $title-font;
|
font-family: var(--title-font);
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
font-size: var(--article-quote-font-size);
|
font-size: var(--article-quote-font-size);
|
||||||
@@ -63,13 +61,13 @@
|
|||||||
border-radius: var(--block-border-radius);
|
border-radius: var(--block-border-radius);
|
||||||
|
|
||||||
cite {
|
cite {
|
||||||
margin-top: $single-margin;
|
margin-top: var(--single-margin);
|
||||||
display: block;
|
display: block;
|
||||||
color: #aaa;
|
color: #aaa;
|
||||||
font-size: initial;
|
font-size: initial;
|
||||||
font-family: $text-font;
|
font-family: var(--text-font);
|
||||||
text-align: right;
|
text-align: right;
|
||||||
margin-right: $single-margin;
|
margin-right: var(--single-margin);
|
||||||
}
|
}
|
||||||
|
|
||||||
p {
|
p {
|
||||||
@@ -93,7 +91,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: $mobile-width) {
|
@media (max-width: var(--mobile-width)) {
|
||||||
.Article {
|
.Article {
|
||||||
--article-line-height: 1.5em;
|
--article-line-height: 1.5em;
|
||||||
--article-quote-padding: .5em;
|
--article-quote-padding: .5em;
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
@import "../variables";
|
|
||||||
|
|
||||||
.ArticleCard {
|
.ArticleCard {
|
||||||
--article-card-width: 30%;
|
--article-card-width: 30%;
|
||||||
--article-card-image-height: 15em;
|
--article-card-image-height: 15em;
|
||||||
@@ -78,7 +76,7 @@
|
|||||||
|
|
||||||
// Tablet
|
// Tablet
|
||||||
|
|
||||||
@media screen and (min-width: $tablet-min-width) and (max-width: $tablet-max-width) {
|
@media screen and (min-width: var(--tablet-min-width)) and (max-width: var(--tablet-max-width)) {
|
||||||
.ArticleCard {
|
.ArticleCard {
|
||||||
--article-card-image-height: 12em;
|
--article-card-image-height: 12em;
|
||||||
}
|
}
|
||||||
@@ -90,7 +88,7 @@
|
|||||||
|
|
||||||
// Mobile
|
// Mobile
|
||||||
|
|
||||||
@media (max-width: $mobile-width) {
|
@media (max-width: var(--mobile-width)) {
|
||||||
.ArticleCard {
|
.ArticleCard {
|
||||||
--article-card-width: 100%;
|
--article-card-width: 100%;
|
||||||
--article-card-margin: var(--single-margin) 0;
|
--article-card-margin: var(--single-margin) 0;
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
@import "../variables";
|
|
||||||
|
|
||||||
.HudozkaTitle {
|
.HudozkaTitle {
|
||||||
--padding: var(--double-margin) 0;
|
--padding: var(--double-margin) 0;
|
||||||
|
|
||||||
@@ -17,7 +15,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: $mobile-width) {
|
@media (max-width: var(--mobile-width)) {
|
||||||
.HudozkaTitle {
|
.HudozkaTitle {
|
||||||
--padding: 0 0 var(--double-margin);
|
--padding: 0 0 var(--double-margin);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
@import "../variables";
|
|
||||||
|
|
||||||
.PersonCard {
|
.PersonCard {
|
||||||
--person-card-width: 30%;
|
--person-card-width: 30%;
|
||||||
--person-card-height: 24em;
|
--person-card-height: 24em;
|
||||||
@@ -78,7 +76,7 @@
|
|||||||
|
|
||||||
// Tablet
|
// Tablet
|
||||||
|
|
||||||
@media screen and (min-width: $tablet-min-width) and (max-width: $tablet-max-width) {
|
@media screen and (min-width: var(--tablet-min-width)) and (max-width: var(--tablet-max-width)) {
|
||||||
.PersonCard {
|
.PersonCard {
|
||||||
--person-card-height: 20em;
|
--person-card-height: 20em;
|
||||||
}
|
}
|
||||||
@@ -90,7 +88,7 @@
|
|||||||
|
|
||||||
// Mobile
|
// Mobile
|
||||||
|
|
||||||
@media (max-width: $mobile-width) {
|
@media (max-width: var(--mobile-width)) {
|
||||||
.PersonCard {
|
.PersonCard {
|
||||||
--person-card-width: 100%;
|
--person-card-width: 100%;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
@import "../variables";
|
|
||||||
|
|
||||||
.accessibility-control {
|
.accessibility-control {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
padding: $single-margin;
|
padding: var(--single-margin);
|
||||||
|
|
||||||
span {
|
span {
|
||||||
margin-right: $single-margin;
|
margin-right: var(--single-margin);
|
||||||
}
|
}
|
||||||
|
|
||||||
span.active {
|
span.active {
|
||||||
@@ -14,12 +12,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&__block {
|
&__block {
|
||||||
margin-bottom: $half-margin;
|
margin-bottom: var(--half-margin);
|
||||||
}
|
}
|
||||||
|
|
||||||
ul li {
|
ul li {
|
||||||
display: inline;
|
display: inline;
|
||||||
margin-right: $single-margin;
|
margin-right: var(--single-margin);
|
||||||
}
|
}
|
||||||
|
|
||||||
ul {
|
ul {
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
@import "../variables";
|
|
||||||
|
|
||||||
.album-collection{
|
.album-collection{
|
||||||
&__body{
|
&__body{
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -8,11 +6,11 @@
|
|||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
align-items:center;
|
align-items:center;
|
||||||
|
|
||||||
margin: 0 0 $double-margin;
|
margin: 0 0 var(--double-margin);
|
||||||
}
|
}
|
||||||
|
|
||||||
&__title{
|
&__title{
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin: $single-margin 0;
|
margin: var(--single-margin )0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,13 +1,11 @@
|
|||||||
@import "../variables";
|
|
||||||
|
|
||||||
.album {
|
.album {
|
||||||
margin-bottom: $double-margin;
|
margin-bottom: var(--double-margin);
|
||||||
|
|
||||||
&__head {
|
&__head {
|
||||||
margin-bottom: 1.25em;
|
margin-bottom: 1.25em;
|
||||||
|
|
||||||
h3 {
|
h3 {
|
||||||
font-size: $biggest-font-size;
|
font-size: var(--biggest-font-size);
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -17,7 +15,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: $dark-color;
|
color: var(--dark-color);
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -28,7 +26,7 @@
|
|||||||
max-height: 40em;
|
max-height: 40em;
|
||||||
|
|
||||||
display: block;
|
display: block;
|
||||||
margin: 1.25em auto $small-margin;
|
margin: 1.25em auto var(--small-margin);
|
||||||
}
|
}
|
||||||
|
|
||||||
p{
|
p{
|
||||||
@@ -37,7 +35,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: $mobile-width) {
|
@media (max-width: var(--mobile-width)) {
|
||||||
.album {
|
.album {
|
||||||
&__body {
|
&__body {
|
||||||
img {
|
img {
|
||||||
|
|||||||
@@ -1,17 +1,15 @@
|
|||||||
@import "../variables";
|
|
||||||
|
|
||||||
$font-size: 0.75em;
|
|
||||||
$item-active-color: $link-color;
|
|
||||||
$item-inactive-color: $inactive-link-color;
|
|
||||||
$separator: '⟶';
|
|
||||||
$mobile-separator: '>';
|
|
||||||
|
|
||||||
breadcrumbs{
|
breadcrumbs{
|
||||||
|
--font-size: 0.75em;
|
||||||
|
--item-active-color: var(--link-color);
|
||||||
|
--item-inactive-color: var(--inactive-link-color);
|
||||||
|
--separator: '⟶';
|
||||||
|
--mobile-separator: '>';
|
||||||
|
|
||||||
padding: .35em 0 0;
|
padding: .35em 0 0;
|
||||||
color: $item-inactive-color;
|
color: var(--item-inactive-color);
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: $item-active-color;
|
color: var(--item-active-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
ul{
|
ul{
|
||||||
@@ -21,22 +19,22 @@ breadcrumbs{
|
|||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
|
||||||
&:not(:first-child):before{
|
&:not(:first-child):before{
|
||||||
content: $separator;
|
content: var(--separator);
|
||||||
margin-left: .4em;
|
margin-left: .4em;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: $mobile-width) {
|
@media screen and (max-width: var(--mobile-width)) {
|
||||||
breadcrumbs{
|
breadcrumbs{
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-size: $font-size;
|
font-size: var(--font-size);
|
||||||
|
|
||||||
ul{
|
ul{
|
||||||
li {
|
li {
|
||||||
&:not(:first-child):before{
|
&:not(:first-child):before{
|
||||||
content: $mobile-separator;
|
content: var(--mobile-separator);
|
||||||
margin-left: .2em;
|
margin-left: .2em;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
@import "../variables";
|
|
||||||
|
|
||||||
.document-row {
|
.document-row {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
@@ -20,7 +18,7 @@
|
|||||||
img {
|
img {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
max-height: 100%;
|
max-height: 100%;
|
||||||
box-shadow: $white-shadow;
|
box-shadow: var(--white-shadow);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,11 +37,11 @@
|
|||||||
.document-row__file-info {
|
.document-row__file-info {
|
||||||
width: 25%;
|
width: 25%;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
font-size: $smaller-font-size;
|
font-size: var(--smaller-font-size);
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: $mobile-width) {
|
@media (max-width: var(--mobile-width)) {
|
||||||
.document-row__file {
|
.document-row__file {
|
||||||
margin-left: var(--half-margin);
|
margin-left: var(--half-margin);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
@import "../variables";
|
|
||||||
|
|
||||||
.document {
|
.document {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin-bottom: $double-margin;
|
margin-bottom: var(--double-margin);
|
||||||
}
|
}
|
||||||
|
|
||||||
.document__image{
|
.document__image{
|
||||||
@@ -13,7 +11,7 @@
|
|||||||
//width: 20em;
|
//width: 20em;
|
||||||
//height: 20em;
|
//height: 20em;
|
||||||
|
|
||||||
margin-bottom: $single-margin;
|
margin-bottom: var(--single-margin);
|
||||||
|
|
||||||
//display: flex;
|
//display: flex;
|
||||||
//justify-content: center;
|
//justify-content: center;
|
||||||
@@ -23,7 +21,7 @@
|
|||||||
img {
|
img {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
max-height: 100%;
|
max-height: 100%;
|
||||||
box-shadow: $white-shadow;
|
box-shadow: var(--white-shadow);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,11 +37,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.document__file-info {
|
.document__file-info {
|
||||||
font-size: $smaller-font-size;
|
font-size: var(--smaller-font-size);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: $mobile-width) {
|
@media (max-width: var(--mobile-width)) {
|
||||||
.document {
|
.document {
|
||||||
margin: 0 $single-margin/2 $single-margin;
|
margin: 0 var(--single-margin) var(--single-margin);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,8 @@
|
|||||||
@import "../variables";
|
|
||||||
|
|
||||||
.documents-container {
|
.documents-container {
|
||||||
margin-bottom: $post-margin;
|
margin-bottom: var(--post-margin);
|
||||||
|
|
||||||
&__head {
|
&__head {
|
||||||
margin-bottom: $double-margin;
|
margin-bottom: var(--double-margin);
|
||||||
}
|
}
|
||||||
|
|
||||||
&__body {
|
&__body {
|
||||||
@@ -14,7 +12,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: $mobile-width) {
|
@media (max-width: var(--mobile-width)) {
|
||||||
.documents-container {
|
.documents-container {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,12 +1,10 @@
|
|||||||
@import "../variables";
|
|
||||||
|
|
||||||
.footer-links {
|
.footer-links {
|
||||||
&__header {
|
&__header {
|
||||||
margin-bottom: $single-margin;
|
margin-bottom: var(--single-margin);
|
||||||
}
|
}
|
||||||
|
|
||||||
&__link {
|
&__link {
|
||||||
margin-bottom: $half-margin;
|
margin-bottom: var(--half-margin);
|
||||||
}
|
}
|
||||||
|
|
||||||
ul {
|
ul {
|
||||||
|
|||||||
@@ -1,14 +1,8 @@
|
|||||||
@import "../variables";
|
|
||||||
|
|
||||||
$img-max-width: 500px;
|
|
||||||
$double-margin-half: $double-margin / 2;
|
|
||||||
$percentage: 25%;
|
|
||||||
|
|
||||||
.gallery-item {
|
.gallery-item {
|
||||||
box-sizing: padding-box;
|
box-sizing: padding-box;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
||||||
margin: 0 $single-margin $double-margin;
|
margin: 0 var(--single-margin) var(--double-margin);
|
||||||
|
|
||||||
a.invisible {
|
a.invisible {
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
@@ -16,8 +10,8 @@ $percentage: 25%;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: $mobile-width) {
|
@media screen and (max-width: var(--mobile-width)) {
|
||||||
.gallery-item {
|
.gallery-item {
|
||||||
margin: 0 0 $double-margin;
|
margin: 0 0 var(--double-margin);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,5 @@
|
|||||||
@import "../variables";
|
|
||||||
|
|
||||||
.main-menu {
|
.main-menu {
|
||||||
font-size: $normal-font-size;
|
font-size: var(--normal-font-size);
|
||||||
|
|
||||||
list-style: none;
|
list-style: none;
|
||||||
|
|
||||||
@@ -35,45 +33,45 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: $dark-color;
|
color: var(--dark-color);
|
||||||
border-color: lighten($dark-color, 60%);
|
border-color: var(--dark-color);
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
border-color: lighten($dark-color, 30%);
|
border-color: var(--dark-color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.selected {
|
.selected {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
border-top: $menu-colored-mark-thickness solid $light-color;
|
border-top: var(--menu-colored-mark-thickness) solid var(--light-color);
|
||||||
|
|
||||||
&.blue {
|
&.blue {
|
||||||
border-top: $menu-colored-mark-thickness solid $blue-color;
|
border-top: var(--menu-colored-mark-thickness) solid var(--blue-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.orange {
|
&.orange {
|
||||||
border-top: $menu-colored-mark-thickness solid $orange-color;
|
border-top: var(--menu-colored-mark-thickness) solid var(--orange-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.green {
|
&.green {
|
||||||
border-top: $menu-colored-mark-thickness solid $green-color;
|
border-top: var(--menu-colored-mark-thickness) solid var(--green-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.yellow {
|
&.yellow {
|
||||||
border-top: $menu-colored-mark-thickness solid $yellow-color;
|
border-top: var(--menu-colored-mark-thickness) solid var(--yellow-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.pink {
|
&.pink {
|
||||||
border-top: $menu-colored-mark-thickness solid $pink-color;
|
border-top: var(--menu-colored-mark-thickness) solid var(--pink-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.red {
|
&.red {
|
||||||
border-top: $menu-colored-mark-thickness solid $red-color;
|
border-top: var(--menu-colored-mark-thickness) solid var(--red-color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: $mobile-width) {
|
@media (max-width: var(--mobile-width)) {
|
||||||
.main-menu{
|
.main-menu{
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
@import "../variables";
|
|
||||||
|
|
||||||
.navigation {
|
.navigation {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
@@ -12,14 +10,14 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.navigation__right {
|
.navigation__right {
|
||||||
color: $dark-color;
|
color: var(--dark-color);
|
||||||
font-size: $normal-font-size;
|
font-size: var(--normal-font-size);
|
||||||
margin-right: 1em;
|
margin-right: 1em;
|
||||||
|
|
||||||
z-index: 1000;
|
z-index: 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: $mobile-width) {
|
@media (max-width: var(--mobile-width)) {
|
||||||
.navigation {
|
.navigation {
|
||||||
background-color: white;
|
background-color: white;
|
||||||
}
|
}
|
||||||
@@ -29,7 +27,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (min-width: $tablet-min-width) and (max-width: $tablet-max-width) {
|
@media screen and (min-width: var(--tablet-min-width)) and (max-width: var(--tablet-max-width)) {
|
||||||
@media screen and (orientation: portrait) {
|
@media screen and (orientation: portrait) {
|
||||||
.navigation__right {
|
.navigation__right {
|
||||||
display: none;
|
display: none;
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
@import "../variables";
|
|
||||||
|
|
||||||
.page-collective {
|
.page-collective {
|
||||||
&.ng-leave {
|
&.ng-leave {
|
||||||
animation: slideOutLeft $short-duration both ease-in;
|
animation: slideOutLeft var(--short-duration) both ease-in;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.ng-enter {
|
&.ng-enter {
|
||||||
animation: slideInRight $short-duration both ease-in;
|
animation: slideInRight var(--short-duration) both ease-in;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -16,7 +14,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: $mobile-width) {
|
@media (max-width: var(--mobile-width)) {
|
||||||
.page-collective {
|
.page-collective {
|
||||||
&__collective-image {
|
&__collective-image {
|
||||||
display: none;
|
display: none;
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
@import "../variables";
|
|
||||||
|
|
||||||
.page-schedule {
|
.page-schedule {
|
||||||
&.ng-leave {
|
&.ng-leave {
|
||||||
transform-origin: 0% 0%;
|
transform-origin: 0% 0%;
|
||||||
animation: rotateFall $short-duration both ease-in;
|
animation: rotateFall var(--short-duration) both ease-in;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.ng-enter {
|
&.ng-enter {
|
||||||
animation: scaleUp $short-duration both ease-in;
|
animation: scaleUp var(--short-duration) both ease-in;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,29 +1,26 @@
|
|||||||
@import "../variables";
|
|
||||||
|
|
||||||
$current-week-day-color: $blue-color;
|
|
||||||
|
|
||||||
$cell-width: 9em;
|
|
||||||
$cell-tablet-width: 7.5em;
|
|
||||||
|
|
||||||
$group-cell-width: 4em;
|
|
||||||
$group-cell-tablet-width: 3em;
|
|
||||||
|
|
||||||
$current-week-day-color: $blue-color;
|
|
||||||
$row-padding: $single-margin 0;
|
|
||||||
$current-day-column-border-thickness: 3px;
|
|
||||||
|
|
||||||
.schedule-table {
|
.schedule-table {
|
||||||
margin-bottom: $double-margin;
|
--current-week-day-color: var(--blue-color);
|
||||||
|
|
||||||
|
--cell-width: 9em;
|
||||||
|
--cell-tablet-width: 7.5em;
|
||||||
|
|
||||||
|
--group-cell-width: 4em;
|
||||||
|
--group-cell-tablet-width: 3em;
|
||||||
|
|
||||||
|
--row-padding: var(--single-margin) 0;
|
||||||
|
--current-day-column-border-thickness: 3px;
|
||||||
|
|
||||||
|
margin-bottom: var(--double-margin);
|
||||||
|
|
||||||
max-width: 70em;
|
max-width: 70em;
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
|
|
||||||
a{
|
a{
|
||||||
color: $dark-color;
|
color: var(--dark-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.lesson {
|
.lesson {
|
||||||
margin-bottom: $half-margin;
|
margin-bottom: var(--half-margin);
|
||||||
}
|
}
|
||||||
|
|
||||||
.teacher {
|
.teacher {
|
||||||
@@ -31,8 +28,8 @@ $current-day-column-border-thickness: 3px;
|
|||||||
}
|
}
|
||||||
|
|
||||||
.current {
|
.current {
|
||||||
border-left: $current-day-column-border-thickness solid;
|
border-left: var(--current-day-column-border-thickness) solid;
|
||||||
border-right: $current-day-column-border-thickness solid;
|
border-right: var(--current-day-column-border-thickness) solid;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__body {
|
&__body {
|
||||||
@@ -46,8 +43,8 @@ $current-day-column-border-thickness: 3px;
|
|||||||
}
|
}
|
||||||
|
|
||||||
&__body-group{
|
&__body-group{
|
||||||
min-width: $group-cell-width;
|
min-width: var(--group-cell-width);
|
||||||
padding: $row-padding;
|
padding: var(--row-padding);
|
||||||
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
||||||
@@ -58,14 +55,14 @@ $current-day-column-border-thickness: 3px;
|
|||||||
}
|
}
|
||||||
|
|
||||||
&__head-record {
|
&__head-record {
|
||||||
min-width: $cell-width;
|
min-width: var(--cell-width);
|
||||||
max-width: $cell-width;
|
max-width: var(--cell-width);
|
||||||
padding: $row-padding;
|
padding: var(--row-padding);
|
||||||
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
||||||
& > .group {
|
& > .group {
|
||||||
min-width: $group-cell-width;
|
min-width: var(--group-cell-width);
|
||||||
}
|
}
|
||||||
|
|
||||||
.date {
|
.date {
|
||||||
@@ -75,19 +72,19 @@ $current-day-column-border-thickness: 3px;
|
|||||||
}
|
}
|
||||||
|
|
||||||
&__record{
|
&__record{
|
||||||
min-width: $cell-width;
|
min-width: var(--cell-width);
|
||||||
max-width: $cell-width;
|
max-width: var(--cell-width);
|
||||||
padding: $row-padding;
|
padding: var(--row-padding);
|
||||||
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__course{
|
&__course{
|
||||||
margin-bottom: $half-margin;
|
margin-bottom: var(--half-margin);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (min-width: $tablet-min-width) and (max-width: $tablet-max-width) {
|
@media screen and (min-width: var(--tablet-min-width)) and (max-width: var(--tablet-max-width)) {
|
||||||
.schedule-table {
|
.schedule-table {
|
||||||
min-width: 900px;
|
min-width: 900px;
|
||||||
|
|
||||||
@@ -103,16 +100,16 @@ $current-day-column-border-thickness: 3px;
|
|||||||
|
|
||||||
&__head{
|
&__head{
|
||||||
& > div{
|
& > div{
|
||||||
min-width: $cell-tablet-width;
|
min-width: var(--cell-tablet-width);
|
||||||
}
|
}
|
||||||
|
|
||||||
& > .group{
|
& > .group{
|
||||||
min-width: $group-cell-tablet-width;
|
min-width: var(--group-cell-tablet-width);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__body-group{
|
&__body-group{
|
||||||
min-width: $group-cell-tablet-width;
|
min-width: var(--group-cell-tablet-width);
|
||||||
|
|
||||||
p.name {
|
p.name {
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
@@ -120,7 +117,7 @@ $current-day-column-border-thickness: 3px;
|
|||||||
}
|
}
|
||||||
|
|
||||||
&__record{
|
&__record{
|
||||||
min-width: $cell-tablet-width;
|
min-width: var(--cell-tablet-width);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,7 @@
|
|||||||
@import "../variables";
|
|
||||||
|
|
||||||
.timeline__record {
|
.timeline__record {
|
||||||
margin: 0 0 $post-margin;
|
margin: 0 0 var(--post-margin);
|
||||||
|
|
||||||
.date {
|
.date {
|
||||||
color: $light-color;
|
color: var(--light-color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,7 @@
|
|||||||
@import "../variables";
|
|
||||||
|
|
||||||
.timeline{
|
.timeline{
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
margin: $double-margin auto;
|
margin: var(--double-margin) auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.timeline__footer{
|
.timeline__footer{
|
||||||
@@ -18,12 +16,12 @@
|
|||||||
|
|
||||||
width: 100px;
|
width: 100px;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
border: 2px solid $dark-color;
|
border: 2px solid var(--dark-color);
|
||||||
padding: 0 10px;
|
padding: 0 10px;
|
||||||
color: $dark-color;
|
color: var(--dark-color);
|
||||||
background: $lighter-color;
|
background: var(--lighter-color);
|
||||||
|
|
||||||
font-size: $normal-font-size;
|
font-size: var(--normal-font-size);
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: gainsboro;
|
background: gainsboro;
|
||||||
|
|||||||
+11
-14
@@ -1,26 +1,24 @@
|
|||||||
@import "variables";
|
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
margin: 0 auto 1em;
|
margin: 0 auto 1em;
|
||||||
|
|
||||||
&.content_full{
|
&.content_full{
|
||||||
width: $content-full;
|
width: var(--content-full);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.content_wide{
|
&.content_wide{
|
||||||
width: $content-wide;
|
width: var(--content-wide);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.content_semi-wide{
|
&.content_semi-wide{
|
||||||
width: $content-semi-wide;
|
width: var(--content-semi-wide);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.content_thin{
|
&.content_thin{
|
||||||
width: $content-thin;
|
width: var(--content-thin);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.content--padding-top--menu{
|
&.content--padding-top--menu{
|
||||||
padding-top: $menu-thickness;
|
padding-top: var(--menu-thickness);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,21 +40,20 @@
|
|||||||
animation: smallSlideInFromTop 350ms ease-out;
|
animation: smallSlideInFromTop 350ms ease-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: var(--mobile-width)) {
|
||||||
@media screen and (max-width: $mobile-width) {
|
--content-mobile: 95%;
|
||||||
$content-mobile: 95%;
|
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
&.content_wide{
|
&.content_wide{
|
||||||
width: $content-mobile;
|
width: var(--content-mobile);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.content_semi-wide{
|
&.content_semi-wide{
|
||||||
width: $content-mobile;
|
width: var(--content-mobile);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.content_thin{
|
&.content_thin{
|
||||||
width: $content-mobile;
|
width: var(--content-mobile);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.content--padding-top--menu{
|
&.content--padding-top--menu{
|
||||||
@@ -65,7 +62,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (min-width: $tablet-min-width) and (max-width: $tablet-max-width) {
|
@media screen and (min-width: var(--tablet-min-width)) and (max-width: var(--tablet-max-width)) {
|
||||||
$thin-value: 70%;
|
$thin-value: 70%;
|
||||||
$wide-value: 95%;
|
$wide-value: 95%;
|
||||||
$semi-wide-value: 80%;
|
$semi-wide-value: 80%;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
@import "variables";
|
@import "variables";
|
||||||
|
|
||||||
footer {
|
footer {
|
||||||
padding: 0 $half-margin $half-margin;
|
padding: 0 var(--half-margin) var(--half-margin);
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
.footer-info {
|
.footer-info {
|
||||||
@@ -9,19 +9,19 @@ footer {
|
|||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
|
||||||
padding: $double-margin $single-margin 0;
|
padding: var(--double-margin) var(--single-margin) 0;
|
||||||
margin-bottom: $double-margin;
|
margin-bottom: var(--double-margin);
|
||||||
}
|
}
|
||||||
|
|
||||||
.contacts {
|
.contacts {
|
||||||
margin: $double-margin 0 $half-margin;
|
margin: var(--double-margin) 0 var(--half-margin);
|
||||||
|
|
||||||
& > div {
|
& > div {
|
||||||
padding: 0 $single-margin;
|
padding: 0 var(--single-margin);
|
||||||
}
|
}
|
||||||
|
|
||||||
p {
|
p {
|
||||||
margin: $half-margin $single-margin;
|
margin: var(--half-margin) var(--single-margin);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+10
-12
@@ -1,14 +1,12 @@
|
|||||||
@import "variables";
|
|
||||||
|
|
||||||
$hmenu-margin: 6px;
|
|
||||||
$hmenu-icon-width: 32px;
|
|
||||||
$hmenu-icon-height: 32px;
|
|
||||||
|
|
||||||
.no-scroll {
|
.no-scroll {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.HMenu {
|
.HMenu {
|
||||||
|
--hmenu-margin: 6px;
|
||||||
|
--hmenu-icon-width: 32px;
|
||||||
|
--hmenu-icon-height: 32px;
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -39,7 +37,7 @@ $hmenu-icon-height: 32px;
|
|||||||
|
|
||||||
.HMenu--folded.HMenu--open {
|
.HMenu--folded.HMenu--open {
|
||||||
li {
|
li {
|
||||||
margin: 0 0 $single-margin;
|
margin: 0 0 var(--single-margin);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,9 +61,9 @@ $hmenu-icon-height: 32px;
|
|||||||
.HMenu__toggle {
|
.HMenu__toggle {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
height: $hmenu-icon-height;
|
height: var(--hmenu-icon-height);
|
||||||
|
|
||||||
margin: $hmenu-margin;
|
margin: var(--hmenu-margin);
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
||||||
@@ -73,8 +71,8 @@ $hmenu-icon-height: 32px;
|
|||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
border: none;
|
border: none;
|
||||||
|
|
||||||
width: $hmenu-icon-width;
|
width: var(--hmenu-icon-width);
|
||||||
height: $hmenu-icon-height;
|
height: var(--hmenu-icon-height);
|
||||||
background-size: 100%;
|
background-size: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,7 +155,7 @@ $hmenu-icon-height: 32px;
|
|||||||
margin-bottom: 2em;
|
margin-bottom: 2em;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: $mobile-width) {
|
@media (max-width: var(--mobile-width)) {
|
||||||
.HMenu--folded .HMenu__item {
|
.HMenu--folded .HMenu__item {
|
||||||
max-width: 10em;
|
max-width: 10em;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
@import "../variables";
|
|
||||||
|
|
||||||
.kazimir__image {
|
.kazimir__image {
|
||||||
--kazimir-caption-margin: 20px;
|
--kazimir-caption-margin: 20px;
|
||||||
|
|
||||||
@@ -17,7 +15,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
figure {
|
figure {
|
||||||
margin: 0 0 $single-margin;
|
margin: 0 0 var(--single-margin);
|
||||||
}
|
}
|
||||||
|
|
||||||
figcaption {
|
figcaption {
|
||||||
@@ -31,7 +29,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: $mobile-width) {
|
@media screen and (max-width: var(--mobile-width)) {
|
||||||
.kazimir__image {
|
.kazimir__image {
|
||||||
--kazimir-caption-margin: 10px;
|
--kazimir-caption-margin: 10px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
@import "../variables";
|
|
||||||
|
|
||||||
.kazimir__video {
|
.kazimir__video {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 0;
|
height: 0;
|
||||||
position: relative;
|
position: relative;
|
||||||
padding-bottom: $embed-video-ratio;
|
padding-bottom: var(--embed-video-ratio);
|
||||||
|
|
||||||
margin-bottom: $single-margin;
|
margin-bottom: var(--single-margin);
|
||||||
|
|
||||||
iframe {
|
iframe {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
@import "variables";
|
|
||||||
|
|
||||||
.main-image{
|
.main-image{
|
||||||
display: none;
|
display: none;
|
||||||
|
|
||||||
@@ -20,7 +18,7 @@
|
|||||||
//}
|
//}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: $mobile-width) {
|
@media (max-width: var(--mobile-width)) {
|
||||||
.main-image {
|
.main-image {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
@import "variables";
|
|
||||||
|
|
||||||
.schedule-selector {
|
.schedule-selector {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin-bottom: $single-margin;
|
margin-bottom: var(--single-margin);
|
||||||
|
|
||||||
label{
|
label{
|
||||||
margin: 0 .5em;
|
margin: 0 .5em;
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
@import "components/breadcrumbs";
|
@import "components/breadcrumbs";
|
||||||
@import "components/timeline";
|
@import "components/timeline";
|
||||||
@import "components/timeline-record";
|
@import "components/timeline-record";
|
||||||
@import "components/timeline-record-instagram";
|
//@import "components/timeline-record-instagram";
|
||||||
@import "components/schedule-table";
|
@import "components/schedule-table";
|
||||||
@import "components/footer-links";
|
@import "components/footer-links";
|
||||||
@import "components/Article";
|
@import "components/Article";
|
||||||
@@ -46,14 +46,14 @@
|
|||||||
// THEMES
|
// THEMES
|
||||||
|
|
||||||
@import "themes/theme-default";
|
@import "themes/theme-default";
|
||||||
@import "themes/theme-accessibility-black-white";
|
//@import "themes/theme-accessibility-black-white";
|
||||||
@import "themes/theme-accessibility-white-black";
|
//@import "themes/theme-accessibility-white-black";
|
||||||
@import "themes/theme-accessibility-font-big";
|
//@import "themes/theme-accessibility-font-big";
|
||||||
@import "themes/theme-accessibility-font-biggest";
|
//@import "themes/theme-accessibility-font-biggest";
|
||||||
@import "themes/theme-accessibility-font-sans";
|
//@import "themes/theme-accessibility-font-sans";
|
||||||
@import "themes/theme-accessibility-font-serif";
|
//@import "themes/theme-accessibility-font-serif";
|
||||||
@import "themes/theme-accessibility-no-image";
|
//@import "themes/theme-accessibility-no-image";
|
||||||
@import "themes/theme-accessibility-kerning-big";
|
//@import "themes/theme-accessibility-kerning-big";
|
||||||
|
|
||||||
@import "marker/MarkerTable";
|
@import "marker/MarkerTable";
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +1,16 @@
|
|||||||
@import "../variables";
|
|
||||||
|
|
||||||
.theme-default {
|
.theme-default {
|
||||||
h1, h2{
|
h1, h2{
|
||||||
font-family: $title-font;
|
font-family: var(--title-font);
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: $dark-color;
|
color: var(--dark-color);
|
||||||
|
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
border-bottom: 1px solid lighten($dark-color, 60%);
|
border-bottom: 1px solid var(--link-color);
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
border-bottom: 2px solid $dark-color;
|
border-bottom: 2px solid var(--dark-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
&:active {
|
&:active {
|
||||||
@@ -34,7 +32,7 @@
|
|||||||
|
|
||||||
.schedule-table {
|
.schedule-table {
|
||||||
a{
|
a{
|
||||||
color: $dark-color;
|
color: var(--dark-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.lesson {
|
.lesson {
|
||||||
@@ -42,7 +40,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.current {
|
.current {
|
||||||
border-color: $current-week-day-color;
|
border-color: var(--current-week-day-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
&__head{
|
&__head{
|
||||||
@@ -53,23 +51,23 @@
|
|||||||
|
|
||||||
&__body-row{
|
&__body-row{
|
||||||
&:nth-child(even) {
|
&:nth-child(even) {
|
||||||
background-color: $schedule-row-color;
|
background-color: var(--schedule-row-color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
footer {
|
footer {
|
||||||
color: $light-color;
|
color: var(--light-color);
|
||||||
background-color: $darker-color;
|
background-color: var(--darker-color);
|
||||||
|
|
||||||
font-size: $smaller-font-size;
|
font-size: var(--smaller-font-size);
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: $light-color;
|
color: var(--light-color);
|
||||||
border-bottom-color: darken($light-color, 50%);
|
border-bottom-color: var(--light-color);
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
border-bottom-color: $light-color;
|
border-bottom-color: var(--light-color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+57
-71
@@ -1,76 +1,39 @@
|
|||||||
//SIZE
|
:root {
|
||||||
$mobile-width: 31.25em;
|
|
||||||
$desktop-width: 59.99999em;
|
|
||||||
|
|
||||||
$tablet-min-width: 31.25em;
|
|
||||||
$tablet-max-width: 64em;
|
|
||||||
|
|
||||||
$content-full: 100%;
|
|
||||||
$content-wide: 90%;
|
|
||||||
$content-semi-wide: 70%;
|
|
||||||
$content-thin: 60%;
|
|
||||||
|
|
||||||
$embed-video-ratio: 56.25%;
|
|
||||||
|
|
||||||
//MARGINS
|
|
||||||
$small-margin: .2em;
|
|
||||||
$half-margin: .5em;
|
|
||||||
$single-margin: 1.0em;
|
|
||||||
$double-margin: 2.0em;
|
|
||||||
$post-margin: 5.0em;
|
|
||||||
|
|
||||||
//COLOR
|
|
||||||
$dark-color: hsl(0, 0%, 20%);
|
|
||||||
$darker-color: hsl(0, 0%, 10%);
|
|
||||||
$light-color: hsl(0, 0%, 80%);
|
|
||||||
$lighter-color: hsl(0, 0%, 100%);
|
|
||||||
|
|
||||||
$blue-color: rgb(63, 156, 234);
|
|
||||||
$orange-color: rgb(240, 160, 65);
|
|
||||||
$green-color: rgb(92, 190, 64);
|
|
||||||
$yellow-color: rgb(255, 238, 0);
|
|
||||||
$pink-color: rgb(246, 85, 160);
|
|
||||||
$magenta-color: rgb(214, 39, 246);
|
|
||||||
$red-color: rgb(246, 27, 20);
|
|
||||||
$gray-color: rgb(153, 154, 160);
|
|
||||||
|
|
||||||
$link-color: $blue-color;
|
|
||||||
$inactive-link-color: $light-color;
|
|
||||||
|
|
||||||
$schedule-row-color: hsl(210, 55%, 90%);
|
|
||||||
|
|
||||||
//SHADOW
|
//SHADOW
|
||||||
$white-shadow: 0 .1em 0.3em rgba(0, 0, 0, 0.2);
|
--white-shadow: 0 .1em 0.3em rgba(0, 0, 0, 0.2);
|
||||||
|
|
||||||
//FONTS
|
//FONTS
|
||||||
$title-font: "PT Serif", Georgia, serif;
|
--title-font: "PT Serif", Georgia, serif;
|
||||||
$text-font: "PT Sans", Arial, sans-serif;
|
--text-font: "PT Sans", Arial, sans-serif;
|
||||||
|
|
||||||
$smaller-font-size: 0.9rem;
|
--smaller-font-size: 0.9rem;
|
||||||
$normal-font-size: 1.0rem;
|
--normal-font-size: 1.0rem;
|
||||||
$bigger-font-size: 1.2rem;
|
--bigger-font-size: 1.2rem;
|
||||||
$big-font-size: 1.5rem;
|
--big-font-size: 1.5rem;
|
||||||
$biggest-font-size: 2.25rem;
|
--biggest-font-size: 2.25rem;
|
||||||
|
|
||||||
//MENU
|
//MENU
|
||||||
$menu-thickness: 2.5em;
|
--menu-thickness: 2.5em;
|
||||||
$menu-colored-mark-thickness: 4px;
|
--menu-colored-mark-thickness: 4px;
|
||||||
$menu-inactive-item-color: hsl(0, 0%, 50%);
|
--menu-inactive-item-color: hsl(0, 0%, 50%);
|
||||||
|
|
||||||
//ANIMATION
|
//ANIMATION
|
||||||
$short-duration: 0.3s;
|
$short-duration: 0.3s;
|
||||||
|
|
||||||
|
//SIZE
|
||||||
|
--mobile-width: 31.25em;
|
||||||
|
--desktop-width: 59.99999em;
|
||||||
|
|
||||||
@media (max-width: $mobile-width) {
|
--tablet-min-width: 31.25em;
|
||||||
//FONTS
|
--tablet-max-width: 64em;
|
||||||
$smaller-font-size: 0.9rem;
|
|
||||||
$normal-font-size: 1.0rem;
|
--content-full: 100%;
|
||||||
$bigger-font-size: 1.1rem;
|
--content-wide: 90%;
|
||||||
$big-font-size: 1.3rem;
|
--content-semi-wide: 70%;
|
||||||
$biggest-font-size: 2.0rem;
|
--content-thin: 60%;
|
||||||
}
|
|
||||||
|
--embed-video-ratio: 56.25%;
|
||||||
|
|
||||||
:root {
|
|
||||||
--text-color: #303030;
|
--text-color: #303030;
|
||||||
--text-color-second: #ccc;
|
--text-color-second: #ccc;
|
||||||
|
|
||||||
@@ -83,24 +46,47 @@ $short-duration: 0.3s;
|
|||||||
--font-size-h3: 1.5rem;
|
--font-size-h3: 1.5rem;
|
||||||
|
|
||||||
//COLOR
|
//COLOR
|
||||||
--dark-color: $dark-color;
|
--dark-color: hsl(0, 0%, 20%);
|
||||||
--darker-color: $darker-color;
|
--darker-color: hsl(0, 0%, 10%);
|
||||||
--light-color: $light-color;
|
--light-color: hsl(0, 0%, 80%);
|
||||||
--lighter-color: $lighter-color;
|
--lighter-color: hsl(0, 0%, 100%);
|
||||||
|
--link-color: hsl(0, 0%, 80%);
|
||||||
|
|
||||||
|
--blue-color: rgb(63, 156, 234);
|
||||||
|
--orange-color: rgb(240, 160, 65);
|
||||||
|
--green-color: rgb(92, 190, 64);
|
||||||
|
--yellow-color: rgb(255, 238, 0);
|
||||||
|
--pink-color: rgb(246, 85, 160);
|
||||||
|
--magenta-color: rgb(214, 39, 246);
|
||||||
|
--red-color: rgb(246, 27, 20);
|
||||||
|
--gray-color: rgb(153, 154, 160);
|
||||||
|
|
||||||
|
--inactive-link-color: var(--light-color);
|
||||||
|
|
||||||
|
--schedule-row-color: hsl(210, 55%, 90%);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//MARGINS
|
//MARGINS
|
||||||
--small-margin: $small-margin;
|
--small-margin: .2em;
|
||||||
--half-margin: $half-margin;
|
--half-margin: .5em;
|
||||||
--single-margin: $single-margin;
|
--single-margin: 1.0em;
|
||||||
--double-margin: $double-margin;
|
--double-margin: 2.0em;
|
||||||
--post-margin: $post-margin;
|
--post-margin: 5.0em;
|
||||||
|
|
||||||
//OTHER
|
//OTHER
|
||||||
--block-border-radius: 3px;
|
--block-border-radius: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: $mobile-width) {
|
@media (max-width: var(--mobile-width)) {
|
||||||
:root {
|
:root {
|
||||||
|
//FONTS
|
||||||
|
--smaller-font-size: 0.9rem;
|
||||||
|
--normal-font-size: 1.0rem;
|
||||||
|
--bigger-font-size: 1.1rem;
|
||||||
|
--big-font-size: 1.3rem;
|
||||||
|
--biggest-font-size: 2.0rem;
|
||||||
|
|
||||||
--font-size-h1: 1.75rem;
|
--font-size-h1: 1.75rem;
|
||||||
--font-size-h2: 1.50rem;
|
--font-size-h2: 1.50rem;
|
||||||
--font-size-h3: 1.25rem;
|
--font-size-h3: 1.25rem;
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="ru">
|
||||||
|
<head>
|
||||||
|
<base href="/">
|
||||||
|
|
||||||
|
<title>Edit Hudozka</title>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
|
||||||
|
<meta name="viewport" content="width=device-width, maximum-scale=1.0"/>
|
||||||
|
<meta name="referrer" content="unsafe-url">
|
||||||
|
|
||||||
|
<link rel="icon" type="image/png" href="//static.shlisselburg.org/art/graphics/favicon-32.png"/>
|
||||||
|
<link rel="apple-touch-icon" sizes="57x57" href="//static.shlisselburg.org/art/graphics/favicon-57.png"/>
|
||||||
|
<link rel="apple-touch-icon" sizes="72x72" href="//static.shlisselburg.org/art/graphics/favicon-72.png"/>
|
||||||
|
<link rel="apple-touch-icon" sizes="114x114" href="//static.shlisselburg.org/art/graphics/favicon-114.png"/>
|
||||||
|
<link rel="apple-touch-icon" sizes="144x144" href="//static.shlisselburg.org/art/graphics/favicon-144.png"/>
|
||||||
|
|
||||||
|
<!--<link href="https://fonts.googleapis.com/css?family=PT+Sans|PT+Serif" rel="stylesheet" as="font">-->
|
||||||
|
<link rel="stylesheet" href="/style.css">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
<script src="edit.js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
Loaded 100 of 107 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user