insert rows from viewer
|
|
@ -5,7 +5,7 @@
|
|||
"imports": {
|
||||
"@std/assert": "jsr:@std/assert@1",
|
||||
"@std/path": "jsr:@std/path@^1.1.1",
|
||||
"@std/uuid": "jsr:@std/uuid@^1.0.9",
|
||||
"@std/uuid": "jsr:@std/uuid@^1.0.9"
|
||||
},
|
||||
"unstable": ["fmt-component"]
|
||||
}
|
||||
18
svelte/deno.lock → deno.lock
generated
|
|
@ -10,7 +10,6 @@
|
|||
"jsr:@std/uuid@*": "1.0.9",
|
||||
"jsr:@std/uuid@^1.0.9": "1.0.9",
|
||||
"npm:@deno/vite-plugin@^1.0.5": "1.0.5_vite@7.1.2__picomatch@4.0.3",
|
||||
"npm:@jsr/std__uuid@^1.0.9": "1.0.9",
|
||||
"npm:@sveltejs/vite-plugin-svelte@^6.1.1": "6.1.1_svelte@5.38.1__acorn@8.15.0_vite@7.1.2__picomatch@4.0.3",
|
||||
"npm:@tsconfig/svelte@^5.0.4": "5.0.4",
|
||||
"npm:svelte-check@^4.3.1": "4.3.1_svelte@5.38.1__acorn@8.15.0_typescript@5.8.3",
|
||||
|
|
@ -370,22 +369,6 @@
|
|||
"@jridgewell/sourcemap-codec"
|
||||
]
|
||||
},
|
||||
"@jsr/std__bytes@1.0.6": {
|
||||
"integrity": "sha512-St6yKggjFGhxS52IFLJWvkchRFbAKg2Xh8UxA4S1EGz7GJ2Ui+ssDDldj/w2c8vCxvl6qgR0HaYbKeFJNqujmA==",
|
||||
"tarball": "https://npm.jsr.io/~/11/@jsr/std__bytes/1.0.6.tgz"
|
||||
},
|
||||
"@jsr/std__crypto@1.0.5": {
|
||||
"integrity": "sha512-iqFCkjeGeQccLgmxH9m1d7abjZcFMW0XrYZu1itNz8vVHzH9crObalonjVQaVDdKHCrNNOklMN1t0u3k46dirA==",
|
||||
"tarball": "https://npm.jsr.io/~/11/@jsr/std__crypto/1.0.5.tgz"
|
||||
},
|
||||
"@jsr/std__uuid@1.0.9": {
|
||||
"integrity": "sha512-hyTTOsmUTnLuah/OMnkYfZ8fSqYbB113idHZfIYlhN26PdpMGTqTUZkFCA1Q6oBhW8d8N82RG0PnRohQ0peqhA==",
|
||||
"dependencies": [
|
||||
"@jsr/std__bytes",
|
||||
"@jsr/std__crypto"
|
||||
],
|
||||
"tarball": "https://npm.jsr.io/~/11/@jsr/std__uuid/1.0.9.tgz"
|
||||
},
|
||||
"@rollup/rollup-android-arm-eabi@4.46.2": {
|
||||
"integrity": "sha512-Zj3Hl6sN34xJtMv7Anwb5Gu01yujyE/cLBDB2gnHTAHaWS1Z38L7kuSG+oAh0giZMqG060f/YBStXtMH6FvPMA==",
|
||||
"os": ["android"],
|
||||
|
|
@ -996,7 +979,6 @@
|
|||
"packageJson": {
|
||||
"dependencies": [
|
||||
"npm:@deno/vite-plugin@^1.0.5",
|
||||
"npm:@jsr/std__uuid@^1.0.9",
|
||||
"npm:@sveltejs/vite-plugin-svelte@^6.1.1",
|
||||
"npm:@tsconfig/svelte@^5.0.4",
|
||||
"npm:svelte-check@^4.3.1",
|
||||
|
|
@ -143,16 +143,6 @@ impl FieldType {
|
|||
Self::Unknown => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn default_for_insert(&self) -> Result<Encodable, FieldTypeUnknownError> {
|
||||
match self {
|
||||
Self::InterimUser {} => Ok(Encodable::Text(None)),
|
||||
Self::Text {} => Ok(Encodable::Text(None)),
|
||||
Self::Timestamp { .. } => Ok(Encodable::Timestamp(None)),
|
||||
Self::Uuid { .. } => Ok(Encodable::Uuid(None)),
|
||||
Self::Unknown => Err(FieldTypeUnknownError {}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Error)]
|
||||
|
|
@ -235,7 +225,7 @@ pub enum Encodable {
|
|||
|
||||
impl Encodable {
|
||||
pub fn bind_onto<'a>(
|
||||
&'a self,
|
||||
self,
|
||||
query: sqlx::query::Query<'a, Postgres, <sqlx::Postgres as sqlx::Database>::Arguments<'a>>,
|
||||
) -> sqlx::query::Query<'a, Postgres, <sqlx::Postgres as sqlx::Database>::Arguments<'a>> {
|
||||
match self {
|
||||
|
|
@ -255,4 +245,11 @@ impl Encodable {
|
|||
let deserialized: Tagged = serde_json::from_value(serialized).unwrap();
|
||||
deserialized.c
|
||||
}
|
||||
|
||||
pub fn is_none(&self) -> bool {
|
||||
match self {
|
||||
Self::Text(None) | Self::Timestamp(None) | Self::Uuid(None) => true,
|
||||
Self::Text(_) | Self::Timestamp(_) | Self::Uuid(_) => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
9
interim-server/src/field.rs
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
use interim_models::field::Field;
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
pub struct FieldInfo {
|
||||
pub field: Field,
|
||||
pub has_default: bool,
|
||||
pub not_null: bool,
|
||||
}
|
||||
|
|
@ -15,6 +15,7 @@ mod auth;
|
|||
mod base_pooler;
|
||||
mod base_user_perms;
|
||||
mod cli;
|
||||
mod field;
|
||||
mod middleware;
|
||||
mod navbar;
|
||||
mod navigator;
|
||||
|
|
|
|||
|
|
@ -81,6 +81,10 @@ pub fn new_router(state: AppState) -> Router<()> {
|
|||
"/d/{base_id}/r/{class_oid}/l/{lens_id}/update-value",
|
||||
post(routes::lenses::update_value_page_post),
|
||||
)
|
||||
.route(
|
||||
"/d/{base_id}/r/{class_oid}/l/{lens_id}/insert",
|
||||
post(routes::lens_insert::insert_page_post),
|
||||
)
|
||||
.route_with_tsr(
|
||||
"/d/{base_id}/r/{class_oid}/l/{lens_id}/viewer/",
|
||||
get(routes::lenses::viewer_page),
|
||||
|
|
|
|||
95
interim-server/src/routes/lens_insert.rs
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use axum::{
|
||||
extract::{Path, State},
|
||||
response::Response,
|
||||
};
|
||||
use axum_extra::extract::Form;
|
||||
use interim_models::{field::Encodable, lens::Lens};
|
||||
use interim_pgtypes::{escape_identifier, pg_class::PgClass};
|
||||
use sqlx::{postgres::types::Oid, query};
|
||||
|
||||
use crate::{
|
||||
app_error::AppError,
|
||||
app_state::AppDbConn,
|
||||
base_pooler::{BasePooler, RoleAssignment},
|
||||
navigator::Navigator,
|
||||
user::CurrentUser,
|
||||
};
|
||||
|
||||
use super::LensPagePath;
|
||||
|
||||
pub async fn insert_page_post(
|
||||
State(mut base_pooler): State<BasePooler>,
|
||||
navigator: Navigator,
|
||||
AppDbConn(mut app_db): AppDbConn,
|
||||
CurrentUser(current_user): CurrentUser,
|
||||
Path(LensPagePath {
|
||||
base_id,
|
||||
class_oid,
|
||||
lens_id,
|
||||
}): Path<LensPagePath>,
|
||||
Form(body): Form<HashMap<String, Vec<String>>>,
|
||||
) -> Result<Response, AppError> {
|
||||
// FIXME auth, csrf
|
||||
|
||||
let lens = Lens::with_id(lens_id).fetch_one(&mut app_db).await?;
|
||||
let mut base_client = base_pooler
|
||||
.acquire_for(base_id, RoleAssignment::User(current_user.id))
|
||||
.await?;
|
||||
|
||||
let rel = PgClass::with_oid(Oid(class_oid))
|
||||
.fetch_one(&mut base_client)
|
||||
.await?;
|
||||
|
||||
let col_names: Vec<String> = body.keys().cloned().collect();
|
||||
let col_list_sql = col_names
|
||||
.iter()
|
||||
.map(|value| escape_identifier(value))
|
||||
.collect::<Vec<String>>()
|
||||
.join(", ");
|
||||
|
||||
let n_rows = body.values().map(|value| value.len()).max().unwrap_or(0);
|
||||
if n_rows > 0 {
|
||||
let mut param_index = 1;
|
||||
let mut params: Vec<Encodable> = vec![];
|
||||
let mut row_list: Vec<String> = vec![];
|
||||
for i in 0..n_rows {
|
||||
let mut param_slots: Vec<String> = vec![];
|
||||
for col in col_names.iter() {
|
||||
let maybe_value: Option<Encodable> = body
|
||||
.get(col)
|
||||
.and_then(|col_values| col_values.get(i))
|
||||
.map(|value_raw| serde_json::from_str(value_raw))
|
||||
.transpose()?;
|
||||
if let Some(value) = maybe_value.filter(|value| !value.is_none()) {
|
||||
params.push(value);
|
||||
param_slots.push(format!("${param_index}"));
|
||||
param_index += 1;
|
||||
} else {
|
||||
param_slots.push("default".to_owned());
|
||||
}
|
||||
}
|
||||
row_list.push(format!("({})", param_slots.join(", ")));
|
||||
}
|
||||
let row_list_sql = row_list.join(",\n");
|
||||
|
||||
let query_sql = &format!(
|
||||
r#"
|
||||
insert into {0}.{1}
|
||||
({col_list_sql})
|
||||
values
|
||||
{row_list_sql}
|
||||
"#,
|
||||
escape_identifier(&rel.regnamespace),
|
||||
escape_identifier(&rel.relname)
|
||||
);
|
||||
let mut q = query(query_sql);
|
||||
for param in params {
|
||||
q = param.bind_onto(q);
|
||||
}
|
||||
q.execute(base_client.get_conn()).await?;
|
||||
}
|
||||
|
||||
Ok(navigator.lens_page(&lens).redirect_to())
|
||||
}
|
||||
|
|
@ -25,12 +25,15 @@ use crate::{
|
|||
app_error::{AppError, bad_request},
|
||||
app_state::AppDbConn,
|
||||
base_pooler::{BasePooler, RoleAssignment},
|
||||
field::FieldInfo,
|
||||
navbar::{NavLocation, Navbar, RelLocation},
|
||||
navigator::Navigator,
|
||||
settings::Settings,
|
||||
user::CurrentUser,
|
||||
};
|
||||
|
||||
use super::LensPagePath;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct LensesPagePath {
|
||||
base_id: Uuid,
|
||||
|
|
@ -138,13 +141,6 @@ pub async fn add_lens_page_post(
|
|||
Ok(navigator.lens_page(&lens).redirect_to())
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct LensPagePath {
|
||||
base_id: Uuid,
|
||||
class_oid: u32,
|
||||
lens_id: Uuid,
|
||||
}
|
||||
|
||||
pub async fn lens_page(
|
||||
State(settings): State<Settings>,
|
||||
State(mut base_pooler): State<BasePooler>,
|
||||
|
|
@ -163,63 +159,15 @@ pub async fn lens_page(
|
|||
let mut base_client = base_pooler
|
||||
.acquire_for(lens.base_id, RoleAssignment::User(current_user.id))
|
||||
.await?;
|
||||
let rel = PgClass::with_oid(lens.class_oid)
|
||||
.fetch_one(&mut base_client)
|
||||
.await?;
|
||||
|
||||
let attrs = PgAttribute::all_for_rel(lens.class_oid)
|
||||
.fetch_all(&mut base_client)
|
||||
.await?;
|
||||
let fields = Field::belonging_to_lens(lens.id)
|
||||
.fetch_all(&mut app_db)
|
||||
.await?;
|
||||
let pkey_attrs = PgAttribute::pkeys_for_rel(lens.class_oid)
|
||||
.fetch_all(&mut base_client)
|
||||
.await?;
|
||||
|
||||
const FRONTEND_ROW_LIMIT: i64 = 1000;
|
||||
let rows: Vec<PgRow> = query(&format!(
|
||||
"select {0} from {1}.{2} limit $1",
|
||||
pkey_attrs
|
||||
.iter()
|
||||
.chain(attrs.iter())
|
||||
.map(|attr| escape_identifier(&attr.attname))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", "),
|
||||
escape_identifier(&rel.regnamespace),
|
||||
escape_identifier(&rel.relname),
|
||||
))
|
||||
.bind(FRONTEND_ROW_LIMIT)
|
||||
.fetch_all(base_client.get_conn())
|
||||
.await?;
|
||||
let pkeys: Vec<HashMap<String, Encodable>> = rows
|
||||
.iter()
|
||||
.map(|row| {
|
||||
let mut pkey_values: HashMap<String, Encodable> = HashMap::new();
|
||||
for attr in pkey_attrs.clone() {
|
||||
let field = Field::default_from_attr(&attr);
|
||||
pkey_values.insert(field.name.clone(), field.get_value_encodable(row).unwrap());
|
||||
}
|
||||
pkey_values
|
||||
})
|
||||
.collect();
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(path = "lens0_2.html")]
|
||||
struct ResponseTemplate {
|
||||
fields: Vec<Field>,
|
||||
all_columns: Vec<PgAttribute>,
|
||||
rows: Vec<PgRow>,
|
||||
pkeys: Vec<HashMap<String, Encodable>>,
|
||||
settings: Settings,
|
||||
navbar: Navbar,
|
||||
}
|
||||
Ok(Html(
|
||||
ResponseTemplate {
|
||||
all_columns: attrs,
|
||||
fields,
|
||||
pkeys,
|
||||
rows,
|
||||
navbar: Navbar::builder()
|
||||
.root_path(settings.root_path.clone())
|
||||
.base(base.clone())
|
||||
|
|
@ -262,13 +210,27 @@ pub async fn get_data_page_get(
|
|||
let attrs = PgAttribute::all_for_rel(lens.class_oid)
|
||||
.fetch_all(&mut base_client)
|
||||
.await?;
|
||||
let fields = Field::belonging_to_lens(lens.id)
|
||||
.fetch_all(&mut app_db)
|
||||
.await?;
|
||||
let pkey_attrs = PgAttribute::pkeys_for_rel(lens.class_oid)
|
||||
.fetch_all(&mut base_client)
|
||||
.await?;
|
||||
|
||||
let fields: Vec<FieldInfo> = {
|
||||
let fields: Vec<Field> = Field::belonging_to_lens(lens.id)
|
||||
.fetch_all(&mut app_db)
|
||||
.await?;
|
||||
let mut field_info: Vec<FieldInfo> = Vec::with_capacity(fields.len());
|
||||
for field in fields {
|
||||
if let Some(attr) = attrs.iter().find(|attr| attr.attname == field.name) {
|
||||
field_info.push(FieldInfo {
|
||||
field,
|
||||
has_default: attr.atthasdef,
|
||||
not_null: attr.attnotnull.unwrap_or_default(),
|
||||
});
|
||||
}
|
||||
}
|
||||
field_info
|
||||
};
|
||||
|
||||
const FRONTEND_ROW_LIMIT: i64 = 1000;
|
||||
let rows: Vec<PgRow> = query(&format!(
|
||||
"select {0} from {1}.{2} limit $1",
|
||||
|
|
@ -291,7 +253,7 @@ pub async fn get_data_page_get(
|
|||
data: Vec<Encodable>,
|
||||
}
|
||||
|
||||
let mut data: Vec<DataRow> = vec![];
|
||||
let mut data_rows: Vec<DataRow> = vec![];
|
||||
let mut pkeys: Vec<String> = vec![];
|
||||
for row in rows.iter() {
|
||||
let mut pkey_values: HashMap<String, Encodable> = HashMap::new();
|
||||
|
|
@ -303,9 +265,9 @@ pub async fn get_data_page_get(
|
|||
pkeys.push(pkey.clone());
|
||||
let mut row_data: Vec<Encodable> = vec![];
|
||||
for field in fields.iter() {
|
||||
row_data.push(field.get_value_encodable(row)?);
|
||||
row_data.push(field.field.get_value_encodable(row)?);
|
||||
}
|
||||
data.push(DataRow {
|
||||
data_rows.push(DataRow {
|
||||
pkey,
|
||||
data: row_data,
|
||||
});
|
||||
|
|
@ -313,14 +275,14 @@ pub async fn get_data_page_get(
|
|||
|
||||
#[derive(Serialize)]
|
||||
struct ResponseBody {
|
||||
rows: Vec<DataRow>,
|
||||
fields: Vec<FieldInfo>,
|
||||
pkeys: Vec<String>,
|
||||
data: Vec<DataRow>,
|
||||
fields: Vec<Field>,
|
||||
}
|
||||
Ok(Json(ResponseBody {
|
||||
rows: data_rows,
|
||||
fields,
|
||||
pkeys,
|
||||
data,
|
||||
})
|
||||
.into_response())
|
||||
}
|
||||
|
|
@ -472,6 +434,7 @@ pub async fn update_value_page_post(
|
|||
body.pkeys
|
||||
.get(&pkey_attrs.first().unwrap().attname)
|
||||
.unwrap()
|
||||
.clone()
|
||||
.bind_onto(body.value.bind_onto(query(&format!(
|
||||
r#"update {0}.{1} set {2} = $1 where {3} = $2"#,
|
||||
escape_identifier(&rel.regnamespace),
|
||||
|
|
|
|||
|
|
@ -1,3 +1,14 @@
|
|||
use serde::Deserialize;
|
||||
use uuid::Uuid;
|
||||
|
||||
pub mod bases;
|
||||
pub mod lens_insert;
|
||||
pub mod lenses;
|
||||
pub mod relations;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct LensPagePath {
|
||||
base_id: Uuid,
|
||||
class_oid: u32,
|
||||
lens_id: Uuid,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@deno/vite-plugin": "^1.0.5",
|
||||
"@jsr/std__uuid": "^1.0.9",
|
||||
"@sveltejs/vite-plugin-svelte": "^6.1.1",
|
||||
"svelte-language-server": "^0.17.19",
|
||||
"uuid": "^11.1.0",
|
||||
|
|
@ -43,12 +43,6 @@ $table-border-color: #ccc;
|
|||
text-align: left;
|
||||
}
|
||||
|
||||
&__header-actions {
|
||||
border: none;
|
||||
background: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
&__main {
|
||||
grid-area: main;
|
||||
overflow-y: auto;
|
||||
|
|
@ -69,25 +63,32 @@ $table-border-color: #ccc;
|
|||
&--insertable {
|
||||
border-style: dashed;
|
||||
}
|
||||
}
|
||||
|
||||
&__inserter {
|
||||
align-items: stretch;
|
||||
display: flex;
|
||||
grid-area: inserter;
|
||||
justify-items: flex-start;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
.lens-cell {
|
||||
&__container {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
|
||||
&--selected {
|
||||
.lens-table__cell-content {
|
||||
outline: 3px solid #37f;
|
||||
outline-offset: -2px;
|
||||
}
|
||||
outline: 3px solid #37f;
|
||||
outline-offset: -2px;
|
||||
}
|
||||
}
|
||||
|
||||
&__cell-content {
|
||||
&__content {
|
||||
flex: 1;
|
||||
font-family: globals.$font-family-data;
|
||||
|
||||
&--null {
|
||||
color: color.scale(#000, $lightness: 50%, $space: hsl);
|
||||
font-style: oblique;
|
||||
text-align: center;
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
&--text {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
|
@ -102,22 +103,60 @@ $table-border-color: #ccc;
|
|||
white-space: nowrap;
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
&--null {
|
||||
color: color.scale(#000, $lightness: 50%, $space: hsl);
|
||||
font-family: globals.$font-family-data;
|
||||
font-style: oblique;
|
||||
text-align: center;
|
||||
padding: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
&__inserter {
|
||||
grid-area: inserter;
|
||||
margin-bottom: 2rem;
|
||||
&__notice {
|
||||
align-items: center;
|
||||
color: color.scale(#000, $lightness: 50%, $space: hsl);
|
||||
display: flex;
|
||||
padding: 0 0.25rem;
|
||||
|
||||
svg path {
|
||||
stroke: currentColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.lens-inserter {
|
||||
&__rows {
|
||||
.lens-table__cell {
|
||||
border: dashed 1px $table-border-color;
|
||||
border-left: none;
|
||||
border-top: none;
|
||||
|
||||
&:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
}
|
||||
|
||||
.lens-table__row:first-child .lens-table__cell {
|
||||
border-top: dashed 1px $table-border-color;
|
||||
}
|
||||
}
|
||||
|
||||
&__submit {
|
||||
@include globals.reset-button;
|
||||
align-items: center;
|
||||
border: dashed 1px globals.$button-primary-background;
|
||||
border-bottom-right-radius: globals.$border-radius-rounded-sm;
|
||||
border-top-right-radius: globals.$border-radius-rounded-sm;
|
||||
color: globals.$button-primary-background;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding: 0 1rem;
|
||||
|
||||
svg path {
|
||||
stroke: currentColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.lens-editor {
|
||||
|
|
@ -128,7 +167,7 @@ $table-border-color: #ccc;
|
|||
|
||||
&__input {
|
||||
@include globals.reset_input;
|
||||
padding: 0.5rem;
|
||||
padding: 0.75rem 0.5rem;
|
||||
font-family: globals.$font-family-data;
|
||||
flex: 1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
@jsr:registry=https://npm.jsr.io
|
||||
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 768 B After Width: | Height: | Size: 768 B |
|
Before Width: | Height: | Size: 894 B After Width: | Height: | Size: 894 B |
|
Before Width: | Height: | Size: 517 B After Width: | Height: | Size: 517 B |
|
Before Width: | Height: | Size: 660 B After Width: | Height: | Size: 660 B |
|
Before Width: | Height: | Size: 488 B After Width: | Height: | Size: 488 B |
|
Before Width: | Height: | Size: 877 B After Width: | Height: | Size: 877 B |
|
Before Width: | Height: | Size: 674 B After Width: | Height: | Size: 674 B |
|
Before Width: | Height: | Size: 490 B After Width: | Height: | Size: 490 B |
|
Before Width: | Height: | Size: 847 B After Width: | Height: | Size: 847 B |
|
Before Width: | Height: | Size: 550 B After Width: | Height: | Size: 550 B |
|
Before Width: | Height: | Size: 665 B After Width: | Height: | Size: 665 B |
|
Before Width: | Height: | Size: 988 B After Width: | Height: | Size: 988 B |
|
Before Width: | Height: | Size: 1,023 B After Width: | Height: | Size: 1,023 B |
|
Before Width: | Height: | Size: 566 B After Width: | Height: | Size: 566 B |
|
Before Width: | Height: | Size: 550 B After Width: | Height: | Size: 550 B |
|
Before Width: | Height: | Size: 566 B After Width: | Height: | Size: 566 B |
|
Before Width: | Height: | Size: 560 B After Width: | Height: | Size: 560 B |
|
Before Width: | Height: | Size: 556 B After Width: | Height: | Size: 556 B |
|
Before Width: | Height: | Size: 2 KiB After Width: | Height: | Size: 2 KiB |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 670 B After Width: | Height: | Size: 670 B |
|
Before Width: | Height: | Size: 988 B After Width: | Height: | Size: 988 B |
|
Before Width: | Height: | Size: 1,011 B After Width: | Height: | Size: 1,011 B |
|
Before Width: | Height: | Size: 560 B After Width: | Height: | Size: 560 B |
|
Before Width: | Height: | Size: 838 B After Width: | Height: | Size: 838 B |
|
Before Width: | Height: | Size: 926 B After Width: | Height: | Size: 926 B |
|
Before Width: | Height: | Size: 883 B After Width: | Height: | Size: 883 B |
|
Before Width: | Height: | Size: 597 B After Width: | Height: | Size: 597 B |
|
Before Width: | Height: | Size: 589 B After Width: | Height: | Size: 589 B |
|
Before Width: | Height: | Size: 658 B After Width: | Height: | Size: 658 B |
|
Before Width: | Height: | Size: 610 B After Width: | Height: | Size: 610 B |
|
Before Width: | Height: | Size: 600 B After Width: | Height: | Size: 600 B |
|
Before Width: | Height: | Size: 602 B After Width: | Height: | Size: 602 B |
|
Before Width: | Height: | Size: 600 B After Width: | Height: | Size: 600 B |
|
Before Width: | Height: | Size: 592 B After Width: | Height: | Size: 592 B |
|
Before Width: | Height: | Size: 689 B After Width: | Height: | Size: 689 B |
|
Before Width: | Height: | Size: 486 B After Width: | Height: | Size: 486 B |
|
Before Width: | Height: | Size: 882 B After Width: | Height: | Size: 882 B |
|
Before Width: | Height: | Size: 700 B After Width: | Height: | Size: 700 B |
|
Before Width: | Height: | Size: 488 B After Width: | Height: | Size: 488 B |
|
Before Width: | Height: | Size: 853 B After Width: | Height: | Size: 853 B |
|
Before Width: | Height: | Size: 566 B After Width: | Height: | Size: 566 B |
|
Before Width: | Height: | Size: 687 B After Width: | Height: | Size: 687 B |
|
Before Width: | Height: | Size: 698 B After Width: | Height: | Size: 698 B |
|
Before Width: | Height: | Size: 691 B After Width: | Height: | Size: 691 B |
|
Before Width: | Height: | Size: 697 B After Width: | Height: | Size: 697 B |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 975 B After Width: | Height: | Size: 975 B |
|
Before Width: | Height: | Size: 1 KiB After Width: | Height: | Size: 1 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 954 B After Width: | Height: | Size: 954 B |
|
Before Width: | Height: | Size: 644 B After Width: | Height: | Size: 644 B |
|
Before Width: | Height: | Size: 1,011 B After Width: | Height: | Size: 1,011 B |
|
Before Width: | Height: | Size: 454 B After Width: | Height: | Size: 454 B |
|
Before Width: | Height: | Size: 589 B After Width: | Height: | Size: 589 B |
|
Before Width: | Height: | Size: 594 B After Width: | Height: | Size: 594 B |
|
Before Width: | Height: | Size: 589 B After Width: | Height: | Size: 589 B |
|
Before Width: | Height: | Size: 594 B After Width: | Height: | Size: 594 B |
|
Before Width: | Height: | Size: 799 B After Width: | Height: | Size: 799 B |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 571 B After Width: | Height: | Size: 571 B |
|
Before Width: | Height: | Size: 740 B After Width: | Height: | Size: 740 B |
|
Before Width: | Height: | Size: 741 B After Width: | Height: | Size: 741 B |
|
Before Width: | Height: | Size: 951 B After Width: | Height: | Size: 951 B |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 991 B After Width: | Height: | Size: 991 B |
|
Before Width: | Height: | Size: 966 B After Width: | Height: | Size: 966 B |
|
Before Width: | Height: | Size: 622 B After Width: | Height: | Size: 622 B |
|
Before Width: | Height: | Size: 536 B After Width: | Height: | Size: 536 B |
|
Before Width: | Height: | Size: 975 B After Width: | Height: | Size: 975 B |
|
Before Width: | Height: | Size: 622 B After Width: | Height: | Size: 622 B |
|
Before Width: | Height: | Size: 659 B After Width: | Height: | Size: 659 B |
|
Before Width: | Height: | Size: 620 B After Width: | Height: | Size: 620 B |
|
Before Width: | Height: | Size: 611 B After Width: | Height: | Size: 611 B |
|
Before Width: | Height: | Size: 408 B After Width: | Height: | Size: 408 B |
|
Before Width: | Height: | Size: 743 B After Width: | Height: | Size: 743 B |
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |