Add wardrobe-2020 outfit preview to item pages
Eyyy tasty! There were some issues with conflicting styles with the main app, but I think we got it! Scoping Chakra's CSS reset was a big deal to not accidentally overwrite the app's own styles lol, and we had to solve a specificity problem for that, thanks Aria for the :where tip!! <3
This commit is contained in:
parent
2eb8a7cd60
commit
9cc45f0988
7 changed files with 311 additions and 48 deletions
|
@ -91,6 +91,8 @@ input[type=text], input[type=password], input[type=search], input[type=number],
|
|||
textarea
|
||||
font: inherit
|
||||
|
||||
// TODO: This conflicts with button styles in embedded wardrobe-2020
|
||||
// components. It'd be nice to not apply it to ALL button elements.
|
||||
a.button, input[type=submit], button
|
||||
+awesome-button
|
||||
&.loud
|
||||
|
|
|
@ -53,7 +53,7 @@
|
|||
&:hover
|
||||
color: #fff
|
||||
&:active
|
||||
top: 1px
|
||||
transform: translateY(1px)
|
||||
|
||||
=reset-awesome-button
|
||||
+border-radius(0)
|
||||
|
|
13
app/javascript/item-page.js
Normal file
13
app/javascript/item-page.js
Normal file
|
@ -0,0 +1,13 @@
|
|||
import React from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
|
||||
import { AppProvider, ItemPageOutfitPreview } from "./wardrobe-2020";
|
||||
|
||||
const rootNode = document.querySelector("#outfit-preview-root");
|
||||
const itemId = rootNode.getAttribute("data-item-id");
|
||||
ReactDOM.render(
|
||||
<AppProvider>
|
||||
<ItemPageOutfitPreview itemId={itemId} />
|
||||
</AppProvider>,
|
||||
rootNode
|
||||
);
|
|
@ -2,32 +2,14 @@ import React from "react";
|
|||
import * as Sentry from "@sentry/react";
|
||||
import { Integrations } from "@sentry/tracing";
|
||||
import { Auth0Provider } from "@auth0/auth0-react";
|
||||
import { CSSReset, ChakraProvider, extendTheme } from "@chakra-ui/react";
|
||||
import { ChakraProvider, Box } from "@chakra-ui/react";
|
||||
import { ApolloProvider } from "@apollo/client";
|
||||
import { useAuth0 } from "@auth0/auth0-react";
|
||||
import { mode } from "@chakra-ui/theme-tools";
|
||||
import { BrowserRouter } from "react-router-dom";
|
||||
import { Global } from "@emotion/react";
|
||||
|
||||
import buildApolloClient from "./apolloClient";
|
||||
|
||||
const theme = extendTheme({
|
||||
styles: {
|
||||
global: (props) => ({
|
||||
html: {
|
||||
// HACK: Chakra sets body as the relative position element, which is
|
||||
// fine, except its `min-height: 100%` doesn't actually work
|
||||
// unless paired with height on the root element too!
|
||||
height: "100%",
|
||||
},
|
||||
body: {
|
||||
background: mode("gray.50", "gray.800")(props),
|
||||
color: mode("green.800", "green.50")(props),
|
||||
transition: "all 0.25s",
|
||||
},
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
export default function AppProvider({ children }) {
|
||||
React.useEffect(() => setupLogging(), []);
|
||||
|
||||
|
@ -45,9 +27,8 @@ export default function AppProvider({ children }) {
|
|||
scope=""
|
||||
>
|
||||
<DTIApolloProvider>
|
||||
<ChakraProvider theme={theme}>
|
||||
<CSSReset />
|
||||
{children}
|
||||
<ChakraProvider resetCSS={false}>
|
||||
<ScopedCSSReset>{children}</ScopedCSSReset>
|
||||
</ChakraProvider>
|
||||
</DTIApolloProvider>
|
||||
</Auth0Provider>
|
||||
|
@ -155,3 +136,288 @@ function setupLogging() {
|
|||
tracesSampleRate: 1.0,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* ScopedCSSReset applies a copy of Chakra UI's CSS reset, but only to its
|
||||
* children (or, well, any element with the chakra-css-reset class).
|
||||
*
|
||||
* TODO: What about Chakra's portal elements like toast messages, which are
|
||||
* intentionally mounted elsewhere in the document?
|
||||
*
|
||||
* NOTE: We use the `:where` CSS selector, instead of the .chakra-css-reset
|
||||
* selector directly, to avoid specificity conflicts. e.g. the selector
|
||||
* `.chakra-css-reset h1` is considered MORE specific than `.my-h1`, whereas
|
||||
* the selector `:where(.chakra-css-reset) h1` is lower specificity.
|
||||
*/
|
||||
function ScopedCSSReset({ children }) {
|
||||
return (
|
||||
<>
|
||||
<Box className="chakra-css-reset">{children}</Box>
|
||||
<Global
|
||||
styles={`
|
||||
:where(.chakra-css-reset) {
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
border-width: 0;
|
||||
border-style: solid;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
main {
|
||||
display: block;
|
||||
}
|
||||
|
||||
hr {
|
||||
border-top-width: 1px;
|
||||
box-sizing: content-box;
|
||||
height: 0;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
pre,
|
||||
code,
|
||||
kbd,
|
||||
samp {
|
||||
font-family: SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
a {
|
||||
background-color: transparent;
|
||||
color: inherit;
|
||||
text-decoration: inherit;
|
||||
}
|
||||
|
||||
abbr[title] {
|
||||
border-bottom: none;
|
||||
text-decoration: underline;
|
||||
-webkit-text-decoration: underline dotted;
|
||||
text-decoration: underline dotted;
|
||||
}
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
small {
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
sub,
|
||||
sup {
|
||||
font-size: 75%;
|
||||
line-height: 0;
|
||||
position: relative;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
sub {
|
||||
bottom: -0.25em;
|
||||
}
|
||||
|
||||
sup {
|
||||
top: -0.5em;
|
||||
}
|
||||
|
||||
img {
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
button,
|
||||
input,
|
||||
optgroup,
|
||||
select,
|
||||
textarea {
|
||||
font-family: inherit;
|
||||
font-size: 100%;
|
||||
line-height: 1.15;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
button,
|
||||
input {
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
button,
|
||||
select {
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
button::-moz-focus-inner,
|
||||
[type="button"]::-moz-focus-inner,
|
||||
[type="reset"]::-moz-focus-inner,
|
||||
[type="submit"]::-moz-focus-inner {
|
||||
border-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
fieldset {
|
||||
padding: 0.35em 0.75em 0.625em;
|
||||
}
|
||||
|
||||
legend {
|
||||
box-sizing: border-box;
|
||||
color: inherit;
|
||||
display: table;
|
||||
max-width: 100%;
|
||||
padding: 0;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
progress {
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
textarea {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
[type="checkbox"],
|
||||
[type="radio"] {
|
||||
box-sizing: border-box;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
[type="number"]::-webkit-inner-spin-button,
|
||||
[type="number"]::-webkit-outer-spin-button {
|
||||
-webkit-appearance: none !important;
|
||||
}
|
||||
|
||||
input[type="number"] {
|
||||
-moz-appearance: textfield;
|
||||
}
|
||||
|
||||
[type="search"] {
|
||||
-webkit-appearance: textfield;
|
||||
outline-offset: -2px;
|
||||
}
|
||||
|
||||
[type="search"]::-webkit-search-decoration {
|
||||
-webkit-appearance: none !important;
|
||||
}
|
||||
|
||||
::-webkit-file-upload-button {
|
||||
-webkit-appearance: button;
|
||||
font: inherit;
|
||||
}
|
||||
|
||||
details {
|
||||
display: block;
|
||||
}
|
||||
|
||||
summary {
|
||||
display: list-item;
|
||||
}
|
||||
|
||||
template {
|
||||
display: none;
|
||||
}
|
||||
|
||||
[hidden] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
body,
|
||||
blockquote,
|
||||
dl,
|
||||
dd,
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6,
|
||||
hr,
|
||||
figure,
|
||||
p,
|
||||
pre {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
button {
|
||||
background: transparent;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
fieldset {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
ol,
|
||||
ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
textarea {
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
button,
|
||||
[role="button"] {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
button::-moz-focus-inner {
|
||||
border: 0 !important;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
font-size: inherit;
|
||||
font-weight: inherit;
|
||||
}
|
||||
|
||||
button,
|
||||
input,
|
||||
optgroup,
|
||||
select,
|
||||
textarea {
|
||||
padding: 0;
|
||||
line-height: inherit;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
img,
|
||||
svg,
|
||||
video,
|
||||
canvas,
|
||||
audio,
|
||||
iframe,
|
||||
embed,
|
||||
object {
|
||||
display: block;
|
||||
}
|
||||
|
||||
img,
|
||||
video {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
[data-js-focus-visible] :focus:not([data-focus-visible-added]) {
|
||||
outline: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
select::-ms-expand {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
`}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -759,7 +759,7 @@ function IconCheckbox({ icon, isChecked, ...props }) {
|
|||
);
|
||||
}
|
||||
|
||||
function ItemPageOutfitPreview({ itemId }) {
|
||||
export function ItemPageOutfitPreview({ itemId }) {
|
||||
const idealPose = React.useMemo(
|
||||
() => (Math.random() > 0.5 ? "HAPPY_FEM" : "HAPPY_MASC"),
|
||||
[]
|
||||
|
@ -1018,6 +1018,7 @@ function ItemPageOutfitPreview({ itemId }) {
|
|||
rowGap="4"
|
||||
columnGap="6"
|
||||
justifyContent="center"
|
||||
width="100%"
|
||||
>
|
||||
<AspectRatio
|
||||
gridArea="preview"
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import AppProvider from "./AppProvider";
|
||||
import { ItemPageOutfitPreview } from "./ItemPage";
|
||||
import WardrobePage from "./WardrobePage";
|
||||
|
||||
export { AppProvider, WardrobePage };
|
||||
export { AppProvider, ItemPageOutfitPreview, WardrobePage };
|
||||
|
|
|
@ -69,16 +69,7 @@
|
|||
%span.more= t '.trading_closet_hangers.show_more'
|
||||
%span.less= t '.trading_closet_hangers.show_less'
|
||||
|
||||
#item-preview-header
|
||||
%h3= t '.preview.header'
|
||||
= link_to t('.preview.customize_more'), root_path, :id => 'customize-more',
|
||||
:class => 'button'
|
||||
|
||||
#item-preview
|
||||
%ul#item-preview-species{'data-supported-species-ids' => @supported_species_ids.join(',')}
|
||||
= standard_species_images_for(@basic_colored_pet_types_by_species_id)
|
||||
#item-preview-error
|
||||
#item-preview-swf= t '.preview.requirements_not_met'
|
||||
#outfit-preview-root{'data-item-id': @item.id}
|
||||
|
||||
- unless @contributors_with_counts.empty?
|
||||
#item-contributors
|
||||
|
@ -88,17 +79,6 @@
|
|||
%li= link_to(contributor.name, user_contributions_path(contributor)) + format_contribution_count(count)
|
||||
%footer= t '.contributors.footer'
|
||||
|
||||
:javascript
|
||||
var CURRENT_ITEM_ZONES_RESTRICT = #{@item.zones_restrict.inspect},
|
||||
IMPRESS_HOST = #{RemoteImpressHost.inspect};
|
||||
|
||||
%script#swf-assets-not-found-template{:type => 'text/x-jquery-tmpl'}
|
||||
= twl '.preview.not_found', :item_name => h(@item.name),
|
||||
:species_name => '${species_name}',
|
||||
:color_name => '${color_name}',
|
||||
:modeling_link_url => root_path
|
||||
|
||||
- content_for :javascripts do
|
||||
= include_javascript_libraries :jquery, :swfobject, :jquery_tmpl
|
||||
= javascript_include_tag 'items/show'
|
||||
= javascript_include_tag 'item-page', defer: true
|
||||
|
||||
|
|
Loading…
Reference in a new issue