diff --git a/.gitignore b/.gitignore index 0711527..96a6724 100644 --- a/.gitignore +++ b/.gitignore @@ -40,3 +40,262 @@ testem.log # System files .DS_Store Thumbs.db +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### Node template +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# vuepress v2.x temp and cache directory +.temp + +# Docusaurus cache and generated files +.docusaurus + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* + +### WebStorm template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff + +# AWS User-specific + +# Generated files + +# Sensitive or high-churn files + +# Gradle + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake + +# Mongo Explorer plugin + +# File-based project format + +# IntelliJ + +# mpeltonen/sbt-idea plugin + +# JIRA plugin + +# Cursive Clojure plugin + +# SonarLint plugin + +# Crashlytics plugin (for Android Studio and IntelliJ) + +# Editor-based Rest Client + +# Android studio 3.1+ serialized cache file + diff --git a/README.md b/README.md index c92ea49..c2d211d 100644 --- a/README.md +++ b/README.md @@ -1,27 +1,64 @@ -# SearchTest +# Omni Search Dev Harness -This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 17.3.6. +![](/home/martin/dev/js/search-test/image.png) -## Development server -Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files. +I have created a small test harness for a basic Omni Search box, it is not complete as it's a simple test harness to get the concept essentiall "down on paper". -## Code scaffolding +The options are specified in JSON contained within the `src/app/app.component.ts` file. -Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`. +The JSON is structured as an array of SearchFragments: -## Build +```json -Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. +[ + { + "mode" : { + "title" : "Project", + "accepted" : ".+", + "output" : "Project title {{$modifier}} {{$value}}", + "hint" : "Project title" + }, + "modifiers" : [ + { + "title" : "Is", + "accepted" : "is", + "output" : "IS", + "hint" : "Is Operator" + }, + { + "title" : "Isn't", + "accepted" : "isn't", + "output" : "IS NOT", + "hint" : "Is Not Operator" + }, + { + "title" : "Contains", + "accepted" : "contains", + "output" : "CONTAINS", + "hint" : "Contains Operator" + } + ] -## Running unit tests + }, +] -Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). -## Running end-to-end tests +``` -Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities. +Each fragment contains a single mode, which details what the mode should be. As for modifiers, that is an array so it can hold as many as required. -## Further help +If you look in `src/app/app.component.ts`, there is a commented out entry, if that is uncommented, it should add a fourth entry with four modifiers. + +Code wise, it is fairly simple, not much error checking or even testing as it's a simple harness to get a concept working. + +## Taking it forward + +If work were to continue with it, it would definitely need to be refined to look like the proper search bar. + +I would like to make the Omni Bar items be removable by clicking a cross button or something. + +The output for each item was planned to be a pseudo SQL like structure, but it really needs refinement, and probably multiple parts to make a coherent sql query. + +It also needs testing, which this has none of. -To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page. diff --git a/angular.json b/angular.json index 168e81b..b8de5c5 100644 --- a/angular.json +++ b/angular.json @@ -35,7 +35,8 @@ "src/assets" ], "styles": [ - "src/styles.css" + "src/styles.css", + "src/hammer.css" ], "scripts": [] }, diff --git a/image.png b/image.png new file mode 100644 index 0000000..0c6965a Binary files /dev/null and b/image.png differ diff --git a/notes.md b/notes.md new file mode 100644 index 0000000..599a466 --- /dev/null +++ b/notes.md @@ -0,0 +1,37 @@ +# Development notes + + + +## Search Fragments + +| Item | Notes | +|------|---------------------------------| +| Mode | Project / Assignee / Status etc | + | Modifier | Is / is not etc | +| Target | What to be searched for | +| Operator? | And / or / Not ? Maybe to implement: Bob AND Stuart OR Status = Done | + + +## Mode / Modifier Structure + +| Item | Notes | +|---|---| +| Title | Actual title of the mode: Project / Assignee etc | +| Accepted | Regex to filter whats need. Datetime / Only allow certains words etc | +| Output | The fragment builder text | +| Hint | An alt text hint | + +> Output could be text, could be a string that can be formatted. Have to see which works +> best + + +## Modes + +- Project +- Assignee +- Creator +- Status +- Creation Date +- Due Date +- Tag +- Contains matching text "*" diff --git a/src/_models/mode.ts b/src/_models/mode.ts new file mode 100644 index 0000000..e51f240 --- /dev/null +++ b/src/_models/mode.ts @@ -0,0 +1,9 @@ +export interface ModeItem { + title: string; + accepted: string; + output: string; + hint: string; + +} + + diff --git a/src/_models/modifier.ts b/src/_models/modifier.ts new file mode 100644 index 0000000..86ee96d --- /dev/null +++ b/src/_models/modifier.ts @@ -0,0 +1,7 @@ +export interface ModifierItem { + title: string; + accepted: string; + output: string; + hint: string; + +} diff --git a/src/_models/searchFragment.ts b/src/_models/searchFragment.ts new file mode 100644 index 0000000..fe8dbb3 --- /dev/null +++ b/src/_models/searchFragment.ts @@ -0,0 +1,45 @@ +import { ModeItem} from "./mode"; +import {ModifierItem} from "./modifier"; + +export interface SearchFragment { + + mode: ModeItem; + modifiers: ModifierItem[]; + +} + +export interface SolidSearchFragment { + mode: ModeItem; + modifier: ModifierItem; + searchTerm: string; +} + +/* + + +{ + "mode" : { + "title" : "Project", + "accepted" : ".+", + "output" : "Project title {{$modifier}} {{$value}}", + "hint" : "Project title" + }, + "modifiers" : [ + { + "title" : "Is", + "accepted" : "is", + "output" : "IS", + "hint" : "Is Operator" + }, + { + "title" : "Isn't", + "accepted" : "isn't", + "output" : "IS NOT", + "hint" : "Is Not Operator" + } + ] + + } + + + */ diff --git a/src/app/app.component.html b/src/app/app.component.html index 36093e1..1c0d1d1 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -8,320 +8,12 @@ -
-
-
- -

