initial mockup

main
Steve Ayerhart 2022-04-10 14:44:29 -04:00
commit 684ed78fb7
No known key found for this signature in database
GPG Key ID: 5C815FDF3A00B8BA
18 changed files with 2023 additions and 0 deletions

24
.gitignore vendored Normal file
View File

@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

3
.vscode/extensions.json vendored Normal file
View File

@ -0,0 +1,3 @@
{
"recommendations": ["johnsoncodehk.volar"]
}

7
README.md Normal file
View File

@ -0,0 +1,7 @@
# Vue 3 + Vite
This template should help get you started developing with Vue 3 in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
## Recommended IDE Setup
- [VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=johnsoncodehk.volar)

13
index.html Normal file
View File

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>reMARCable</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>

1143
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

17
package.json Normal file
View File

@ -0,0 +1,17 @@
{
"name": "remarcable",
"private": true,
"version": "0.0.0",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"vue": "^3.2.25"
},
"devDependencies": {
"@vitejs/plugin-vue": "^2.3.0",
"vite": "^2.9.0"
}
}

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

359
src/App.vue Normal file
View File

@ -0,0 +1,359 @@
<script setup>
import { ref, reactive } from 'vue'
import MarcRecords from './components/MarcRecords.vue'
import MarcFileUploader from './components/MarcFileUploader.vue'
const records = reactive([]);
const marcFileLoaded = ref(false);
function onMarcFileLoaded(event) {
marcFileLoaded.value = true;
}
records.push(
{
leader: "01505nas 2200325 i 4500",
directory: '4500001001400000003000800014005001700022006001900039007001500058008004100073022002500114040003500139050001000174082001500184245002600199264004600225300002200271310002100293336002100314337002300335338003200358362001000390500007400400520044900474588009200923650002601015710005301041776003101094856005401125',
fields: [
{
tag: "022",
subfields: [
{
code: 'a',
content: '0022-2372'
},
{
code: 'y',
content: '1545-1532'
}
]
},
{
tag: "040",
subfields: [
{
code: 'a',
content: 'StDuBDS'
},
{
code: 'b',
content: 'eng'
},
{
code: 'c',
content: 'StDuBDS'
},
{
code: 'e',
content: 'erda'
},
{
code: 'e',
content: 'epn'
},
]
},
{
tag: "050",
subfields: [
{
code: 'a',
content: 'QL700'
}
]
},
{
tag: "082",
subfields: [
{
code: 'a',
content: '599.05'
},
{
code: '2',
content: '23'
}
]
},
{
tag: "245",
subfields: [
{
code: 'a',
content: 'Journal of mammology.'
}
]
},
{
tag: "264",
subfields: [
{
code: 'a',
content: 'Oxford :'
},
{
code: 'b',
content: 'Oxford University Press,'
},
{
code: 'c',
content: '1919 -'
}
]
},
{
tag: "300",
subfields: [
{
code: 'a',
content: '1 online resource'
}
]
},
{
tag: "310",
subfields: [
{
code: 'a',
content: 'Six times a year'
}
]
},
{
tag: "336",
subfields: [
{
code: 'a',
content: 'text'
},
{
code: '2',
content: 'rdacontent'
}
]
},
{
tag: "337",
subfields: [
{
code: 'a',
content: 'computer'
},
{
code: '2',
content: 'rdamedia'
}
]
},
{
tag: "338",
subfields: [
{
code: 'a',
content: 'online resource'
},
{
code: '2',
content: 'rdacarrier'
}
]
},
{
tag: "362",
subfields: [
{
code: 'a',
content: '1919-'
}
]
},
{
tag: "500",
subfields: [
{
code: 'a',
content: '"Published on behalf of American Society of Mammalogists"--Home page.'
}
]
},
{
tag: "520",
subfields: [
{
code: 'a',
content: 'The Journal of Mammalogy has been the flagship publication of the American Society of Mammalogists since 1919 and was voted one of the top 100 most influential serials in biology and medicine of the 20th century. This highly respected international scientific journal is produced 6 times per year and promotes interest in mammals throughout the world by the publication of original and timely research on all aspects of the biology of mammals.'
}
]
},
{
tag: "588",
subfields: [
{
code: 'a',
content: 'Description based on online resource; title from home page (viewed on October 3, 2016).'
}
]
},
{
tag: "650",
subfields: [
{
code: 'a',
content: 'Mammals'
},
{
code: 'v',
content: 'Periodicals'
}
]
},
{
tag: "710",
subfields: [
{
code: 'a',
content: 'American Society of Mammalogists,'
},
{
code: 'e',
content: 'issuing body.'
}
]
},
{
tag: "776",
subfields: [
{
code: 'i',
content: 'Print version :'
},
{
code: 'x',
content: '1545-1542'
}
]
},
{
tag: "856",
subfields: [
{
code: 'a',
content: 'Oxford journals'
},
{
code: 'u',
content: 'https://academic.oup.com/jmammal'
}
]
},
]
});
records.push(
{
leader: "xxxxxxxxxxxxxxxxxxxxxxxx",
directory: "directory",
fields: [
{
tag: "123",
subfields: [
{
code: 'a',
content: 'some content'
}
]
},
{
tag: "999",
subfields: [
{
code: 'a',
content: 'some content'
},
{
code: 'b',
content: 'some content'
},
{
code: 'c',
content: 'some content more content'
},
{
code: 'd',
content: 'some content content content content content'
},
{
code: 'e',
content: 'some content content content content content content content content content content content content content'
},
]
},
{
tag: "321",
subfields: [
{
code: 'a',
content: 'some content'
}
]
},
]
});
</script>
<template>
<div id="components" :class="{ active: marcFileLoaded }">
<MarcFileUploader :class="{ mini: marcFileLoaded }" @marc-file-loaded="onMarcFileLoaded"/>
<MarcRecords v-model="records" v-if="marcFileLoaded"/>
</div>
</template>
<style>
html {
box-sizing: border-box;
font-size: 62.5%;
}
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
color: #2c3e50;
width: 100vw;
height: 100vh;
}
ul {
list-style: none;
margin: 0;
padding: 0;
}
body {
margin: 0;
padding: 0;
font-size: 1.3em;
letter-spacing: .01em;
line-height: 1.6;
background-color: #f2f2f2;
}
*, *:after, *:before {
box-sizing: inherit;
}
#components {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
#components.active {
align-items: normal;
justify-content: normal;
}
</style>

