Added new models. Major changes to the table widget. Still has problems with the editing in the table widget.

This commit is contained in:
2022-06-13 07:42:26 -07:00
parent 11fbfcfac0
commit 69ca0d5eb6
15 changed files with 417 additions and 16578 deletions

View File

@@ -361,6 +361,7 @@ html {
font-weight: normal;
font-style: normal;
font-size: 24px;
/* Preferred icon size */
display: inline-block;
line-height: 1;
text-transform: none;
@@ -378,4 +379,8 @@ html {
font-feature-settings: "liga";
}
.material-symbols-outlined {
font-variation-settings: "FILL" 0, "wght" 400, "GRAD" 0, "opsz" 48;
}
/*# sourceMappingURL=main.css.map */

View File

@@ -1 +1 @@
{"version":3,"sourceRoot":"","sources":["simple-grid.sass","app.sass","material-icons.sass"],"names":[],"mappings":"AAAQ;AAER;EACC;EACA;EACA;EACA;EACA;EACA;;;AAED;AAEA;EACC;EACA;EACA;;;AAED;AAEA;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;EACA;EACA;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;AAEA;EACC;;;AAED;EACC;;;AAED;EACC;EACA;EACA;;;AAED;EACC;;;AAED;AAEA;EAEC;EACA;EACA;;;AAED;EACC;;AAGA;EACC;EACA;EACA;;;AAEF;EACC;EACA;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;EACA;EACA;;;AAED;EACC;;;AAED;AACC;EAEA;IACC;;;AAEF;AACC;EAEA;IACC;;;EAED;IACC;;;EAED;IACC;;;EAED;IACC;;;EAED;IACC;;;EAED;IACC;;;EAED;IACC;;;EAED;IACC;;;EAED;IACC;;;EAED;IACC;;;EAED;IACC;;;EAED;IACC;;;EAED;IACC;;;AAEF;AACC;EAEA;IACC;IACA;;;ACzLF;AACA;EACC;;;AAED;EACC;;;AAED;EACC;EACA;;;AAED;EACC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAQD;EACC;EACA;EACA;;;AAED;EACC;EACA;EACA;;;AAED;EACC;EACA;EACA;EACA;EACA;EACA;EACA;;;AAED;EACC;;;AAED;EACC;EACA;EACA;;;AAED;EACC;EACA;EACA;EACA;EACA;EACA;;;AAED;EACC;EACA;EACA;EACA;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;EACA;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;IACC;;;AAEF;EACC;IACC;;;EAED;IACC;IACA;;;EAED;IACC;;;EAED;IACC;;;AC3GF;EACC;EACA;EACA;EACA;EACA;;AAED;EACC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;AAEA;EACA;AACA;EACA;AAEA;EACA;AAEA;EACA","file":"main.css"}
{"version":3,"sourceRoot":"","sources":["simple-grid.sass","app.sass","material-icons.sass"],"names":[],"mappings":"AAAQ;AAER;EACC;EACA;EACA;EACA;EACA;EACA;;;AAED;AAEA;EACC;EACA;EACA;;;AAED;AAEA;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;EACA;EACA;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;AAEA;EACC;;;AAED;EACC;;;AAED;EACC;EACA;EACA;;;AAED;EACC;;;AAED;AAEA;EAEC;EACA;EACA;;;AAED;EACC;;AAGA;EACC;EACA;EACA;;;AAEF;EACC;EACA;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;EACA;EACA;;;AAED;EACC;;;AAED;AACC;EAEA;IACC;;;AAEF;AACC;EAEA;IACC;;;EAED;IACC;;;EAED;IACC;;;EAED;IACC;;;EAED;IACC;;;EAED;IACC;;;EAED;IACC;;;EAED;IACC;;;EAED;IACC;;;EAED;IACC;;;EAED;IACC;;;EAED;IACC;;;EAED;IACC;;;AAEF;AACC;EAEA;IACC;IACA;;;ACzLF;AACA;EACC;;;AAED;EACC;;;AAED;EACC;EACA;;;AAED;EACC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAQD;EACC;EACA;EACA;;;AAED;EACC;EACA;EACA;;;AAED;EACC;EACA;EACA;EACA;EACA;EACA;EACA;;;AAED;EACC;;;AAED;EACC;EACA;EACA;;;AAED;EACC;EACA;EACA;EACA;EACA;EACA;;;AAED;EACC;EACA;EACA;EACA;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;EACA;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;IACC;;;AAEF;EACC;IACC;;;EAED;IACC;IACA;;;EAED;IACC;;;EAED;IACC;;;ACzGF;EACC;EACA;EACA;EACA;EACA;;AAED;EACC;EACA;EACA;EACA;AACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;AACA;EACA;AACA;EACA;AACA;EACA;AACA;EACA;;;AAED;EACC","file":"main.css"}

View File

@@ -1,3 +1,5 @@
// https://github.com/google/material-design-icons
@font-face
font-family: 'Material Icons'
font-style: normal
@@ -9,7 +11,8 @@
font-family: 'Material Icons'
font-weight: normal
font-style: normal
font-size: 24px /* Preferred icon size */
font-size: 24px
/* Preferred icon size */
display: inline-block
line-height: 1
text-transform: none
@@ -17,14 +20,14 @@
word-wrap: normal
white-space: nowrap
direction: ltr
/* Support for all WebKit browsers. */
-webkit-font-smoothing: antialiased
/* Support for Safari and Chrome. */
text-rendering: optimizeLegibility
/* Support for Firefox. */
-moz-osx-font-smoothing: grayscale
/* Support for IE. */
font-feature-settings: 'liga'
.material-symbols-outlined
font-variation-settings: 'FILL' 0, 'wght' 400, 'GRAD' 0, 'opsz' 48

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,3 +1,6 @@
import "./users.js";
import "./data-collection.js";
import "./admin.js";
import "./students.js";
import "./rooms.js";
import "./sites.js";

24
imports/api/rooms.js Normal file
View File

@@ -0,0 +1,24 @@
import {Mongo} from "meteor/mongo";
import {Meteor} from "meteor/meteor";
export const Rooms = new Mongo.Collection('rooms');
if (Meteor.isServer) {
// This code only runs on the server
Meteor.publish('rooms', function(siteId) {
return Rooms.find({siteId});
});
}
Meteor.methods({
'rooms.add'(name, siteId) {
if(Roles.userIsInRole(Meteor.userId(), "admin", {anyScope:true})) {
Rooms.insert({name, siteId});
}
},
'rooms.remove'(_id) {
if(Roles.userIsInRole(Meteor.userId(), "admin", {anyScope:true})) {
//TODO: Need to first verify there are no checked out assets to the room.
}
},
});

37
imports/api/sites.js Normal file
View File

@@ -0,0 +1,37 @@
import {Mongo} from "meteor/mongo";
import {Meteor} from "meteor/meteor";
import {Students} from "./students";
import {Rooms} from "./rooms";
export const Sites = new Mongo.Collection('sites');
if (Meteor.isServer) {
// This code only runs on the server
Meteor.publish('sites', function() {
return Sites.find({});
});
}
Meteor.methods({
'sites.update'(_id, name) {
if(Roles.userIsInRole(Meteor.userId(), "admin", {anyScope:true})) {
Sites.update({_id}, {$set: {name}});
}
},
'sites.add'(name) {
if(Roles.userIsInRole(Meteor.userId(), "admin", {anyScope:true})) {
Sites.insert({name});
}
},
'sites.remove'(_id) {
if(Roles.userIsInRole(Meteor.userId(), "admin", {anyScope:true})) {
let site = Sites.find({_id});
if(site) {
//Clear any site references in student/room entries.
Students.update({siteId: _id}, {$unset: {siteId: 1}});
Rooms.update({siteId: _id}, {$unset: {siteId: 1}});
Sites.remove({_id});
}
}
},
});

66
imports/api/students.js Normal file
View File

@@ -0,0 +1,66 @@
import { Meteor } from 'meteor/meteor';
import { Mongo } from 'meteor/mongo';
import { check } from 'meteor/check';
import {Sites} from "./sites";
export const Students = new Mongo.Collection('students');
if (Meteor.isServer) {
// This code only runs on the server
Meteor.publish('students', function(siteId) {
return Students.find({siteId});
});
}
Meteor.methods({
/**
* Expects the CSV string to contain comma delimited data in the form:
* email, student ID, first name, last name, grade, first name alias, last name alias
*
* The query in Aeries is: `LIST STU SEM ID FN LN GR FNA LNA`.
* The query in SQL is: `SELECT [STU].[SEM] AS [StuEmail], [STU].[ID] AS [Student ID], STU.FN AS [First Name], STU.LN AS [Last Name], [STU].[GR] AS [Grade], [STU].[FNA] AS [First Name Alias], [STU].[LNA] AS [Last Name Alias] FROM (SELECT [STU].* FROM STU WHERE [STU].DEL = 0) STU WHERE ( [STU].SC = 5) ORDER BY [STU].[LN], [STU].[FN];`.
* Run the query in Aeries as a `Report`, select TXT, and upload here.
*
* Aeries adds a header per 'page' of data (I think 35 entries per page).
* Example:
* Anderson Valley Jr/Sr High School,6/11/2022
* 2021-2022,Page 1
* StuEmail,Student ID,First Name,Last Name,Grade,First Name Alias,Last Name Alias
*/
'students.loadCsv'(csv, siteId) {
if(Roles.userIsInRole(Meteor.userId(), "admin", {anyScope:true})) {
check(csv, String);
check(siteId, String);
let site = Sites.find({_id: siteId});
if(site) {
let lines = csv.split(/\r?\n/);
let pageHeader = lines[0];
let skip;
for (const line of lines) {
if (skip > 0) skip--;
else if (line === pageHeader) {
skip = 2;
} else {
let values = line.split(/\s*,\s*/);
let email = values[0];
let id = values[1];
let firstName = values[2];
let lastName = values[3];
let grade = values[4];
let firstNameAlias = values[5];
let lastNameAlias = values[6];
let student = {email, id, firstName, lastName, grade, firstNameAlias, lastNameAlias};
console.log(student);
//TODO: Find existing student. Update student, and move them to the new site.
//TODO: Create student if none exists.
}
}
}
}
}
});

View File

@@ -1,8 +1,124 @@
<script>
import {Meteor} from "meteor/meteor";
import {onMount} from "svelte";
import {Sites} from "../api/sites";
import GridTable from "./GridTable.svelte";
import {writable} from "svelte/store";
import TextField from '@smui/textfield';
import HelperText from '@smui/textfield/helper-text';
onMount(async () => {
Meteor.subscribe('sites');
});
const fixRecords = () => {Meteor.call("admin.fixRecords");}
const submitForm = () => {
Meteor.call('students.loadCsv');
}
let files;
const siteColumns = [
{
key: "_id",
title: "ID",
value: v => v._id,
minWidth: 20,
weight: 1,
cls: "id",
}, {
key: "name",
title: "Name",
value: v => v.name,
minWidth: 100,
weight: 1,
cls: "name",
},
];
const actions = {
title: "Actions",
headerWidgets: [
{icon: "add_box", action: () => {editedSite.set({name: ""});}, tooltip: "Add a new Site."}
],
rowWidgets: [
{icon: "add_circle", action: (v) => {editedSite.set(v)}},
{icon: "delete", action: (v) => {deleteSite(v)}}
],
};
const deleteSite = site => {
//TODO:
};
let editedSite = writable(null);
let sites = Sites.find({});
const applySiteChanges = () => {
if($editedSite._id)
Meteor.call("sites.update", $editedSite._id,$editedSite.name);
else
Meteor.call("sites.add", $editedSite.name);
editedSite.set(null);
}
const rejectSiteChanges = () => {
editedSite.set(null);
}
</script>
<div class="container">
<button type="button" on:click={fixRecords}>Fix Records</button>
<GridTable bind:rows={sites} columns="{siteColumns}" actions="{actions}" rowKey="{(user) => {return user._id}}" bind:edited="{editedSite}">
{#if $editedSite}
<div class="editorContainer">
<div style="grid-column: 1/span 1">
<TextField type="text" style="width: 100%" bind:value={$editedSite.name} label="Name">
<HelperText slot="helper">Provide a unique name for the site.</HelperText>
</TextField>
</div>
<button type="button" style="grid-column: 2/span 1;" class="button accept-button material-icons material-symbols-outlined" on:click={applySiteChanges}>
check
</button>
<button type="button" style="grid-column: 3/span 1;" class="button reject-button material-icons material-symbols-outlined" on:click={rejectSiteChanges}>
close
</button>
</div>
{/if}
</GridTable>
<!--{#each sites as site}-->
<!-- <div>{site.name}</div>-->
<!--{/each}-->
<!-- <button type="button" on:click={fixRecords}>Fix Records</button>-->
<form on:submit={submitForm}>
<input type="file" bind:files={files}/>
<input type="submit"/>
</form>
</div>
<style>
.editorContainer {
display: grid;
grid-template-columns: minmax(10px, 1fr) minmax(3rem, 3rem) minmax(3rem, 3rem);
}
.accept-button, .reject-button {
font-size: .8rem;
padding: .6rem;
margin: auto;
color: white;
border-radius: 50%;
border: 0;
font-weight: 800;
alignment: center;
cursor: pointer;
}
.accept-button {
background-color: rgba(61, 148, 61, 0.91);
}
.reject-button {
background-color: rgba(176, 64, 64, 0.61);
}
.accept-button:hover {
background-color: rgb(61, 148, 61);
}
.reject-button:hover {
background-color: rgb(176, 64, 64);
}
</style>

View File

@@ -51,6 +51,9 @@
import ChromebookScan from './ChromebookScan.svelte';
import {Meteor} from "meteor/meteor";
import TextField from '@smui/textfield';
import HelperText from '@smui/textfield/helper-text';
import Icon from '@smui/textfield/icon';
import { Icon as CommonIcon } from '@smui/common';
$: serialInput = null;
$: emailInput = null;
@@ -150,7 +153,12 @@
<ul>
<li>
<TextField type="text" bind:value={emailInput} label="Email">
<TextField type="text" bind:value={emailInput} on:keypress={(e) => {if(e.keyCode === 13) emailSearch()}} label="Email">
<svelte:fragment slot="label">
<CommonIcon class="material-icons" style="font-size: 1em; line-height: normal; vertical-align: top;">email</CommonIcon> Email
</svelte:fragment>
<Icon class="material-icons" slot="trailingIcon">search</Icon>
<HelperText slot="helper">Type any part of an email address.</HelperText>
</TextField>
</li>
<li>

View File

@@ -1,17 +1,29 @@
<script>
export let rows;
export let columns;
export let rowKey;
export let rowKey; //Must only be null/undefined if the row is a new object (not associated with a row in the table). Should not change.
export let edited;
export let actions;
// Setup a width for each column.
columns.forEach(column => {
let min = column.minWidth ? Math.max(10, column.minWidth) : 10;
let weight = column.weight ? Math.max(1, column.weight) : 1;
column.width = 'minmax(' + min + 'px, ' + weight + 'fr)';
column.isActions = false;
});
// Add the actions column to the end.
// TODO: Allow it to be positioned.
if(actions) {
// TODO: make this fixed width based on the possible widget widths.
actions.width = 'minmax(10px, 1fr)';
actions.isActions = true;
columns[columns.length] = actions;
}
// Collect the column widths for the layout.
let gridTemplateColumns = columns.map(({width}) => width).join(' ');
// Resize column code.
let headerBeingResized = null;
let horizontalScrollOffset = 0;
const initResize = ({target}) => {
@@ -46,6 +58,7 @@
} catch(e) {console.log(e);}
}
// Select row code.
let selectedRowElement = null;
const selectRow = (e, row) => {
let element = e.target;
@@ -60,12 +73,44 @@
element.classList.add('selected');
}
// Edit row code.
let editorContainer;
let editorTable;
// Listen for changes to the edited variable. Move the editor row and display it when there is a value.
// Relies on the table row being hidden when the edited value matches the row's value.
$: {
if(editorContainer) {
if($edited) {
let id = rowKey($edited);
let hiddenRow = editorTable.querySelector('tbody tr[data-key="' + id + '"]');
if(!hiddenRow) {
let body = editorTable.querySelector('tbody');
body.firstChild ? body.insertBefore(editorContainer, body.firstChild) : body.appendChild(editorContainer);
editorContainer.classList.remove('hidden');
}
else {
//let editor = hiddenRow.querySelector('.editor');
let body = editorTable.querySelector('tbody');
let next = hiddenRow.nextSibling;
//editor.appendChild(editorContainer);
next ? body.insertBefore(editorContainer, next) : body.appendChild(editorContainer);
editorContainer.classList.remove('hidden');
}
}
else {
console.log("Edited cleared");
editorContainer.classList.add('hidden');
}
}
}
/*
const editRow = (e, row) => {
let element = e.target;
while(element && element.nodeName !== "TR") element = element.parentNode;
let editor = element.querySelector('.editor');
isNew = false;
// Save the edited row so the editor has access to it.
$edited = row;
@@ -74,32 +119,60 @@
}
editorContainer.classList.remove('hidden');
}
const newRow = () => {
isNew = true;
newEditor.appendChild(editorContainer);
editorContainer.classList.remove('hidden');
}
*/
</script>
<div bind:this={editorContainer} class="hidden"><slot>Slot</slot></div>
<table style="--grid-template-columns: {gridTemplateColumns}">
<div bind:this={editorContainer} class="editor2 hidden"><slot>Slot</slot></div>
<table bind:this={editorTable} style="--grid-template-columns: {gridTemplateColumns}">
<thead>
<tr>
{#each columns as column}
{#if column.isActions}
<th bind:this={column.element}>{column.title}
{#each column.headerWidgets as widget}
<span class="material-icons material-symbols-outlined" on:click={widget.action} title="{widget.tooltip}">{widget.icon}</span>
{/each}
</th>
{:else}
<th bind:this={column.element}>{column.title} <span class="resize-handle" on:mousedown={initResize}></span></th>
{/if}
{/each}
</tr>
</thead>
<tbody>
{#each $rows as row (rowKey(row))}
<!-- data-key="{rowKey(row)}"-->
<tr class:hidden={row === $edited} on:mousedown={(e) => selectRow(e, row)} on:dblclick={(e) => editRow(e, row)}>
<!-- on:dblclick={(e) => editRow(e, row)} -->
<tr data-key="{rowKey(row)}" class:hidden={row === $edited} on:mousedown={(e) => selectRow(e, row)} on:dblclick={(e) => {$edited = row}}>
{#each columns as column}
<td>{column.value(row)}</td>
{#if column.isActions}
<td>
{#each column.rowWidgets as widget}
<span class="material-icons material-symbols-outlined" on:click={widget.action(row)}>{widget.icon}</span>
{/each}
</td>
{:else}
<td>{column.value(row)}</td>
{/if}
{/each}
<td class="editor"></td>
</tr>
{/each}
</tbody>
</table>
<button on:click={() => {$edited = null}} type="button">Stop Editing</button>
<!--<button on:click={() => {$edited = null}} type="button">Stop Editing</button>-->
<style>
.material-icons {
cursor: pointer;
}
thead .material-icons {
color: white;
}
table {
width: auto;
-webkit-box-flex: 1;
@@ -122,10 +195,10 @@
position: -webkit-sticky;
position: sticky;
top: 0;
background: #5cb85c;
background: #3f4f3f;
text-align: left;
font-weight: normal;
font-size: 1.1rem;
font-size: 1rem;
color: white;
position: relative;
}
@@ -163,7 +236,7 @@
background-color: yellow;
}
.editor {
grid-column: 1 / 4;
grid-column: 1 / -1;
display: none;
}
/*:global(td.hidden) {*/
@@ -178,4 +251,8 @@
:global(div.hidden) {
display: none !important;
}
.editor2 {
grid-column: 1/-1;
}
</style>

View File

@@ -43,10 +43,8 @@
//TODO: Setup the editor for the given row.
}
let edited = writable(null);
let editedPermissions = null;
$: rows = Meteor.users.find({});
let editedPermissions = null;
edited.subscribe((value) => {
if(value) {
editedPermissions = {
@@ -82,7 +80,7 @@
Loading...
{:then allUsers}
<GridTable bind:rows={rows} columns="{columns}" rowKey="{getRowKey}" bind:edited="{edited}">
{#if editedPermissions}
{#if editedPermissions && $edited && $edited.profile}
<div class="editorContainer">
<label style="grid-column: 1/4; font-weight: 800; border-bottom: 2px solid #888; margin-bottom: 0.5rem">{$edited.profile.name}</label>
<label class="checkbox" style="grid-column: 1/4;"><input type="checkbox" bind:checked="{editedPermissions.isAdmin}" style="--form-control-color: black"/> Administrator</label>

46
package-lock.json generated
View File

@@ -22,6 +22,7 @@
"ws": "^8.4.2"
},
"devDependencies": {
"@smui/common": "^6.0.0-beta.16",
"@smui/textfield": "^6.0.0-beta.16",
"chai": "^4.2.0",
"rollup-plugin-css-only": "^3.1.0",
@@ -2695,22 +2696,6 @@
"node": ">=0.10.0"
}
},
"node_modules/rollup": {
"version": "2.75.5",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.75.5.tgz",
"integrity": "sha512-JzNlJZDison3o2mOxVmb44Oz7t74EfSd1SQrplQk0wSaXV7uLQXtVdHbxlcT3w+8tZ1TL4r/eLfc7nAbz38BBA==",
"dev": true,
"peer": true,
"bin": {
"rollup": "dist/bin/rollup"
},
"engines": {
"node": ">=10.0.0"
},
"optionalDependencies": {
"fsevents": "~2.3.2"
}
},
"node_modules/rollup-plugin-css-only": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/rollup-plugin-css-only/-/rollup-plugin-css-only-3.1.0.tgz",
@@ -3007,19 +2992,6 @@
"node": ">=4"
}
},
"node_modules/typescript": {
"version": "4.7.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.3.tgz",
"integrity": "sha512-WOkT3XYvrpXx4vMMqlD+8R8R37fZkjyLGlxavMc4iB8lrl8L0DeTcHbYgw/v0N/z9wAFsgBhcsF0ruoySS22mA==",
"peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=4.2.0"
}
},
"node_modules/underscore": {
"version": "1.13.2",
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.2.tgz",
@@ -5530,16 +5502,6 @@
"integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
"dev": true
},
"rollup": {
"version": "2.75.5",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.75.5.tgz",
"integrity": "sha512-JzNlJZDison3o2mOxVmb44Oz7t74EfSd1SQrplQk0wSaXV7uLQXtVdHbxlcT3w+8tZ1TL4r/eLfc7nAbz38BBA==",
"dev": true,
"peer": true,
"requires": {
"fsevents": "~2.3.2"
}
},
"rollup-plugin-css-only": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/rollup-plugin-css-only/-/rollup-plugin-css-only-3.1.0.tgz",
@@ -5765,12 +5727,6 @@
"integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==",
"dev": true
},
"typescript": {
"version": "4.7.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.3.tgz",
"integrity": "sha512-WOkT3XYvrpXx4vMMqlD+8R8R37fZkjyLGlxavMc4iB8lrl8L0DeTcHbYgw/v0N/z9wAFsgBhcsF0ruoySS22mA==",
"peer": true
},
"underscore": {
"version": "1.13.2",
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.2.tgz",

View File

@@ -50,6 +50,7 @@
"css": true
},
"devDependencies": {
"@smui/common": "^6.0.0-beta.16",
"@smui/textfield": "^6.0.0-beta.16",
"chai": "^4.2.0",
"rollup-plugin-css-only": "^3.1.0",