Hello, {{ title }}

-

Congratulations! Your app is running. 🎉

-
- -
-
- @for (item of [ - { title: 'Explore the Docs', link: 'https://angular.dev' }, - { title: 'Learn with Tutorials', link: 'https://angular.dev/tutorials' }, - { title: 'CLI Docs', link: 'https://angular.dev/tools/cli' }, - { title: 'Angular Language Service', link: 'https://angular.dev/tools/language-service' }, - { title: 'Angular DevTools', link: 'https://angular.dev/tools/devtools' }, - ]; track item.title) { - - {{ item.title }} - - - - - } -
- -
-
+
+

OmniSearch Dev Harness

+
diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 5c3892f..745808e 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,4 +1,5 @@ import { Component } from '@angular/core'; +import { SearchFragment} from "../_models/searchFragment"; @Component({ selector: 'app-root', @@ -7,4 +8,121 @@ import { Component } from '@angular/core'; }) export class AppComponent { title = 'search-test'; + + + omniSearchOptions: SearchFragment[] = [ + { + "mode" : { + "title" : "Project", + "accepted" : ".+", + "output" : "Project title {{$modifier}} {{$value}}", + "hint" : "Project title" + }, + "modifiers" : [ + { + "title" : "Is", + "accepted" : "is", + "output" : "IS", + "hint" : "Is Operator" + }, + { + "title" : "Isn't", + "accepted" : "isn't", + "output" : "IS NOT", + "hint" : "Is Not Operator" + }, + { + "title" : "Contains", + "accepted" : "contains", + "output" : "CONTAINS", + "hint" : "Contains Operator" + } + ] + + }, + { + "mode" : { + "title" : "Assignee", + "accepted" : ".+", + "output" : "Assignee {{$modifier}} {{$value}}", + "hint" : "Assignee ID" + }, + "modifiers" : [ + { + "title" : "Is", + "accepted" : "is", + "output" : "IS", + "hint" : "Is Operator" + }, + { + "title" : "Isn't", + "accepted" : "isn't", + "output" : "IS NOT", + "hint" : "Is Not Operator" + } + ] + + }, + { + "mode" : { + "title" : "Creation Date", + "accepted" : ".+", + "output" : "Assignee is {{}}", + "hint" : "Assignee ID" + }, + "modifiers" : [ + { + "title" : "Is", + "accepted" : "is", + "output" : "IS", + "hint" : "Is Operator" + }, + { + "title" : "Isn't", + "accepted" : "isn't", + "output" : "IS NOT", + "hint" : "Is Not Operator" + } + ] + + } + , + /*{ + "mode" : { + "title" : "Tag", + "accepted" : ".+", + "output" : "Tag is {{}}", + "hint" : "Tag" + }, + "modifiers" : [ + { + "title" : "Is", + "accepted" : "is", + "output" : "IS", + "hint" : "Is Operator" + }, + { + "title" : "Isn't", + "accepted" : "isn't", + "output" : "IS NOT", + "hint" : "Is Not Operator" + }, + { + "title" : "Contains", + "accepted" : "contains", + "output" : "CONTAINS", + "hint" : "Contains Operator" + }, + { + "title" : "Doesn't contain", + "accepted" : "doesn't contain", + "output" : "DOES NOT CONTAIN", + "hint" : "Does Not Contain Operator" + } + ] + + }*/ + + ] + } diff --git a/src/app/app.module.ts b/src/app/app.module.ts index b1c6c96..2f460e8 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -3,14 +3,18 @@ import { BrowserModule } from '@angular/platform-browser'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; +import { OmniSearchBoxComponent } from './omni-search-box/omni-search-box.component'; +import {FormsModule} from "@angular/forms"; @NgModule({ declarations: [ - AppComponent + AppComponent, + OmniSearchBoxComponent ], imports: [ BrowserModule, - AppRoutingModule + AppRoutingModule, + FormsModule ], providers: [], bootstrap: [AppComponent] diff --git a/src/app/omni-search-box/omni-search-box.component.css b/src/app/omni-search-box/omni-search-box.component.css new file mode 100644 index 0000000..7a26e92 --- /dev/null +++ b/src/app/omni-search-box/omni-search-box.component.css @@ -0,0 +1,11 @@ +.omnibox { + + border: 1px solid red; + padding: 3px; +} + + +.surround { + border: 1px solid blue; + padding: 2px; +} diff --git a/src/app/omni-search-box/omni-search-box.component.html b/src/app/omni-search-box/omni-search-box.component.html new file mode 100644 index 0000000..9e7f42b --- /dev/null +++ b/src/app/omni-search-box/omni-search-box.component.html @@ -0,0 +1,48 @@ +{{optionsCount}} items in the config + +
+ + {{omniItem.mode.title}} + {{omniItem.modifier.title}} + {{omniItem.searchTerm}} + + + +
+
{{fullSearch.length}} items in the omni search
+
+
+ +
+ + + +
selectedOption:{{selectedOption}}
+ +
+
+ + +
selectedModifier:{{selectedModifier}}
+
+ +
+ + +
+
+ +
+
+ + +
diff --git a/src/app/omni-search-box/omni-search-box.component.spec.ts b/src/app/omni-search-box/omni-search-box.component.spec.ts new file mode 100644 index 0000000..26b541d --- /dev/null +++ b/src/app/omni-search-box/omni-search-box.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { OmniSearchBoxComponent } from './omni-search-box.component'; + +describe('OmniSearchBoxComponent', () => { + let component: OmniSearchBoxComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [OmniSearchBoxComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(OmniSearchBoxComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/omni-search-box/omni-search-box.component.ts b/src/app/omni-search-box/omni-search-box.component.ts new file mode 100644 index 0000000..7fddeea --- /dev/null +++ b/src/app/omni-search-box/omni-search-box.component.ts @@ -0,0 +1,145 @@ +import {Component, Input, OnInit} from '@angular/core'; +import {SearchFragment, SolidSearchFragment} from "../../_models/searchFragment"; +import {ModifierItem} from "../../_models/modifier"; + +@Component({ + selector: 'app-omni-search-box', + templateUrl: './omni-search-box.component.html', + styleUrl: './omni-search-box.component.css' +}) +export class OmniSearchBoxComponent implements OnInit { + + @Input() searchConfig: SearchFragment[]; + + fullSearch: SolidSearchFragment[] = []; + + optionsCount: number = 0; + currentModifierList: ModifierItem[]; + modifiersListLength: number = 0 + + modifiersDisabled: boolean = true; + searchTextDisabled: boolean = true; + buttonDisabled: boolean = true; + + selectedOption: string; + selectedModifier: string; + searchText: string; + + newSearchItem: SolidSearchFragment; + + ngOnInit() { + + this.resetForm(); + + this.optionsCount = this.searchConfig.length; + + } + + /** + * Reset the form and variables for a new entry + */ + + resetForm() { + this.newSearchItem = this.emptyNewSearchItem(); + this.currentModifierList = []; + this.modifiersListLength = 0; + this.selectedOption = ''; + this.selectedModifier = ''; + this.searchText = '' + + this.modifiersDisabled = true; + this.searchTextDisabled = true; + this.buttonDisabled = true; + } + + /** + * Update the Mode Selection item, and build the operator list + */ + updateModeSelection() { + + this.newSearchItem = this.emptyNewSearchItem(); + this.currentModifierList = []; + + // Find the search Item and add it to the newSearchItem + + let foundMode = this.searchConfig.find((item) => { + + return item.mode.title === this.selectedOption + + }) + + // If we found an item, populate the newSearchItem, and get the modifiers for this selected item + if (foundMode) { + this.newSearchItem.mode = {...foundMode.mode} + + this.currentModifierList = [...foundMode.modifiers] + + this.modifiersListLength = this.currentModifierList.length + + this.modifiersDisabled = false; + } + + } + + /** + * Update the Modifier Selection and put it in the newSearchItem + */ + updateModifierSelection() { + + let foundModifier = this.currentModifierList.find((item) => { + + return item.title === this.selectedModifier; + + }); + + // If we find the correct modifier, populate the newSearchItem's modifier and enable the button and text entry + if (foundModifier) { + this.newSearchItem.modifier = {...foundModifier} + this.buttonDisabled = this.searchTextDisabled = false; + } + + } + + /** + * Search Text Filter, based on the mode text filter + */ + searchTextFilter() { + // todo: Some text / item filtering here + } + + /** + * AddButton click handler + */ + addClickHandler() { + + this.newSearchItem.searchTerm = this.searchText; + + this.fullSearch.push(this.newSearchItem); + + this.resetForm(); + } + + + /** + * Return an empty SolidSearchFragment + */ + emptyNewSearchItem(): SolidSearchFragment { + return { + mode: { + title: '', + accepted: '', + output: '', + hint: '', + }, + modifier: { + title: '', + accepted: '', + output: '', + hint: '', + }, + searchTerm: '' + + } + + } +} diff --git a/src/hammer.css b/src/hammer.css new file mode 100644 index 0000000..4e19761 --- /dev/null +++ b/src/hammer.css @@ -0,0 +1,2 @@ +@import url("https://fonts.googleapis.com/css?family=Roboto+Condensed");*,:after,:before{-webkit-box-sizing:border-box;box-sizing:border-box;margin:0;padding:0}body{font-family:Roboto Condensed,sans-serif;font-size:12px;height:100%;line-height:1.6;overflow-x:hidden}a{color:#5e81ac;text-decoration:none}a:hover{color:#666}a.active{color:#b48ead}ul{list-style:none}img{width:100%}.container{margin:auto;max-width:1100px;padding:0 2rem}hr{border-width:0;border-top:1px solid #2e3440;margin-bottom:2rem;margin-top:2.2rem}.flex{display:-webkit-box;display:-ms-flexbox;display:flex}.pad{padding:.5rem}.pad--1{padding:1rem}.pad--2{padding:2rem}.pad--3{padding:3rem}.pady{padding:.5rem 0}.pady--top{padding-top:.5rem}.pady--bottom{padding-bottom:.5rem}.pady--1{padding:1rem 0}.pady--2{padding:2rem 0}.pady--3{padding:3rem 0}.padx{padding:0 .5rem}.padx--right{padding-right:.5rem}.padx--left{padding-left:.5rem}.padx--1{padding:0 1rem}.padx--2{padding:0 2rem}.padx--3{padding:0 3rem}.grid{display:-webkit-box;display:flex;display:-ms-flexbox;-ms-flex-wrap:wrap;flex-wrap:wrap}.grid--1{grid-gap:1rem;display:grid;grid-template-columns:repeat(1,1fr)}.grid--2{grid-gap:1rem;display:grid;grid-template-columns:repeat(2,1fr)}.grid--3{grid-gap:1px;display:grid;grid-template-columns:repeat(3,1fr)}.grid--4{grid-gap:1rem;display:grid;grid-template-columns:repeat(4,1fr)}.row{margin-left:3px;margin-right:3px}.row:after,.row:before{content:" ";display:table}.row:after{clear:both}.column,.columns{margin-left:4%}.column:first-child,.columns:first-child{margin-left:0}.col-1{width:8.33333%}.col-2{width:16.66667%}.col-3{width:25%}.col-4{width:33.33333%}.col-5{width:41.66667%}.col-6{width:50%}.col-7{width:58.33333%}.col-8{width:66.66667%}.col-9{width:75%}.col-10{width:83.33333%}.col-11{width:91.66667%}.col-12{margin-left:0;width:100%}.col-1-3rd{width:32.666667%}.col-2-3rd{width:65.3333333333%}.col-half{width:48%}.offset-1-col{margin-left:8.66666666667%}.offset-2-col{margin-left:17.3333333333%}.offset-3-col{margin-left:26%}.offset-4-col{margin-left:34.6666666667%}.offset-5-col{margin-left:43.3333333333%}.offset-6-col{margin-left:52%}.offset-7-col{margin-left:60.6666666667%}.offset-8-col{margin-left:69.3333333333%}.offset-9-col{margin-left:78%}.offset-10-col{margin-left:86.6666666667%}.offset-11-col{margin-left:95.3333333333%}.offset-1-3rd-col{margin-left:34.6666666667%}.offset-2-3rd-col{margin-left:69.3333333333%}.offset-half-col{margin-left:52%}.x-large{font-size:4rem}.large,.x-large{line-height:1.2;margin-bottom:1rem}.large{font-size:3rem}.lead{font-size:1.5rem;margin-bottom:1rem}.text-primary{color:#5e81ac}.text-dark,.text-light{color:#2e3440}.text-success{color:#a3be8c}.text-danger{color:#bf616a}.text-highlight{color:#b48ead}.text-highlight2{color:#ebcb8b}.text-center{text-align:center}.text-right{text-align:right}.text-left{text-align:left}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-display1,h1{font-size:34px;font-weight:400;line-height:40px}.text-title,h3{font-size:20px;font-weight:400;line-height:28px}.text-subhead,h4{font-size:16px;font-weight:400;line-height:24px}.text-body2,h5{font-size:14px;font-weight:500;line-height:24px}.text-body1{font-size:14px;font-weight:400;line-height:20px}.text-caption{font-size:12px;font-weight:400;line-height:16px}.align-middle{vertical-align:middle!important}.all-center{-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-box-pack:center;-ms-flex-pack:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;justify-content:center;margin:auto;text-align:center;width:100%}.text-justify{text-align:justify!important}.text-nowrap{white-space:nowrap!important}.align-baseline{vertical-align:baseline!important}.align-top{vertical-align:top!important}.align-bottom{vertical-align:bottom!important}.btn,.btn--slim{background:#e5e9f0;border:none;color:#333;cursor:pointer;display:inline-block;font-size:1rem;margin-right:.5rem;outline:none;padding:.4rem 1.3rem;-webkit-transition:opacity .2s ease-in;transition:opacity .2s ease-in}.btn--slim:disabled,.btn:disabled{-webkit-box-shadow:none;box-shadow:none;cursor:not-allowed;opacity:.6;pointer-events:none}.btn--slim:enabled:hover,.btn:enabled:hover{opacity:.8}.btn--slim{padding:.4rem}.btn-link{background:none;margin:0;padding:0}.btn-block{display:block;width:100%}.btn-sm,.btn-sm--slim{font-size:.8rem;margin-right:.2rem;padding:.3rem 1rem}.btn-sm--slim{padding:.3rem}.badge{border-radius:3px;display:inline-block;font-size:.6rem;margin:.3rem;padding:.1rem .4rem;text-align:center}.alert,.badge{background:#e5e9f0;color:#333}.alert{margin:1rem 0;opacity:.9;padding:.7rem}.alert-primary,.badge-primary,.bg-primary,.btn-primary{background:#5e81ac;color:#fff}.alert-light,.badge-light,.bg-light,.btn-light{background:#e5e9f0;color:#333}.alert-dark,.badge-dark,.bg-dark,.btn-dark{background:#2e3440;color:#fff}.alert-danger,.badge-danger,.bg-danger,.btn-danger{background:#bf616a;color:#fff}.alert-success,.badge-success,.bg-success,.btn-success{background:#a3be8c;color:#fff}.alert-white,.badge-white,.bg-white,.btn-white{background:#fff;border:1px solid #ccc;color:#333}.badge-light,.bg-light{border:1px solid #ccc}.table-responsive{display:block;overflow-x:auto;width:100%}table{border:0;border-collapse:collapse;margin-bottom:1rem;max-width:100%;width:100%}tr{border-top:1px solid #ccc}tbody tr:nth-of-type(odd){background-color:rgba(0,0,0,.04)}tbody td{border-top:1px solid #e1e1e1}.navbar{-webkit-box-pack:justify;-ms-flex-pack:justify;-webkit-box-align:center;-ms-flex-align:center;align-items:center;justify-content:space-between;margin-bottom:1rem;min-height:56px;opacity:.9;position:fixed;width:100%;z-index:2}.navbar,.navbar ul{display:-webkit-box;display:-ms-flexbox;display:flex}.navbar a{color:#fff;margin:0 .25rem;padding:.45rem}.navbar a:hover{color:#e5e9f0}.navbar .welcome span{margin-right:.6rem}.navbar .navbar-section{-webkit-box-align:center;-webkit-box-flex:1;-ms-flex-align:center;align-items:center;display:-webkit-box;display:flex;display:-ms-flexbox;-ms-flex:1 0 0;flex:1 0 0}.navbar .navbar-section:not(:first-child):last-child{-ms-flex-pack:end;-webkit-box-pack:end;justify-content:flex-end}.navbar .navbar-brand{font-size:200%;font-weight:700}header+div.container{height:calc(100vh - 56px);max-height:calc(100vh - 56px);overflow:auto;position:relative;top:56px}.m{margin:.5rem}.m--1{margin:1rem}.m--2{margin:2rem}.m--3{margin:3rem}.mb{margin-bottom:.1rem!important}.mb--1{margin-bottom:.2rem!important}.mb--2{margin-bottom:.4rem!important}.ml{margin-left:.1rem!important}.ml--1{margin-left:.2rem!important}.ml--2{margin-left:.4rem!important}.mr{margin-right:.1rem!important}.mr--1{margin-right:.2rem!important}.mr .mr-2{margin-right:.4rem!important}.mt{margin-top:.1rem!important}.mt--1{margin-top:.2rem!important}.mt--2{margin-top:.4rem!important}.mx{margin-left:.5rem!important;margin-right:.5rem!important}.mx--1{margin-left:1rem!important;margin-right:1rem!important}.mx--2{margin-left:2rem!important;margin-right:2rem!important}.my{margin:.5rem 0}.my--1{margin:1rem 0}.my--2{margin:2rem 0}.my--3{margin:3rem 0}.card,.card--slim{border:1px dotted #ccc;margin:.7rem 0;padding:1rem}.card--slim{padding:5px}.cardTitle{border-bottom:1px solid #eee;margin-bottom:15px}.cardV2{background-color:#fff;border-radius:4px;-webkit-box-shadow:0 0 4px 0 rgba(0,0,0,.14),0 3px 4px 0 rgba(0,0,0,.12),0 1px 5px 0 rgba(0,0,0,.2);box-shadow:0 0 4px 0 rgba(0,0,0,.14),0 3px 4px 0 rgba(0,0,0,.12),0 1px 5px 0 rgba(0,0,0,.2);min-width:0}.seemore{font-size:14px;font-weight:500}.cardLink{color:#2196f3;margin-top:10px}input{margin:.2rem 0}.form-text{color:#888;display:block;margin-top:.3rem}input[type=date],input[type=email],input[type=password],input[type=text],select,textarea{border:1px solid #ccc;display:block;padding:.1rem;width:100%}button,input[type=submit]{font:inherit}label,legend{display:block;font-weight:600;margin-bottom:.1rem}input[type=checkbox],input[type=radio]{display:inline}label>.label-body{background-color:#dcc894;display:inline-block;font-weight:400;margin-left:.5rem}table td,table th{padding:1rem;text-align:left}table th{background:var(--light-color)}.list{margin:.5rem 0}.list li{padding-bottom:.3rem}.dataRow{cursor:pointer}.modalWindow{background:rgba(0,0,0,.2);bottom:0;left:0;opacity:0;pointer-events:none;position:fixed;right:0;text-align:center;top:0;z-index:99999}.modalWindow:target{opacity:1;pointer-events:auto}.modalWindow>div{background:#fff;margin:10% auto;position:relative;width:500px}.alias{cursor:alias}.all-scroll{cursor:all-scroll}.auto{cursor:auto}.cell{cursor:cell}.context-menu{cursor:context-menu}.col-resize{cursor:col-resize}.copy{cursor:copy}.crosshair{cursor:crosshair}.default{cursor:default}.e-resize{cursor:e-resize}.ew-resize{cursor:ew-resize}.grab{cursor:-webkit-grab;cursor:grab}.grabbing{cursor:-webkit-grabbing;cursor:grabbing}.help{cursor:help}.move{cursor:move}.n-resize{cursor:n-resize}.ne-resize{cursor:ne-resize}.nesw-resize{cursor:nesw-resize}.ns-resize{cursor:ns-resize}.nw-resize{cursor:nw-resize}.nwse-resize{cursor:nwse-resize}.no-drop{cursor:no-drop}.none{cursor:none}.not-allowed{cursor:not-allowed}.pointer{cursor:pointer}.progress{cursor:progress}.row-resize{cursor:row-resize}.s-resize{cursor:s-resize}.se-resize{cursor:se-resize}.sw-resize{cursor:sw-resize}.text{cursor:text}.url{cursor:pointer}.w-resize{cursor:w-resize}.wait{cursor:wait}.zoom-in{cursor:-webkit-zoom-in;cursor:zoom-in}.zoom-out{cursor:-webkit-zoom-out;cursor:zoom-out}.panel{background-color:#fff;border-radius:0;-webkit-box-shadow:0 2px 2px 0 rgba(0,0,0,.16),0 0 2px 0 rgba(0,0,0,.12);box-shadow:0 2px 2px 0 rgba(0,0,0,.16),0 0 2px 0 rgba(0,0,0,.12);margin-bottom:20px;padding:15px}.panel:after,.panel:before{content:" ";display:table}.panel:after{clear:both}.glassy{-webkit-backdrop-filter:blur(5px);backdrop-filter:blur(2px);background-color:rgba(31,28,23,.6)}@media (max-width:480px){.container{max-width:98vw;overflow-y:scroll;padding:0}}@media (max-height:480px){.navbar{min-height:36px}header+div.container{max-height:calc(100vh - 36px);position:relative;top:36px}.navbar .navbar-brand{font-size:125%;font-weight:700}}.nrccAlert a{color:#00ffa2} +/*# sourceMappingURL=hammer.css.map */ diff --git a/src/styles.css b/src/styles.css index 90d4ee0..dd3e7eb 100644 --- a/src/styles.css +++ b/src/styles.css @@ -1 +1,2 @@ /* You can add global styles to this file, and also import other style files */ + diff --git a/structure.json b/structure.json new file mode 100644 index 0000000..d764241 --- /dev/null +++ b/structure.json @@ -0,0 +1,71 @@ +[ + { + "mode" : { + "title" : "Project", + "accepted" : ".+", + "output" : "Project title {{$modifier}} {{$value}}", + "hint" : "Project title" + }, + "modifiers" : [ + { + "title" : "Is", + "accepted" : "is", + "output" : "IS", + "hint" : "Is Operator" + }, + { + "title" : "Isn't", + "accepted" : "isn't", + "output" : "IS NOT", + "hint" : "Is Not Operator" + } + ] + + }, + { + "mode" : { + "title" : "Assignee", + "accepted" : ".+", + "output" : "Assignee {{$modifier}} {{$value}}", + "hint" : "Assignee ID" + }, + "modifiers" : [ + { + "title" : "Is", + "accepted" : "is", + "output" : "IS", + "hint" : "Is Operator" + }, + { + "title" : "Isn't", + "accepted" : "isn't", + "output" : "IS NOT", + "hint" : "Is Not Operator" + } + ] + + }, + { + "mode" : { + "title" : "Creation Date", + "accepted" : ".+", + "output" : "Assignee is {{}}", + "hint" : "Assignee ID" + }, + "modifiers" : [ + { + "title" : "Is", + "accepted" : "is", + "output" : "IS", + "hint" : "Is Operator" + }, + { + "title" : "Isn't", + "accepted" : "isn't", + "output" : "IS NOT", + "hint" : "Is Not Operator" + } + ] + + } +] diff --git a/tsconfig.json b/tsconfig.json index eb49734..abd88bb 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -18,6 +18,7 @@ "target": "ES2022", "module": "ES2022", "useDefineForClassFields": false, + "strictPropertyInitialization": false, "lib": [ "ES2022", "dom"