BIN
src/assets/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

@ -0,0 +1,35 @@
<script setup>
import { defineProps, defineEmits, computed, ref } from 'vue'
const props = defineProps({
directory: String
});
const isFull = ref(false);
</script>
<template>
<div class="directory" :class="{full: isFull}" @click.prevent="isFull = !isFull">
<span> {{ props.directory }}</span>
</div>
</template>
<style>
.directory {
flex: 1;
font-family: monospace;
font-weight: bold;
font-size: 1.3em;
padding: 0 .5rem 0 0;
overflow: hidden;
text-overflow: ellipsis;
cursor: zoom-in;
}
.directory.full {
cursor: zoom-out;
flex: auto;
word-break: break-word;
}
</style>

59
src/components/Fields.vue Normal file
View File

@ -0,0 +1,59 @@
<script setup>
import { ref, toRef, computed } from 'vue'
import Subfields from './Subfields.vue'
const props = defineProps({
modelValue: Array,
})
const isControlField = computed(() => {
return true;
});
</script>
<template>
<div class="fields">
<ul>
<li v-for="field in modelValue">
<div class="field">
<div class="tag">
{{ field.tag }}
</div>
<Subfields v-model="field.subfields"/>
</div>
</li>
</ul>
</div>
</template>
<style>
.fields {
background-color: #eeeeee;
}
.field:last {
border: none;
}
.field {
display: flex;
width: 100%;
align-items: stretch;
border-bottom: 1px solid #fff;
}
.field .tag {
padding: .25rem .5rem;
background-color: #4c3973;
color: #fff;
font-family: monospace;
font-weight: bold;
font-size: 1.3rem;
}
.field .subfields {
width: 100%;
}
</style>

110
src/components/Leader.vue Normal file
View File

@ -0,0 +1,110 @@
<script setup>
import { defineProps, computed } from 'vue'
const props = defineProps({
leader: String
})
const parsedLeader = computed(() => {
return [
{
description: 'Record length',
value: props.leader.slice(0, 4)
},
{
description: 'Record status',
value: props.leader.slice(4, 5)
},
{
description: 'Type of record',
value: props.leader.slice(5, 6)
},
{
description: 'Bibliographic level',
value: props.leader.slice(6, 7)
},
{
description: 'Type of control',
value: props.leader.slice(7, 8)
},
{
description: 'Character coding scheme',
value: props.leader.slice(8, 9)
},
{
description: 'Indicator count',
value: props.leader.slice(9, 10)
},
{
description: 'Subfield code count',
value: props.leader.slice(10, 11)
},
{
description: 'Base address of data',
value: props.leader.slice(11, 16)
},
{
description: 'Encoding Level',
value: props.leader.slice(16, 17)
},
{
description: 'Descriptive cataloging form',
value: props.leader.slice(17, 18)
},
{
description: 'Multipart resource record level',
value: props.leader.slice(18, 19)
},
{
description: 'Length of the length-of-field portion',
value: props.leader.slice(19, 20)
},
{
description: 'Length of the starting-cahracter-position portion',
value: props.leader.slice(20, 21)
},
{
description: 'Length of the implementation-defined portion',
value: props.leader.slice(21, 22)
},
{
description: 'undefined',
value: props.leader.slice(22, 23)
},
]
});
</script>
<template>
<div class="leader">
<ul>
<li v-for="section in parsedLeader">
<span>{{ section.value }}</span>
</li>
</ul>
</div>
</template>
<style>
.leader {
font-family: monospace;
font-weight: bold;
font-size: 1.3em;
padding: 0 .5rem 0 0;
}
.leader ul li {
display: inline-block;
text-align: center;
background-color: rgba(217, 107, 11, .25);
}
.leader ul li:after {
content: '\22c5';
display: inline-block;
padding: 0 .5rem;
}
.leader ul li:last-child:after {
content: none;
}
</style>

View File

@ -0,0 +1,118 @@
<script setup>
import { defineEmits } from 'vue'
const emit = defineEmits(['marc-file-loaded']);
function onDrop(event) {
console.log("dropped");
}
function onProcessClicked(event) {
emit('marc-file-loaded');
}
</script>
<template>
<section id="marc-file-uploader" class="">
<div id="drop-zone" @drop.prevent="onDrop">
<label for="marc-file-input">
<p>Drop a MARC file here</p>
<p><strong>or</strong></p>
<p>click to select file</p>
<input type="file" id="marc-file-input" />
</label>
</div>
<button id="marc-process-button" @click.prevent="onProcessClicked">Upload</button>
</section>
</template>
<style scoped>
#marc-file-uploader {
font-family: 'Open Sans', Arial, sans-serif;
font-size: 1.6rem;
width: 50vh;
height: 50vh;
background-color: #473367;
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
}
#marc-file-uploader #marc-process-button {
width: 25%;
margin: 0 2rem 2rem 2rem;
background-color: #d96b0b;
border: none;
height: 2.6rem;
font-weight: bold;
cursor: pointer;
text-transform: uppercase;
color: #fff;
}
#marc-file-uploader #drop-zone {
width: 100%;
height: 100%;
padding: 6rem;
}
#marc-file-uploader #drop-zone label {
cursor: pointer;
color: #fff;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
border: 1px solid #8162b2;
background-color: rgba(129, 94, 178, .25);
}
#marc-file-uploader input[type=file]:not(:focus-visible) {
position: absolute !important;
width: 1px !important;
height: 1px !important;
padding: 0 !important;
margin: -1px !important;
overflow: hidden !important;
clip: rect(0, 0, 0, 0) !important;
white-space: nowrap !important;
border: 0 !important;
}
#marc-file-uploader.mini {
flex: 0;
width: 100%;
height: auto;
max-height: auto;
flex-direction: row;
margin: 0;
padding: .8rem 1rem;
align-items: normal;
}
#marc-file-uploader.mini #marc-process-button {
padding: 0;
margin: 0 0 0 .8rem;
}
#marc-file-uploader.mini #drop-zone {
padding: 0;
}
#marc-file-uploader.mini #drop-zone label {
cusor: default;
display: block;
}
#marc-file-uploader.mini #drop-zone label p {
display: inline-block;
margin: 0 .8rem;
}
</style>

View File

@ -0,0 +1,58 @@
<script setup>
import { defineProps, defineEmits, computed } from 'vue'
import Fields from './Fields.vue'
import Leader from './Leader.vue'
import Directory from './Directory.vue'
const props = defineProps({
modelValue: Array
})
const emit = defineEmits(['update:modelValue'])
</script>
<template>
<section id="marc-records">
<ul>
<li v-for="record in modelValue" class="marc-record-group">
<div class="marc-record">
<div class="record-metadata">
<Leader :leader="record.leader"/>
<Directory :directory="record.directory"/>
</div>
<Fields v-model="record.fields"/>
</div>
</li>
</ul>
</section>
</template>
<style>
#marc-records {
width: 100%;
padding: 1rem;
}
#marc-records .marc-record-group {
display: flex;
}
#marc-records .marc-record-group .marc-record-controls {
}
#marc-records .marc-record {
width: 100%;
}
#marc-records .marc-record .record-metadata {
display: flex;
flex-wrap: wrap;
background-color: #d96B0b;
padding: 0 .5rem;
color: #fff;
width: 100%;
}
</style>

View File

@ -0,0 +1,66 @@
<script setup>
import { defineProps, defineEmits, computed } from 'vue'
const props = defineProps({
modelValue: Array,
})
function onContentEdit(event, index) {
props.modelValue[index].content = event.target.innerText;
}
</script>
<template>
<div class="subfields">
<ul>
<li v-for="(subfield, index) in modelValue" :key="index">
<div class="subfield">
<div class="code">
<span
contenteditable
v-html="subfield.code"
@blur="onCodeEdit($event, index)"
@keydown.enter.prevent
@keyup.enter="$event.target.blur()" />
</div>
<div class="content">
<span contenteditable
v-html="subfield.content"
@blur="onContentEdit($event, index)"
@keydown.enter.prevent
@keyup.enter="$event.target.blur()"></span>
</div>
</div>
</li>
</ul>
</div>
</template>
<style scoped>
.subfield {
display: flex;
wdith: 100%;
}
.subfield .code {
padding: .25rem .5rem;
background-color: #037f8c;
color: #fff;
font-family: monospace;
font-weight: bold;
font-size: 1.3rem;
}
.subfield .content {
padding: .25rem .25rem;
background-color: #fafafa;
width: 100%;
font-family: monospace;
font-size: 1.3rem;
}
.subfields ul li {
display: inline-block;
}
</style>

View File

4
src/main.js Normal file
View File

@ -0,0 +1,4 @@
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')

7
vite.config.js Normal file
View File

@ -0,0 +1,7 @@
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()]
})