Orzu Ionut
3 years ago
47 changed files with 24754 additions and 56954 deletions
-
36app/Http/Controllers/RegexController.php
-
7app/Http/Controllers/SearchAndDisplaceController.php
-
80app/Http/Controllers/SearcherController.php
-
28app/SearchDisplace/Regex/RegexFactory.php
-
27app/SearchDisplace/SearchAndDisplace.php
-
4app/SearchDisplace/SearchAndDisplaceFromFiles.php
-
202app/SearchDisplace/Searchers/Searcher.php
-
36app/SearchDisplace/Searchers/SearcherCreator.php
-
18app/SearchDisplace/Searchers/SearcherFactory.php
-
50app/SearchDisplace/Searchers/SearchersStorage.php
-
51585package-lock.json
-
3package.json
-
890public/css/app.css
-
17342public/js/app.js
-
23resources/js/app.ts
-
4resources/js/bootstrap.ts
-
2resources/js/components/ProcessFile.vue
-
69resources/js/components/Regex/Create.vue
-
46resources/js/components/Regex/Flags.vue
-
45resources/js/components/Regex/PatternBox.vue
-
225resources/js/components/Regex/SideBar.vue
-
39resources/js/components/Regex/TextBox.vue
-
62resources/js/components/Searchers/AddBox.vue
-
65resources/js/components/Searchers/Create.vue
-
65resources/js/components/Searchers/Index.vue
-
50resources/js/components/Searchers/Show.vue
-
54resources/sass/_layout.sass
-
60resources/sass/_layout.scss
-
7resources/sass/_variables.sass
-
7resources/sass/_variables.scss
-
79resources/sass/app.sass
-
48resources/sass/app.scss
-
1resources/sass/components/_index.sass
-
1resources/sass/components/regex/_index.sass
-
9resources/sass/components/regex/create/_flags.sass
-
43resources/sass/components/regex/create/_index.sass
-
26resources/sass/components/regex/create/_pattern-box.sass
-
63resources/sass/components/regex/create/_references.sass
-
63resources/sass/components/regex/create/_text-box.sass
-
10resources/views/app.blade.php
-
12resources/views/pages/home.blade.php
-
5resources/views/pages/regex/create.blade.php
-
5resources/views/pages/searchers/create.blade.php
-
7resources/views/pages/searchers/show.blade.php
-
13routes/web.php
-
2webpack.mix.js
-
10190yarn.lock
@ -0,0 +1,36 @@ |
|||
<?php |
|||
|
|||
namespace App\Http\Controllers; |
|||
|
|||
use App\SearchDisplace\Regex\RegexFactory; |
|||
|
|||
class RegexController extends Controller |
|||
{ |
|||
public function create() |
|||
{ |
|||
return view('pages.regex.create'); |
|||
} |
|||
|
|||
public function store() |
|||
{ |
|||
request()->validate([ |
|||
'name' => 'required', |
|||
'expression' => 'required', |
|||
]); |
|||
|
|||
try { |
|||
$factory = new RegexFactory(request()->get('name'), request()->get('expression')); |
|||
|
|||
$factory->create(); |
|||
|
|||
return response()->json([ |
|||
'status' => 'success', |
|||
], 200); |
|||
} catch (\Exception $exception) { |
|||
return response()->json([ |
|||
'status' => 'fail', |
|||
'message' => $exception->getMessage(), |
|||
], 400); |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,80 @@ |
|||
<?php |
|||
|
|||
namespace App\Http\Controllers; |
|||
|
|||
use App\SearchDisplace\Searchers\SearcherFactory; |
|||
use App\SearchDisplace\Searchers\SearchersStorage; |
|||
|
|||
class SearcherController extends Controller |
|||
{ |
|||
public function index() |
|||
{ |
|||
// @TODO Add filters
|
|||
if (request()->has('q')) { |
|||
$searchers = []; |
|||
} else { |
|||
$searchers = (new SearchersStorage())->all(); |
|||
} |
|||
|
|||
if (request()->wantsJson()) { |
|||
return response()->json([ |
|||
'searchers' => $searchers, |
|||
], 200); |
|||
} |
|||
|
|||
return view('pages.searchers.index', [ |
|||
'searchers' => $searchers, |
|||
]); |
|||
} |
|||
|
|||
public function create() |
|||
{ |
|||
return view('pages.searchers.create'); |
|||
} |
|||
|
|||
public function store() |
|||
{ |
|||
request()->validate([ |
|||
'name' => 'required', |
|||
'rows' => 'required|array', |
|||
'rows.*' => 'array', |
|||
]); |
|||
|
|||
try { |
|||
$factory = new SearcherFactory(request()->get('name'), request()->get('rows')); |
|||
|
|||
$factory->create(); |
|||
|
|||
return response()->json([ |
|||
'status' => 'success', |
|||
], 200); |
|||
} catch (\Exception $exception) { |
|||
return response()->json([ |
|||
'status' => 'fail', |
|||
'message' => $exception->getMessage(), |
|||
], 400); |
|||
} |
|||
} |
|||
|
|||
public function show($id) |
|||
{ |
|||
$searchersStorage = new SearchersStorage(); |
|||
|
|||
try { |
|||
if ( ! $searchersStorage->has($id)) { |
|||
abort(404); |
|||
} |
|||
|
|||
return view('pages.searchers.show', [ |
|||
'searcher' => $searchersStorage->get($id), |
|||
]); |
|||
} catch (\Exception $exception) { |
|||
abort(400); |
|||
} |
|||
} |
|||
|
|||
public function update($id) |
|||
{ |
|||
|
|||
} |
|||
} |
@ -0,0 +1,28 @@ |
|||
<?php |
|||
|
|||
namespace App\SearchDisplace\Regex; |
|||
|
|||
use App\SearchDisplace\Searchers\SearcherCreator; |
|||
|
|||
class RegexFactory extends SearcherCreator |
|||
{ |
|||
protected $expression; |
|||
|
|||
public function __construct($name, $expression) |
|||
{ |
|||
parent::__construct($name, ''); |
|||
|
|||
$this->expression = $expression; |
|||
} |
|||
|
|||
public function create() |
|||
{ |
|||
$this->rows = [ |
|||
[ |
|||
[ 'expression' => $this->expression, ] |
|||
], |
|||
]; |
|||
|
|||
$this->store(); |
|||
} |
|||
} |
@ -0,0 +1,36 @@ |
|||
<?php |
|||
|
|||
namespace App\SearchDisplace\Searchers; |
|||
|
|||
use Illuminate\Support\Facades\Storage; |
|||
|
|||
abstract class SearcherCreator |
|||
{ |
|||
protected $name; |
|||
protected $id; |
|||
protected $description; |
|||
protected $rows; |
|||
protected $storage; |
|||
|
|||
public function __construct($name, $description) |
|||
{ |
|||
$this->name = $name; |
|||
$this->description = $description; |
|||
$this->id = md5(uniqid(rand(), true)); |
|||
$this->storage = Storage::disk('local'); |
|||
} |
|||
|
|||
abstract public function create(); |
|||
|
|||
protected function store() |
|||
{ |
|||
$contents = [ |
|||
'id' => $this->id, |
|||
'name' => $this->name, |
|||
'description' => $this->description, |
|||
'rows' => $this->rows, |
|||
]; |
|||
|
|||
$this->storage->put("searchers/{$this->id}_{$this->name}.json", json_encode($contents)); |
|||
} |
|||
} |
@ -0,0 +1,18 @@ |
|||
<?php |
|||
|
|||
namespace App\SearchDisplace\Searchers; |
|||
|
|||
class SearcherFactory extends SearcherCreator |
|||
{ |
|||
public function __construct($name, $rows) |
|||
{ |
|||
parent::__construct($name, ''); |
|||
|
|||
$this->rows = $rows; |
|||
} |
|||
|
|||
public function create() |
|||
{ |
|||
$this->store(); |
|||
} |
|||
} |
@ -0,0 +1,50 @@ |
|||
<?php |
|||
|
|||
namespace App\SearchDisplace\Searchers; |
|||
|
|||
use Illuminate\Support\Facades\Storage; |
|||
|
|||
class SearchersStorage |
|||
{ |
|||
protected $storage; |
|||
protected $searchers = []; |
|||
|
|||
public function __construct() |
|||
{ |
|||
$this->storage = Storage::disk('local'); |
|||
} |
|||
|
|||
public function all() |
|||
{ |
|||
$searchers = []; |
|||
$files = $this->storage->files('searchers'); |
|||
|
|||
foreach ($files as $file) { |
|||
if (pathinfo($file, PATHINFO_EXTENSION) === 'json') { |
|||
$name = pathinfo($file, PATHINFO_FILENAME); |
|||
$result = explode('_', $name); |
|||
|
|||
if (count($result) === 2) { |
|||
$searchers[$name] = [ |
|||
'id' => $result[0], |
|||
'name' => $result[1], |
|||
]; |
|||
} |
|||
} |
|||
} |
|||
|
|||
return $searchers; |
|||
} |
|||
|
|||
public function has($id) |
|||
{ |
|||
return $this->storage->exists("searchers/$id.json"); |
|||
} |
|||
|
|||
public function get($id) |
|||
{ |
|||
$contents = $this->storage->get("searchers/$id.json"); |
|||
|
|||
return json_decode($contents, true); |
|||
} |
|||
} |
51585
package-lock.json
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
890
public/css/app.css
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
17342
public/js/app.js
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,69 @@ |
|||
<template> |
|||
<div id="regex-create"> |
|||
<div> |
|||
<div> |
|||
<input v-model="name" |
|||
type="text" |
|||
placeholder="Enter searcher name" |
|||
class="input"> |
|||
</div> |
|||
|
|||
<button @click="onSave" :disabled=" ! name || ! pattern"> |
|||
Save |
|||
</button> |
|||
</div> |
|||
|
|||
<div class="regex-box"> |
|||
<div class="main"> |
|||
<pattern-box v-model="pattern"></pattern-box> |
|||
|
|||
<text-box :pattern="pattern" |
|||
:flags="flags"> |
|||
</text-box> |
|||
|
|||
<flags v-model="flags"></flags> |
|||
</div> |
|||
|
|||
<aside> |
|||
<side-bar></side-bar> |
|||
</aside> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
|
|||
<script lang="ts"> |
|||
import {Component, Vue} from "vue-property-decorator"; |
|||
import TextBox from './TextBox.vue'; |
|||
import PatternBox from './PatternBox.vue'; |
|||
import Flags from './Flags.vue'; |
|||
import SideBar from './SideBar.vue'; |
|||
|
|||
@Component({ |
|||
components: { |
|||
TextBox, |
|||
PatternBox, |
|||
Flags, |
|||
SideBar, |
|||
}, |
|||
}) |
|||
|
|||
export default class Create extends Vue { |
|||
private name: string = ''; |
|||
private pattern: string = ''; |
|||
private flags: Array<string> = ['g', 'i']; |
|||
|
|||
async onSave() { |
|||
try { |
|||
const { data } = await (window as any).axios.post('/regex', { |
|||
name: this.name, |
|||
expression: this.pattern, |
|||
}); |
|||
|
|||
alert('Saved.'); |
|||
} catch (e) { |
|||
console.log(e); |
|||
console.log('Something went wrong.'); |
|||
} |
|||
} |
|||
}; |
|||
</script> |
@ -0,0 +1,46 @@ |
|||
<template> |
|||
<div id="flags" class="flags"> |
|||
<div class="input-group"> |
|||
<label for="global_match"> |
|||
<input type="checkbox" |
|||
value="g" |
|||
v-model="flags" |
|||
id="global_match"> global match |
|||
</label> |
|||
</div> |
|||
|
|||
<div class="input-group"> |
|||
<label for="ignore_case"> |
|||
<input type="checkbox" |
|||
value="i" |
|||
v-model="flags" |
|||
id="ignore_case"> case insensitive |
|||
</label> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
|
|||
<script lang="ts"> |
|||
import {Component, Prop, Watch, Vue} from "vue-property-decorator"; |
|||
|
|||
@Component |
|||
export default class Flags extends Vue { |
|||
private flags: Array<string> = ['g']; |
|||
|
|||
@Prop() public readonly value!: Array<string>; |
|||
|
|||
@Watch('flags') |
|||
flagsChanged(update: Array<string>) { |
|||
this.$emit('input', update); |
|||
} |
|||
|
|||
@Watch('value') |
|||
valueChanged(update: Array<string>) { |
|||
this.flags = update; |
|||
} |
|||
|
|||
created() { |
|||
this.flags = this.value; |
|||
} |
|||
}; |
|||
</script> |
@ -0,0 +1,45 @@ |
|||
<template> |
|||
<div id="pattern-box" class="pattern-box-wrapper"> |
|||
<input autofocus |
|||
v-model="pattern" |
|||
placeholder="Enter pattern" |
|||
class="pattern-box" |
|||
type="text"> |
|||
|
|||
<p v-show="error" class="error-text">{{ error }}</p> |
|||
</div> |
|||
</template> |
|||
|
|||
<script lang="ts"> |
|||
import {Component, Prop, Watch, Vue} from "vue-property-decorator"; |
|||
|
|||
@Component |
|||
export default class PatternBox extends Vue { |
|||
private pattern: string = ''; |
|||
private error: string = ''; |
|||
|
|||
@Prop({default: ''}) public readonly value: string = ''; |
|||
|
|||
@Watch('pattern') |
|||
patternChanged(value: string) { |
|||
try { |
|||
new RegExp(value); |
|||
|
|||
this.$emit('input', value); |
|||
|
|||
this.error = ''; |
|||
} catch (error) { |
|||
this.error = 'Expression is invalid'; |
|||
} |
|||
} |
|||
|
|||
@Watch('value') |
|||
valueChanged(value: string) { |
|||
this.pattern = value |
|||
} |
|||
|
|||
created() { |
|||
this.pattern = this.value |
|||
} |
|||
}; |
|||
</script> |
@ -0,0 +1,225 @@ |
|||
<template> |
|||
<div id="references" class="references"> |
|||
<div class="reference"> |
|||
<h3 class="title mb">Metacharacters</h3> |
|||
<h4 class="caption mb">Metacharacters express sets of characters or special characters.</h4> |
|||
<ul class="characters"> |
|||
<li class="character mb"> |
|||
<p class="character__code"> |
|||
<span class="purple">.</span> |
|||
</p> |
|||
<p class="character__info"> |
|||
<span>any character</span> |
|||
</p> |
|||
</li> |
|||
<li class="character"> |
|||
<p class="character__code"> |
|||
<span>^</span> |
|||
</p> |
|||
<p class="character__info"> |
|||
<span>beginning of a line</span> |
|||
</p> |
|||
</li> |
|||
<li class="character"> |
|||
<p class="character__code"> |
|||
<span>$</span> |
|||
</p> |
|||
<p class="character__info"> |
|||
<span>end of a line</span> |
|||
</p> |
|||
</li> |
|||
<li class="character"> |
|||
<p class="character__code"> |
|||
<span>\</span> |
|||
</p> |
|||
<p class="character__info"> |
|||
<span>quotes special characters <br>( |
|||
<span class="purple">* ? + [ ] ( ) { } ^ $ | \ . /</span> ) |
|||
</span> |
|||
</p> |
|||
</li> |
|||
<li class="character"> |
|||
<p class="character__code"> |
|||
<span class="blue">\w</span> |
|||
</p> |
|||
<p class="character__info"> |
|||
<span>word</span> |
|||
</p> |
|||
</li> |
|||
<li class="character"> |
|||
<p class="character__code"> |
|||
<span class="blue">\t</span> |
|||
</p> |
|||
<p class="character__info"> |
|||
<span>horizontal tabulation</span> |
|||
</p> |
|||
</li> |
|||
<li class="character"> |
|||
<p class="character__code"> |
|||
<span class="blue">\n</span> |
|||
</p> |
|||
<p class="character__info"> |
|||
<span>new line</span> |
|||
</p> |
|||
</li> |
|||
<li class="character"> |
|||
<p class="character__code"> |
|||
<span class="blue">\d</span> |
|||
</p> |
|||
<p class="character__info"> |
|||
<span>any digit</span> |
|||
</p> |
|||
</li> |
|||
<li class="character"> |
|||
<p class="character__code"> |
|||
<span class="blue">\t</span> |
|||
</p> |
|||
<p class="character__info"> |
|||
<span>white space character ( |
|||
<span class="blue">\t \n \f \r \{Z}</span>)</span> |
|||
</p> |
|||
</li> |
|||
<li class="character"> |
|||
<p class="character__code"> |
|||
<span class="blue">[...]</span> |
|||
</p> |
|||
<p class="character__info"> |
|||
<span>match any character (or range of characters) inside the bracket. Range may be e.g. |
|||
<span class="blue">[a-z], [A-Z], [3...5]</span>, etc.</span> |
|||
</p> |
|||
</li> |
|||
<li class="character"> |
|||
<p class="character__code"> |
|||
<span class="blue">\D</span> |
|||
</p> |
|||
<p class="character__info"> |
|||
<span>any character that is not a decimal digit</span> |
|||
</p> |
|||
</li> |
|||
<li class="character"> |
|||
<p class="character__code"> |
|||
<span class="blue">\S</span> |
|||
</p> |
|||
<p class="character__info"> |
|||
<span>non-whitespace character</span> |
|||
</p> |
|||
</li> |
|||
<li class="character"> |
|||
<p class="character__code"> |
|||
<span class="blue">\W</span> |
|||
</p> |
|||
<p class="character__info"> |
|||
<span>non-word character</span> |
|||
</p> |
|||
</li> |
|||
</ul> |
|||
</div> |
|||
<div class="reference"> |
|||
<h3 class="title mb">Operators</h3> |
|||
<h4 class="caption mb">Operators allow to desribe how an expression (or subexpression) should be matched.</h4> |
|||
<ul class="characters"> |
|||
<li class="character mb"> |
|||
<p class="character__code"> |
|||
<span>( |
|||
<span class="white">…</span>)</span> |
|||
</p> |
|||
<p class="character__info"> |
|||
<span>groups expression into subexpressions</span> |
|||
</p> |
|||
</li> |
|||
<li class="character mb"> |
|||
<p class="character__code"> |
|||
<span class="white">A |
|||
<span class="purple">|</span> B</span> |
|||
</p> |
|||
<p class="character__info"> |
|||
<span>groups expression into subexpressions</span> |
|||
</p> |
|||
</li> |
|||
<li class="character mb"> |
|||
<p class="character__code"> |
|||
<span class="purple">^</span> |
|||
</p> |
|||
<p class="character__info"> |
|||
<span>negation</span> |
|||
</p> |
|||
</li> |
|||
<li class="character mb"> |
|||
<p class="character__code"> |
|||
<span class="purple">*</span> |
|||
</p> |
|||
<p class="character__info"> |
|||
<span>match 0 or more times</span> |
|||
</p> |
|||
</li> |
|||
<li class="character mb"> |
|||
<p class="character__code"> |
|||
<span class="purple">+</span> |
|||
</p> |
|||
<p class="character__info"> |
|||
<span>match 1 or more times</span> |
|||
</p> |
|||
</li> |
|||
<li class="character mb"> |
|||
<p class="character__code"> |
|||
<span class="purple">?</span> |
|||
</p> |
|||
<p class="character__info"> |
|||
<span>match 0 or 1 time</span> |
|||
</p> |
|||
</li> |
|||
<li class="character mb"> |
|||
<p class="character__code"> |
|||
<span class="purple">{ |
|||
<span class="white">n</span> }</span> |
|||
</p> |
|||
<p class="character__info"> |
|||
<span>match exactly n times</span> |
|||
</p> |
|||
</li> |
|||
<li class="character mb"> |
|||
<p class="character__code"> |
|||
<span class="purple">{ |
|||
<span class="white">n |
|||
<span class="purple">,</span> m</span> }</span> |
|||
</p> |
|||
<p class="character__info"> |
|||
<span>match exactly n times</span> |
|||
</p> |
|||
</li> |
|||
</ul> |
|||
</div> |
|||
<div class="reference"> |
|||
<h3 class="title mb">Non-capturing operators</h3> |
|||
<h4 class="caption mb">These operators work by one simple rule. They are not captured in groups.</h4> |
|||
<ul class="characters"> |
|||
<li class="character mb"> |
|||
<p class="character__code"> |
|||
<span class="disabled">( ? # ... )</span> |
|||
</p> |
|||
<p class="character__info"> |
|||
<span>comment</span> |
|||
</p> |
|||
</li> |
|||
<li class="character mb"> |
|||
<p class="character__code"> |
|||
<span class="purple">( ? : |
|||
<span class="white">…</span> )</span> |
|||
</p> |
|||
<p class="character__info"> |
|||
<span>Subexpression must occur but it's not captured in group.</span> |
|||
</p> |
|||
</li> |
|||
<li class="character mb"> |
|||
<p class="character__code"> |
|||
<span class="purple">( ? ! |
|||
<span class="white">…</span> )</span> |
|||
</p> |
|||
<p class="character__info"> |
|||
<span>Makes sure that the subexpression does not occur at current position. It's useful to exclude part of expression.</span> |
|||
</p> |
|||
</li> |
|||
</ul> |
|||
</div> |
|||
</div> |
|||
</template> |
@ -0,0 +1,39 @@ |
|||
<template> |
|||
<div id="text-box" class="text-box"> |
|||
<div ref="backdrop" class="backdrop"> |
|||
<div class="matches" v-html="matches"></div> |
|||
</div> |
|||
|
|||
<textarea ref="text" |
|||
v-model="text" |
|||
placeholder="Enter text to check matches" |
|||
@scroll="handleScroll"> |
|||
</textarea> |
|||
</div> |
|||
</template> |
|||
|
|||
<script lang="ts"> |
|||
import {Component, Prop, Vue} from "vue-property-decorator"; |
|||
|
|||
@Component |
|||
export default class TextBox extends Vue { |
|||
private text: string = ''; |
|||
|
|||
@Prop({default: ''}) public readonly pattern: string = ''; |
|||
@Prop() public readonly flags!: Array<string>; |
|||
|
|||
getMatches() { |
|||
return this.text |
|||
.replace(/\n$/g, '\n\n') |
|||
.replace(new RegExp(this.pattern, this.flags.join('')), '<mark>$&</mark>'); |
|||
} |
|||
|
|||
handleScroll() { |
|||
// this.$refs.backdrop.scrollTop = this.$refs.text.scrollTop; |
|||
} |
|||
|
|||
get matches(): string { |
|||
return this.getMatches(); |
|||
} |
|||
}; |
|||
</script> |
@ -0,0 +1,62 @@ |
|||
<template> |
|||
<div> |
|||
<div @click="adding = true" |
|||
class="box" |
|||
style="font-size: 6rem;"> |
|||
+ |
|||
</div> |
|||
|
|||
<Dialog header="Select searcher" |
|||
position="right" |
|||
:visible="adding" |
|||
:closable="false" |
|||
:style="{width: '50vw'}" |
|||
:modal="true"> |
|||
<searchers @selected="onSearcherSelected"></searchers> |
|||
|
|||
<template #footer> |
|||
<Button label="Close" |
|||
icon="pi pi-times" |
|||
@click="adding = false" |
|||
class="p-button-text"/> |
|||
|
|||
<Button label="Confirm" |
|||
icon="pi pi-check" |
|||
@click="onConfirm" |
|||
:disabled="Object.keys(selectedSearcher).length === 0" |
|||
autofocus /> |
|||
</template> |
|||
</Dialog> |
|||
</div> |
|||
</template> |
|||
|
|||
<script lang="ts"> |
|||
import {Component, Vue} from "vue-property-decorator"; |
|||
import Dialog from 'primevue/dialog'; |
|||
import Searchers from './Index.vue'; |
|||
|
|||
@Component({ |
|||
name: 'AddBox', |
|||
|
|||
components: { |
|||
Dialog, |
|||
Searchers, |
|||
}, |
|||
}) |
|||
export default class Create extends Vue { |
|||
private adding: boolean = false; |
|||
private selectedSearcher: Object = {}; |
|||
|
|||
onSearcherSelected(searcher: Object) { |
|||
this.selectedSearcher = searcher; |
|||
} |
|||
|
|||
onConfirm() { |
|||
this.$emit('added', this.selectedSearcher); |
|||
|
|||
this.selectedSearcher = {}; |
|||
|
|||
this.adding = false; |
|||
} |
|||
}; |
|||
</script> |
@ -0,0 +1,65 @@ |
|||
<template> |
|||
<div id="searchers-create"> |
|||
<div> |
|||
<input v-model="name" |
|||
type="text" |
|||
placeholder="Enter searcher name" |
|||
class="input"> |
|||
|
|||
<button @click="onSave" :disabled=" ! name || rows.length === 0"> |
|||
Save |
|||
</button> |
|||
</div> |
|||
|
|||
<div v-for="(row, rowIndex) in rows" :key="`row-${rowIndex}`" class="flex-row"> |
|||
<div v-for="(searcher, columnIndex) in row" :key="`column-${columnIndex}`"> |
|||
<div class="box"> |
|||
{{ searcher.name }} |
|||
</div> |
|||
</div> |
|||
|
|||
<add-box @added="(searcher) => { onSearcherAdded(searcher, rowIndex); }"> |
|||
</add-box> |
|||
</div> |
|||
|
|||
<add-box @added="onNewRowSearcherAdded"> |
|||
</add-box> |
|||
</div> |
|||
</template> |
|||
|
|||
<script lang="ts"> |
|||
import {Component, Vue} from "vue-property-decorator"; |
|||
import AddBox from './AddBox.vue'; |
|||
|
|||
@Component({ |
|||
components: { |
|||
AddBox, |
|||
}, |
|||
}) |
|||
export default class Create extends Vue { |
|||
private name: String = ''; |
|||
private rows: Array<Array<Object>> = []; |
|||
|
|||
onNewRowSearcherAdded(searcher: Object) { |
|||
const length = this.rows.push([]); |
|||
|
|||
this.onSearcherAdded(searcher, length - 1); |
|||
} |
|||
|
|||
onSearcherAdded(searcher: Object, rowIndex: number) { |
|||
this.rows[rowIndex].push(searcher); |
|||
} |
|||
|
|||
async onSave() { |
|||
try { |
|||
const { data } = await (window as any).axios.post('/searchers', { |
|||
name: this.name, |
|||
rows: this.rows, |
|||
}); |
|||
} catch (e) { |
|||
console.log(e); |
|||
console.log('Something went wrong.'); |
|||
} |
|||
} |
|||
}; |
|||
</script> |
@ -0,0 +1,65 @@ |
|||
<template> |
|||
<div style="display: flex; flex-direction: row;"> |
|||
<div v-for="(searcher, id) in searchers" |
|||
:key="id" |
|||
@click="onSelect(id)" |
|||
class="box flex-column" |
|||
:class="{selected: id === selectedSearcherId}" |
|||
style="margin-left: 1rem;"> |
|||
<div> |
|||
<span> {{ searcher.name }} </span> |
|||
</div> |
|||
|
|||
<div style="margin-top: 5px; color: dodgerblue;"> |
|||
<a @click.stop="onOpen(id)"> View </a> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
|
|||
<script lang="ts"> |
|||
import {Component, Vue} from "vue-property-decorator"; |
|||
|
|||
@Component({ |
|||
}) |
|||
export default class Create extends Vue { |
|||
private searchers: any = {}; |
|||
private selectedSearcherId: string = ''; |
|||
|
|||
async boot() { |
|||
try { |
|||
const { data } = await (window as any).axios.get('/searchers'); |
|||
|
|||
this.searchers = data.searchers; |
|||
} catch (e) { |
|||
|
|||
} |
|||
} |
|||
|
|||
onSelect(id: string) { |
|||
if (this.selectedSearcherId === id) { |
|||
this.selectedSearcherId = ''; |
|||
|
|||
this.$emit('selected', {}); |
|||
|
|||
return; |
|||
} |
|||
|
|||
this.selectedSearcherId = id; |
|||
|
|||
this.$emit('selected', this.searchers[id]); |
|||
} |
|||
|
|||
onOpen(id: string) { |
|||
window.open(this.getURL(id), '_blank'); |
|||
} |
|||
|
|||
getURL(id: string) { |
|||
return `/searchers/${id}`; |
|||
} |
|||
|
|||
created() { |
|||
this.boot(); |
|||
} |
|||
}; |
|||
</script> |
@ -0,0 +1,50 @@ |
|||
<template> |
|||
<div id="searchers-show"> |
|||
<div> |
|||
<h3>{{ searcher.name }}</h3> |
|||
|
|||
<h5>{{ searcher.description }}</h5> |
|||
|
|||
<a v-if="editable" |
|||
:href="`/searchers/${searcher.id}/edit`"> |
|||
Edit |
|||
</a> |
|||
</div> |
|||
|
|||
<div v-for="(row, rowIndex) in searcher.rows" :key="`row-${rowIndex}`" class="flex-row"> |
|||
<div v-for="(searcherItem, columnIndex) in row" :key="`column-${columnIndex}`"> |
|||
<div class="box is-plain"> |
|||
<template v-if="searcherItem.hasOwnProperty('name')"> |
|||
<searcher-show :editable="false" |
|||
:searcher="searcherItem"> |
|||
</searcher-show> |
|||
</template> |
|||
|
|||
<template v-else> |
|||
<b>{{ searcherItem.expression }}</b> |
|||
|
|||
<p> |
|||
<!-- // Show example here, so for example the user has to input the example in order to save--> |
|||
<!-- // the regex, show highlight, so apply regex on example text--> |
|||
</p> |
|||
</template> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
|
|||
<script lang="ts"> |
|||
import {Component, Vue, Prop} from "vue-property-decorator"; |
|||
|
|||
@Component({ |
|||
name: 'SearcherShow', |
|||
}) |
|||
export default class Create extends Vue { |
|||
@Prop({default: {}}) |
|||
public readonly searcher!: Object; |
|||
|
|||
@Prop({default: true}) |
|||
public readonly editable!: boolean; |
|||
}; |
|||
</script> |
@ -0,0 +1,54 @@ |
|||
@import "variables" |
|||
|
|||
.page-wrapper |
|||
flex: 1 |
|||
|
|||
.header |
|||
// background: #ffffff |
|||
background: $header-background |
|||
height: $header-height |
|||
padding: 0 2rem |
|||
border-bottom: 1px solid #dee2e6 |
|||
display: -ms-flexbox |
|||
display: flex |
|||
-ms-flex-align: center |
|||
align-items: center |
|||
-ms-flex-pack: justify |
|||
justify-content: space-between |
|||
color: #495057 |
|||
|
|||
.left |
|||
display: -ms-flexbox |
|||
display: flex |
|||
-ms-flex-align: center |
|||
align-items: center |
|||
|
|||
.footer |
|||
// background: #ffffff |
|||
background: $footer-background |
|||
height: $footer-height |
|||
border-top: 1px solid #dee2e6 |
|||
border-bottom: 1px solid #dee2e6 |
|||
padding: 0 2rem |
|||
display: -ms-flexbox |
|||
display: flex |
|||
-ms-flex-align: center |
|||
align-items: center |
|||
-ms-flex-pack: justify |
|||
justify-content: space-between |
|||
|
|||
flex-shrink: 0 |
|||
|
|||
.left |
|||
display: -ms-flexbox |
|||
display: flex |
|||
-ms-flex-align: center |
|||
align-items: center |
|||
|
|||
|
|||
.right |
|||
font-size: 0.875rem |
|||
color: #6c757d |
|||
|
|||
.content |
|||
padding: $content-padding |
@ -1,60 +0,0 @@ |
|||
@import "variables"; |
|||
|
|||
.page-wrapper { |
|||
flex: 1; |
|||
} |
|||
|
|||
.header { |
|||
// background: #ffffff; |
|||
background: $header-background; |
|||
height: $header-height; |
|||
padding: 0 2rem; |
|||
border-bottom: 1px solid #dee2e6; |
|||
display: -ms-flexbox; |
|||
display: flex; |
|||
-ms-flex-align: center; |
|||
align-items: center; |
|||
-ms-flex-pack: justify; |
|||
justify-content: space-between; |
|||
color: #495057; |
|||
|
|||
.left { |
|||
display: -ms-flexbox; |
|||
display: flex; |
|||
-ms-flex-align: center; |
|||
align-items: center; |
|||
} |
|||
} |
|||
|
|||
.footer { |
|||
// background: #ffffff; |
|||
background: $footer-background; |
|||
height: $footer-height; |
|||
border-top: 1px solid #dee2e6; |
|||
border-bottom: 1px solid #dee2e6; |
|||
padding: 0 2rem; |
|||
display: -ms-flexbox; |
|||
display: flex; |
|||
-ms-flex-align: center; |
|||
align-items: center; |
|||
-ms-flex-pack: justify; |
|||
justify-content: space-between; |
|||
|
|||
flex-shrink: 0; |
|||
|
|||
.left { |
|||
display: -ms-flexbox; |
|||
display: flex; |
|||
-ms-flex-align: center; |
|||
align-items: center; |
|||
} |
|||
|
|||
.right { |
|||
font-size: 0.875rem; |
|||
color: #6c757d; |
|||
} |
|||
} |
|||
|
|||
.content { |
|||
padding: $content-padding; |
|||
} |
@ -0,0 +1,7 @@ |
|||
$header-height: 4rem |
|||
$header-background: var(--blue-400) |
|||
|
|||
$footer-height: 4rem |
|||
$footer-background: var(--blue-400) |
|||
|
|||
$content-padding: 25px |
@ -1,7 +0,0 @@ |
|||
$header-height: 4rem; |
|||
$header-background: var(--blue-400); |
|||
|
|||
$footer-height: 4rem; |
|||
$footer-background: var(--blue-400); |
|||
|
|||
$content-padding: 25px; |
@ -0,0 +1,79 @@ |
|||
@import '~primeflex/src/_variables' |
|||
@import '~primeflex/src/_grid' |
|||
@import '~primeflex/src/_formlayout' |
|||
@import '~primeflex/src/_display' |
|||
@import '~primeflex/src/_text' |
|||
@import '~primeflex/src/flexbox/_flexbox' |
|||
@import '~primeflex/src/_spacing' |
|||
@import '~primeflex/src/_elevation' |
|||
|
|||
// @import '~primevue/resources/themes/fluent-light/theme.css' |
|||
// @import '~primevue/resources/themes/vela-green/theme.css' |
|||
@import '~primevue/resources/themes/mdc-light-indigo/theme.css' |
|||
@import '~primevue/resources/primevue.min.css' |
|||
@import '~primeicons/primeicons.css' |
|||
|
|||
@import 'layout' |
|||
|
|||
@import "components/index" |
|||
|
|||
body |
|||
height: 100% |
|||
margin: 0 |
|||
background: #f8f9fa |
|||
|
|||
#app |
|||
font-family: Avenir, Helvetica, Arial, sans-serif |
|||
-webkit-font-smoothing: antialiased |
|||
-moz-osx-font-smoothing: grayscale |
|||
text-align: center |
|||
color: #2c3e50 |
|||
display: flex |
|||
flex-direction: column |
|||
min-height: 100vh |
|||
|
|||
#nav |
|||
padding: 30px |
|||
|
|||
a |
|||
font-weight: bold |
|||
color: #2c3e50 |
|||
|
|||
&.router-link-exact-active |
|||
color: #42b983 |
|||
|
|||
// Temp location |
|||
.box |
|||
border: 1px solid black |
|||
min-width: 100px |
|||
min-height: 100px |
|||
padding: 10px |
|||
display: flex |
|||
justify-content: center |
|||
align-items: center |
|||
cursor: pointer |
|||
margin-left: 1rem |
|||
margin-bottom: 1rem |
|||
|
|||
&.selected |
|||
border-color: dodgerblue |
|||
|
|||
&.is-plain |
|||
cursor: default |
|||
|
|||
&.auto |
|||
width: auto |
|||
min-width: 100px |
|||
|
|||
.flex-row |
|||
display: flex |
|||
flex-direction: row |
|||
|
|||
.flex-column |
|||
display: flex |
|||
flex-direction: column |
|||
|
|||
.flex-center |
|||
display: flex |
|||
align-items: center |
|||
justify-content: center |
@ -1,48 +0,0 @@ |
|||
|
|||
@import '~primeflex/src/_variables'; |
|||
@import '~primeflex/src/_grid'; |
|||
@import '~primeflex/src/_formlayout'; |
|||
@import '~primeflex/src/_display'; |
|||
@import '~primeflex/src/_text'; |
|||
@import '~primeflex/src/flexbox/_flexbox'; |
|||
@import '~primeflex/src/_spacing'; |
|||
@import '~primeflex/src/_elevation'; |
|||
|
|||
// @import '~primevue/resources/themes/fluent-light/theme.css'; |
|||
// @import '~primevue/resources/themes/vela-green/theme.css'; |
|||
@import '~primevue/resources/themes/mdc-light-indigo/theme.css'; |
|||
@import '~primevue/resources/primevue.min.css'; |
|||
@import '~primeicons/primeicons.css'; |
|||
|
|||
@import 'layout'; |
|||
|
|||
|
|||
body { |
|||
height: 100%; |
|||
margin: 0; |
|||
background: #f8f9fa; |
|||
} |
|||
|
|||
#app { |
|||
font-family: Avenir, Helvetica, Arial, sans-serif; |
|||
-webkit-font-smoothing: antialiased; |
|||
-moz-osx-font-smoothing: grayscale; |
|||
text-align: center; |
|||
color: #2c3e50; |
|||
display: flex; |
|||
flex-direction: column; |
|||
min-height: 100vh; |
|||
} |
|||
|
|||
#nav { |
|||
padding: 30px; |
|||
|
|||
a { |
|||
font-weight: bold; |
|||
color: #2c3e50; |
|||
|
|||
&.router-link-exact-active { |
|||
color: #42b983; |
|||
} |
|||
} |
|||
} |
@ -0,0 +1 @@ |
|||
@import "regex/index" |
@ -0,0 +1 @@ |
|||
@import 'create/index' |
@ -0,0 +1,9 @@ |
|||
#flags.flags |
|||
display: flex |
|||
border-top: 1px solid #f7f7f72d |
|||
padding: 0.8rem |
|||
color: #d6d7cc |
|||
|
|||
.input-group |
|||
margin-right: 1.2rem |
|||
font-size: 12px |
@ -0,0 +1,43 @@ |
|||
@import 'pattern-box' |
|||
@import 'flags' |
|||
@import 'text-box' |
|||
@import 'references' |
|||
|
|||
#regex-create |
|||
.regex-box |
|||
background-color: #001221 |
|||
display: flex |
|||
width: 850px |
|||
height: 500px |
|||
margin: 5rem auto |
|||
position: relative |
|||
border-radius: 5px |
|||
transition: all 0.25s linear |
|||
|
|||
.regex-box:hover |
|||
box-shadow: 0.4rem 1.4rem 1.4rem rgba(0, 0, 0, 0.2) |
|||
transform: translateY(-10px) |
|||
|
|||
.main |
|||
display: flex |
|||
flex-direction: column |
|||
width: 550px |
|||
|
|||
aside |
|||
width: 300px |
|||
padding: 1.5rem |
|||
border-left: 1px solid #ffffff31 |
|||
overflow-y: scroll |
|||
|
|||
aside::-webkit-scrollbar |
|||
background: #3f4545 |
|||
width: 10px !important |
|||
|
|||
aside::-webkit-scrollbar-track |
|||
border-radius: 10px !important |
|||
|
|||
aside::-webkit-scrollbar-thumb |
|||
border-radius: 10px !important |
|||
-webkit-box-shadow: inset 0 0 6px rgba(54, 52, 52, 0.925) !important |
|||
box-shadow: inset 0 0 6px rgba(54, 52, 52, 0.863) !important |
|||
|
@ -0,0 +1,26 @@ |
|||
#pattern-box |
|||
.pattern-box |
|||
width: 100% |
|||
font: inherit |
|||
min-height: 46px |
|||
background: transparent |
|||
color: #d6d7cc |
|||
padding: 2.3rem 25px |
|||
border: 0 |
|||
outline: none |
|||
|
|||
.error-text |
|||
position: absolute |
|||
width: 100% |
|||
bottom: 0 |
|||
color: #d6d7cc |
|||
font-size: 12px |
|||
text-align: center |
|||
letter-spacing: 1px |
|||
|
|||
input::placeholder |
|||
color: grey |
|||
|
|||
#pattern-box.pattern-box-wrapper |
|||
position: relative |
|||
border-bottom: 1px solid #f7f7f72d !important |
@ -0,0 +1,63 @@ |
|||
#references.references |
|||
.reference:not(:first-child) |
|||
margin-top: 1.6rem |
|||
|
|||
.reference * |
|||
color: #fff |
|||
font-size: 12px |
|||
font-weight: inherit |
|||
margin: 0 |
|||
padding: 0 |
|||
|
|||
.mb |
|||
margin-bottom: 0.5rem |
|||
|
|||
.title |
|||
font-size: 17px |
|||
|
|||
ul, |
|||
li |
|||
list-style: none |
|||
|
|||
.character |
|||
display: flex |
|||
|
|||
.character:not(:last-child) |
|||
margin-bottom: 15px |
|||
|
|||
.character__code |
|||
background: #00080e |
|||
border: 1px solid #f7f7f72d |
|||
padding: 0.3rem 0.8rem |
|||
display: flex |
|||
justify-content: center |
|||
align-items: center |
|||
margin-right: 0.5rem |
|||
|
|||
.character__code span |
|||
white-space: nowrap |
|||
font-weight: 600 |
|||
color: #c77ce0 |
|||
|
|||
.character__info |
|||
display: flex |
|||
align-items: center |
|||
padding-bottom: 5px |
|||
|
|||
.disabled, |
|||
.purple, |
|||
.blue, |
|||
.white |
|||
font-weight: 600 |
|||
|
|||
.disabled |
|||
color: #888888 !important |
|||
|
|||
.purple |
|||
color: #c77ce0 !important |
|||
|
|||
.blue |
|||
color: #3c7fc2 !important |
|||
|
|||
.white |
|||
color: white !important |
@ -0,0 +1,63 @@ |
|||
#text-box.text-box |
|||
flex-grow: 1 |
|||
width: 100% |
|||
overflow: hidden |
|||
position: relative |
|||
background-color: #011627 |
|||
|
|||
.text-box * |
|||
box-sizing: border-box |
|||
|
|||
.backdrop, |
|||
textarea |
|||
position: absolute |
|||
padding: 1.5rem 25px 8px |
|||
width: 100% |
|||
height: 100% |
|||
font-size: inherit |
|||
background-color: transparent |
|||
outline: none |
|||
border: 0 |
|||
overflow: auto |
|||
letter-spacing: 1px |
|||
|
|||
.backdrop |
|||
z-index: 1 |
|||
pointer-events: none |
|||
|
|||
.matches, |
|||
textarea |
|||
font: inherit |
|||
|
|||
textarea |
|||
display: block |
|||
position: absolute |
|||
z-index: 2 |
|||
resize: none |
|||
color: #fff |
|||
|
|||
textarea::-webkit-scrollbar |
|||
background: #282a36 |
|||
width: 17px !important |
|||
|
|||
textarea::-webkit-scrollbar-track |
|||
border-radius: 10px !important |
|||
|
|||
textarea::-webkit-scrollbar-thumb |
|||
border-radius: 10px !important |
|||
|
|||
textarea::placeholder |
|||
color: grey |
|||
|
|||
.matches |
|||
white-space: pre-wrap |
|||
word-wrap: break-word |
|||
color: transparent |
|||
|
|||
/* dynamically generated content style with deep selector */ |
|||
.matches mark |
|||
color: transparent |
|||
background-color: #9e3cc0 |
|||
|
|||
.matches mark:nth-child(even) |
|||
background-color: #3c7fc2 |
@ -1,15 +1,5 @@ |
|||
|
|||
@extends('app') |
|||
|
|||
@section('content') |
|||
|
|||
<div class="page-wrapper"> |
|||
<app-header></app-header> |
|||
|
|||
<div class="content"> |
|||
<home :filters="{{ json_encode($filters) }}"></home> |
|||
</div> |
|||
</div> |
|||
|
|||
<app-footer></app-footer> |
|||
<home :filters="{{ json_encode($filters) }}"></home> |
|||
@endsection |
@ -0,0 +1,5 @@ |
|||
@extends('app') |
|||
|
|||
@section('content') |
|||
<regex-create></regex-create> |
|||
@endsection |
@ -0,0 +1,5 @@ |
|||
@extends('app') |
|||
|
|||
@section('content') |
|||
<searchers-create></searchers-create> |
|||
@endsection |
@ -0,0 +1,7 @@ |
|||
@extends('app') |
|||
|
|||
@section('content') |
|||
|
|||
<searchers-show :searcher="{{ json_encode($searcher) }}"></searchers-show> |
|||
|
|||
@endsection |
10190
yarn.lock
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
Write
Preview
Loading…
Cancel
Save
Reference in new issue