Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhance catalog entry and code mode RHS panel to be able to create new catalog entry #1993

Merged
merged 44 commits into from
Jan 24, 2025
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
b8cdbe7
update catalog entry card
tintinthong Jan 14, 2025
de4baaa
add boxel spec component
tintinthong Jan 14, 2025
f488ac9
fix re-exports not being detected by the schema editor
tintinthong Jan 14, 2025
1a24148
integrate boxxel spec in code submode
tintinthong Jan 14, 2025
47b8e08
get select disabled to not display down arrow
tintinthong Jan 14, 2025
30732e5
sort catalog entry by latest created
tintinthong Jan 14, 2025
4d5451b
enable boxel spec instance to be created via create-file-modal
tintinthong Jan 14, 2025
651302e
fix test
tintinthong Jan 14, 2025
63222a9
Update everything to new schema
tintinthong Jan 14, 2025
997799a
fix lint
tintinthong Jan 14, 2025
6b26bef
fix seed instances
tintinthong Jan 14, 2025
ac05498
fix instances in base
tintinthong Jan 14, 2025
dc40e25
filer by type attribute
tintinthong Jan 14, 2025
a6dcabf
fix all test that query by isField
tintinthong Jan 14, 2025
d9978cd
Revert "fix re-exports not being detected by the schema editor"
tintinthong Jan 14, 2025
80a7386
fix test that use title. Change to name
tintinthong Jan 14, 2025
e8ac5db
fix test
tintinthong Jan 14, 2025
35d479f
fix more test
tintinthong Jan 14, 2025
b21fbb4
fix test to use isField to signal field or card
tintinthong Jan 14, 2025
0baf052
fix lint
tintinthong Jan 14, 2025
1ec41ea
readd show boxel spec preview
tintinthong Jan 14, 2025
5102958
add test
tintinthong Jan 14, 2025
adab3db
add snapshot
tintinthong Jan 14, 2025
99c4674
use README title in all caps
tintinthong Jan 14, 2025
d3244be
string boxel spec thru creation file args to impute specType
tintinthong Jan 15, 2025
d4a6244
fix default
tintinthong Jan 15, 2025
6a2b2aa
add test boxel spec
tintinthong Jan 15, 2025
cae1a39
fix lint
tintinthong Jan 15, 2025
453be4e
fix lint for test
tintinthong Jan 17, 2025
95235df
rename type -> specType
tintinthong Jan 17, 2025
18643fc
fix type
tintinthong Jan 17, 2025
58801d8
fix test data-test-field-type
tintinthong Jan 17, 2025
9cc39f3
fix test again
tintinthong Jan 17, 2025
63ed23a
fix specType test
tintinthong Jan 17, 2025
36de466
add linkedExamples + containedExamples field
tintinthong Jan 18, 2025
c6c26ff
fix lint
tintinthong Jan 18, 2025
f5e5845
render cardTypeIcon. Need to load card from code ref
tintinthong Jan 18, 2025
1bc9750
render containedExamples only when the specType is a field
tintinthong Jan 20, 2025
a6402ac
Merge branch 'main' into introduce-boxel-spec
tintinthong Jan 22, 2025
c231d13
fix lint
tintinthong Jan 22, 2025
08cce23
add Loader
tintinthong Jan 23, 2025
a4801a0
fix test
tintinthong Jan 23, 2025
32be607
Merge branch 'main' into introduce-boxel-spec
tintinthong Jan 24, 2025
4a32548
fix test
tintinthong Jan 24, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion packages/base/cards-grid.gts
Original file line number Diff line number Diff line change
Expand Up @@ -384,13 +384,19 @@ class Isolated extends Component<typeof CardsGrid> {
on: catalogEntryRef,
eq: { ref: activeFilterRef },
},
sort: [
{
by: 'createdAt',
direction: 'desc',
},
],
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Focus on the last newly created catalog entry, if there are more than one

};
}
let card = await chooseCard<CatalogEntry>(
{
filter: {
on: catalogEntryRef,
every: [{ eq: { isField: false } }],
every: [{ eq: { type: 'card' } }],
tintinthong marked this conversation as resolved.
Show resolved Hide resolved
},
},
{ preselectedCardTypeQuery },
Expand Down
324 changes: 262 additions & 62 deletions packages/base/catalog-entry.gts
Original file line number Diff line number Diff line change
Expand Up @@ -3,73 +3,61 @@ import {
field,
Component,
CardDef,
FieldDef,
relativeTo,
realmInfo,
linksToMany,
} from './card-api';
import StringField from './string';
import BooleanField from './boolean';
import CodeRef from './code-ref';
import MarkdownField from './markdown';

import { FieldContainer } from '@cardstack/boxel-ui/components';
import GlimmerComponent from '@glimmer/component';
import BoxModel from '@cardstack/boxel-icons/box-model';
import BookOpenText from '@cardstack/boxel-icons/book-open-text';
import LayersSubtract from '@cardstack/boxel-icons/layers-subtract';
import GitBranch from '@cardstack/boxel-icons/git-branch';
import { DiagonalArrowLeftUp } from '@cardstack/boxel-ui/icons';
import { Pill } from '@cardstack/boxel-ui/components';
import StackIcon from '@cardstack/boxel-icons/stack';
import AppsIcon from '@cardstack/boxel-icons/apps';
import LayoutList from '@cardstack/boxel-icons/layout-list';
import Brain from '@cardstack/boxel-icons/brain';

export class SpecType extends StringField {
static displayName = 'Spec Type';
}

export class CatalogEntry extends CardDef {
static displayName = 'Catalog Entry';
static icon = BoxModel;
@field title = contains(StringField);
@field description = contains(StringField);
@field name = contains(StringField);
@field readMe = contains(MarkdownField);

@field ref = contains(CodeRef);
tintinthong marked this conversation as resolved.
Show resolved Hide resolved
@field type = contains(SpecType);

// If it's not a field, then it's a card
@field isField = contains(BooleanField);
@field isField = contains(BooleanField, {
computeVia: function (this: CatalogEntry) {
return this.type === 'field';
},
});

@field isCard = contains(BooleanField, {
computeVia: function (this: CatalogEntry) {
return this.type === 'card';
},
});
@field moduleHref = contains(StringField, {
computeVia: function (this: CatalogEntry) {
return new URL(this.ref.module, this[relativeTo]).href;
},
});
@field demo = contains(FieldDef);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed this for now and will treat adding examples of different boxel spec in separate PR

Copy link
Contributor

@habdelra habdelra Dec 31, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this actually needed a lot more discussion in terms of how we render this as it should be a polymorphic card and not a field. but yeah, good to remove for now...

@field realmName = contains(StringField, {
@field examples = linksToMany(CardDef);
Copy link
Contributor Author

@tintinthong tintinthong Dec 31, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

stub this for now. We need to consider ways to model examples for both fieldDef and cardDef. I think @demo used to be the way to do this but let's think about this in another PR. We may very well just re-include demo but we need to investigate its usage more carefully. But, given fields are mostly displayed in cards anyway, maybe we only want to link to cards for now

@field title = contains(StringField, {
computeVia: function (this: CatalogEntry) {
return this[realmInfo]?.name;
return this.name || this.ref.name;
},
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This won't work well for default exports. The ref name will be 'default' for each one

@field thumbnailURL = contains(StringField, { computeVia: () => null }); // remove this if we want card type entries to have images

get showDemo() {
return !this.isField;
}

// An explicit edit template is provided since computed isPrimitive bool
// field (which renders in the embedded format) looks a little wonky
// right now in the edit view.
static edit = class Edit extends Component<typeof this> {
<template>
<CatalogEntryContainer>
<FieldContainer @tag='label' @label='Title' data-test-field='title'>
<@fields.title />
</FieldContainer>
<FieldContainer
@tag='label'
@label='Description'
data-test-field='description'
>
<@fields.description />
</FieldContainer>
<FieldContainer @label='Ref' data-test-field='ref'>
<@fields.ref />
</FieldContainer>
<FieldContainer @label='Workspace Name' data-test-field='realmName'>
<@fields.realmName />
</FieldContainer>
<FieldContainer @vertical={{true}} @label='Demo' data-test-field='demo'>
<@fields.demo />
</FieldContainer>
</CatalogEntryContainer>
</template>
};

static fitted = class Fitted extends Component<typeof this> {
<template>
Expand Down Expand Up @@ -102,30 +90,193 @@ export class CatalogEntry extends CardDef {
};

static isolated = class Isolated extends Component<typeof this> {
get icon() {
return this.args.model.constructor?.icon;
}
<template>
<CatalogEntryContainer class='container'>
tintinthong marked this conversation as resolved.
Show resolved Hide resolved
<h1 data-test-title><@fields.title /></h1>
<em data-test-description><@fields.description /></em>
<div data-test-ref>
Module:
<@fields.moduleHref />
Name:
{{@model.ref.name}}
<div class='container'>
<div class='header'>
<div class='header-icon-container'>
<this.icon class='box header-icon-svg' />
</div>
<div class='header-info-container'>
<div class='box'>
<h1 data-test-title><@fields.title /></h1>
<em data-test-description><@fields.description /></em>
</div>
</div>
</div>
<div class='realm-name' data-test-realm-name>
in
<@fields.realmName />
<div class='readme section'>
<div class='row-header'>
<BookOpenText />
Read Me
</div>
{{#if @model.readMe}}
<div class='box'>
<@fields.readMe />
</div>
{{/if}}
</div>
{{#if @model.showDemo}}
<div data-test-demo><@fields.demo /></div>
{{/if}}
</CatalogEntryContainer>
<div class='examples section'>
<div class='row-header'>
<LayersSubtract />
Examples
</div>
<@fields.examples />
</div>
<div class='module section'>
<div class='row-header'>
<GitBranch />
Module</div>
<div class='container-code-ref'>
<div class='row-code-ref'>
<div class='row-code-ref-label'>URL</div>
<div class='row-code-ref-value box' data-test-module-href>
{{@model.moduleHref}}
</div>
</div>
<div class='row-code-ref'>
<div class='row-code-ref-label'>Exported Name</div>
<div class='row-code-ref-value box'>
<div class='exported-row'>
<div class='exported-name' data-test-exported-name>
<DiagonalArrowLeftUp class='exported-arrow' />
{{@model.ref.name}}
</div>
<div class='exported-type' data-test-exported-type>
{{@model.type}}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<style scoped>
.container {
padding: var(--boxel-sp);
background-color: var(--boxel-200);
}
.box {
border: 2px solid var(--boxel-border-color);
border-radius: var(--boxel-border-radius-lg);
padding: var(--boxel-sp-xs);
background-color: var(--boxel-light);
}
.header {
display: flex;
gap: var(--boxel-sp-sm);
}
.section {
padding: var(--boxel-sp-sm);
}
.header-icon-container {
padding: var(--boxel-sp-sm);
flex-shrink: 0;
}
.header-icon-svg {
height: var(--boxel-icon-xxl);
width: var(--boxel-icon-xxl);
border: 2px solid var(--boxel-border-color);
border-radius: var(--boxel-border-radius-lg);
}
.header-info-container {
padding: var(--boxel-sp-sm);
flex: 1;
}
.row-header {
display: inline-flex;
align-items: center;
justify-content: center;
gap: var(--boxel-sp-xs);
font-weight: 600;
}
.container-code-ref {
display: flex;
flex-direction: column;
gap: var(--boxel-sp-xs);
}
.row-code-ref {
display: flex;
flex-direction: column;
gap: var(--boxel-sp-xs);
}
.row-code-ref-value {
background-color: var(--boxel-300);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.exported-row {
display: flex;
justify-content: space-between;
}
.exported-name {
display: inline-flex;
align-items: center;
gap: var(--boxel-sp-xxs);
}
.exported-type {
text-transform: uppercase;
font: 500 var(--boxel-font-xs);
color: var(--boxel-450);
letter-spacing: var(--boxel-lsp-xl);
}
.realm-name {
color: var(--boxel-teal);
.exported-arrow {
width: var(--boxel-icon-xxs);
height: var(--boxel-icon-xxs);
--icon-color: var(--boxel-teal);
}
</style>
</template>
};

static embedded = class Embedded extends Component<typeof this> {
get icon() {
return this.args.model.constructor?.icon;
}
<template>
<div class='header'>
<div class='header-icon-container'>
<this.icon class='box header-icon-svg' />
</div>
<div class='header-info-container'>
<header class='title'><@fields.title /></header>
<p class='description'><@fields.description /></p>
</div>
<div class='pill-container'>
{{#if @model.type}}
<SpecTag @type={{@model.type}} />
{{/if}}
</div>
</div>
<style scoped>
.header {
display: flex;
align-items: center;
gap: var(--boxel-sp-sm);
}
.header-icon-container {
flex-shrink: 0;
padding: var(--boxel-sp-sm);
}
.header-info-container {
flex: 1;
}
.pill-container {
padding-right: var(--boxel-sp-sm);
}
.header-icon-svg {
width: 50px;
height: 50px;
border: 2px solid var(--boxel-border-color);
border-radius: var(--boxel-border-radius-lg);
}
.title {
font: 600 var(--boxel-font-sm);
}
.description {
margin: 0;
color: var(--boxel-500);
font-size: var(--boxel-font-size-xs);
}
</style>
Expand Down Expand Up @@ -157,3 +308,52 @@ class CatalogEntryContainer extends GlimmerComponent<Signature> {
</style>
</template>
}

interface SpecTagSignature {
Element: HTMLDivElement;
Args: {
type: string;
};
}

export class SpecTag extends GlimmerComponent<SpecTagSignature> {
get icon() {
return getIcon(this.args.type);
}
<template>
{{#if this.icon}}
<Pill class='spec-tag-pill' ...attributes>
<:iconLeft>
<div class='spec-tagicon'>
{{this.icon}}
</div>
</:iconLeft>
<:default>
{{@type}}
</:default>
</Pill>

{{/if}}
<style scoped>
.spec-tag-pill {
--pill-font: 500 var(--boxel-font-xs);
--pill-background-color: var(--boxel-200);
}
</style>
</template>
}

function getIcon(specType: string) {
switch (specType) {
case 'card':
return StackIcon;
case 'app':
return AppsIcon;
case 'field':
return LayoutList;
case 'skill':
return Brain;
default:
return;
}
Comment on lines +388 to +400
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are the different types of boxel specs we are supporting for now

}
Loading
Loading